Skip to content

Commit 8ff3b05

Browse files
[NFC] Factor out DiagnosticBridge between swift-syntax
Refactor out the code that handles printing diagnostics from swift-syntax.
1 parent 4e4d547 commit 8ff3b05

File tree

5 files changed

+277
-185
lines changed

5 files changed

+277
-185
lines changed

include/swift/AST/DiagnosticBridge.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//===--- DiagnosticBridge.h - Diagnostic Bridge to SwiftSyntax --*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file declares the DiagnosticBridge class, which bridges to swift-syntax
14+
// for diagnostics printing.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_BASIC_DIAGNOSTICBRIDGE_H
19+
#define SWIFT_BASIC_DIAGNOSTICBRIDGE_H
20+
21+
#include "swift/AST/DiagnosticConsumer.h"
22+
#include "swift/Basic/SourceManager.h"
23+
#include "llvm/ADT/DenseMap.h"
24+
#include "llvm/Support/raw_ostream.h"
25+
26+
namespace swift {
27+
/// Declare the bridge between swift-syntax and swift-frontend for diagnostics
28+
/// handling. The methods in this class should only be called when
29+
/// `SWIFT_BUILD_SWIFT_SYNTAX` is defined as they are otherwise undefined.
30+
class DiagnosticBridge {
31+
/// A queued up source file known to the queued diagnostics.
32+
using QueuedBuffer = void *;
33+
34+
/// The queued diagnostics structure.
35+
void *queuedDiagnostics = nullptr;
36+
llvm::DenseMap<unsigned, QueuedBuffer> queuedBuffers;
37+
38+
/// Source file syntax nodes cached by { source manager, buffer ID }.
39+
llvm::DenseMap<std::pair<SourceManager *, unsigned>, void *> sourceFileSyntax;
40+
41+
public:
42+
/// Enqueue diagnostics.
43+
void enqueueDiagnostic(SourceManager &SM, const DiagnosticInfo &Info,
44+
unsigned innermostBufferID);
45+
46+
/// Flush all enqueued diagnostics.
47+
void flush(llvm::raw_ostream &OS, bool includeTrailingBreak,
48+
bool forceColors);
49+
50+
/// Retrieve the stack of source buffers from the provided location out to
51+
/// a physical source file, with source buffer IDs for each step along the way
52+
/// due to (e.g.) macro expansions or generated code.
53+
///
54+
/// The resulting vector will always contain valid source locations. If the
55+
/// initial location is invalid, the result will be empty.
56+
static SmallVector<unsigned, 1> getSourceBufferStack(SourceManager &sourceMgr,
57+
SourceLoc loc);
58+
59+
DiagnosticBridge() = default;
60+
~DiagnosticBridge();
61+
62+
private:
63+
/// Retrieve the SourceFileSyntax for the given buffer.
64+
void *getSourceFileSyntax(SourceManager &SM, unsigned bufferID,
65+
StringRef displayName);
66+
67+
void queueBuffer(SourceManager &sourceMgr, unsigned bufferID);
68+
};
69+
} // namespace swift
70+
71+
#endif

include/swift/Frontend/PrintingDiagnosticConsumer.h

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
#ifndef SWIFT_PRINTINGDIAGNOSTICCONSUMER_H
1919
#define SWIFT_PRINTINGDIAGNOSTICCONSUMER_H
2020

21+
#include "swift/AST/DiagnosticBridge.h"
2122
#include "swift/AST/DiagnosticConsumer.h"
2223
#include "swift/Basic/DiagnosticOptions.h"
2324
#include "swift/Basic/LLVM.h"
2425

25-
#include "llvm/ADT/DenseMap.h"
26-
#include "llvm/Support/raw_ostream.h"
2726
#include "llvm/Support/Process.h"
27+
#include "llvm/Support/raw_ostream.h"
2828

2929
namespace swift {
3030

@@ -43,16 +43,7 @@ class PrintingDiagnosticConsumer : public DiagnosticConsumer {
4343
bool SuppressOutput = false;
4444

4545
/// swift-syntax rendering
46-
47-
/// A queued up source file known to the queued diagnostics.
48-
using QueuedBuffer = void *;
49-
50-
/// The queued diagnostics structure.
51-
void *queuedDiagnostics = nullptr;
52-
llvm::DenseMap<unsigned, QueuedBuffer> queuedBuffers;
53-
54-
/// Source file syntax nodes cached by { source manager, buffer ID }.
55-
llvm::DenseMap<std::pair<SourceManager *, unsigned>, void *> sourceFileSyntax;
46+
DiagnosticBridge DiagBridge;
5647

5748
public:
5849
PrintingDiagnosticConsumer(llvm::raw_ostream &stream = llvm::errs());

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ add_swift_host_library(swiftAST STATIC
3838
Decl.cpp
3939
DeclContext.cpp
4040
DeclNameLoc.cpp
41+
DiagnosticBridge.cpp
4142
DiagnosticConsumer.cpp
4243
DiagnosticEngine.cpp
4344
DiagnosticList.cpp

lib/AST/DiagnosticBridge.cpp

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//===--- DiagnosticBridge.cpp - Diagnostic Bridge to swift-syntax ---------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements the DiagnosticBridge class.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/AST/DiagnosticBridge.h"
18+
#include "swift/AST/ASTBridging.h"
19+
#include "swift/AST/DiagnosticConsumer.h"
20+
#include "swift/AST/DiagnosticEngine.h"
21+
#include "swift/Basic/SourceManager.h"
22+
#include "swift/Bridging/ASTGen.h"
23+
#include "llvm/Support/raw_ostream.h"
24+
25+
using namespace swift;
26+
27+
#if SWIFT_BUILD_SWIFT_SYNTAX
28+
/// Enqueue a diagnostic with ASTGen's diagnostic rendering.
29+
static void addQueueDiagnostic(void *queuedDiagnostics,
30+
const DiagnosticInfo &info, SourceManager &SM) {
31+
llvm::SmallString<256> text;
32+
{
33+
llvm::raw_svector_ostream out(text);
34+
DiagnosticEngine::formatDiagnosticText(out, info.FormatString,
35+
info.FormatArgs);
36+
}
37+
38+
BridgedDiagnosticSeverity severity;
39+
switch (info.Kind) {
40+
case DiagnosticKind::Error:
41+
severity = BridgedDiagnosticSeverity::BridgedError;
42+
break;
43+
44+
case DiagnosticKind::Warning:
45+
severity = BridgedDiagnosticSeverity::BridgedWarning;
46+
break;
47+
48+
case DiagnosticKind::Remark:
49+
severity = BridgedDiagnosticSeverity::BridgedRemark;
50+
break;
51+
52+
case DiagnosticKind::Note:
53+
severity = BridgedDiagnosticSeverity::BridgedNote;
54+
break;
55+
}
56+
57+
// Map the highlight ranges.
58+
SmallVector<const void *, 2> highlightRanges;
59+
for (const auto &range : info.Ranges) {
60+
if (range.isInvalid())
61+
continue;
62+
63+
highlightRanges.push_back(range.getStart().getOpaquePointerValue());
64+
highlightRanges.push_back(range.getEnd().getOpaquePointerValue());
65+
}
66+
67+
// FIXME: Translate Fix-Its.
68+
swift_ASTGen_addQueuedDiagnostic(queuedDiagnostics, text.data(), text.size(),
69+
severity, info.Loc.getOpaquePointerValue(),
70+
highlightRanges.data(),
71+
highlightRanges.size() / 2);
72+
}
73+
74+
void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM,
75+
const DiagnosticInfo &Info,
76+
unsigned innermostBufferID) {
77+
// If there are no enqueued diagnostics, or we have hit a non-note
78+
// diagnostic, flush any enqueued diagnostics and start fresh.
79+
if (!queuedDiagnostics)
80+
queuedDiagnostics = swift_ASTGen_createQueuedDiagnostics();
81+
82+
queueBuffer(SM, innermostBufferID);
83+
addQueueDiagnostic(queuedDiagnostics, Info, SM);
84+
}
85+
86+
void DiagnosticBridge::flush(llvm::raw_ostream &OS, bool includeTrailingBreak,
87+
bool forceColors) {
88+
if (!queuedDiagnostics)
89+
return;
90+
91+
BridgedStringRef bridgedRenderedString{nullptr, 0};
92+
swift_ASTGen_renderQueuedDiagnostics(queuedDiagnostics, /*contextSize=*/2,
93+
forceColors ? 1 : 0,
94+
&bridgedRenderedString);
95+
auto renderedString = bridgedRenderedString.unbridged();
96+
if (renderedString.data()) {
97+
OS.write(renderedString.data(), renderedString.size());
98+
swift_ASTGen_freeBridgedString(renderedString);
99+
}
100+
swift_ASTGen_destroyQueuedDiagnostics(queuedDiagnostics);
101+
queuedDiagnostics = nullptr;
102+
queuedBuffers.clear();
103+
104+
if (includeTrailingBreak)
105+
OS << "\n";
106+
}
107+
108+
void *DiagnosticBridge::getSourceFileSyntax(SourceManager &sourceMgr,
109+
unsigned bufferID,
110+
StringRef displayName) {
111+
auto known = sourceFileSyntax.find({&sourceMgr, bufferID});
112+
if (known != sourceFileSyntax.end())
113+
return known->second;
114+
115+
auto bufferContents = sourceMgr.getEntireTextForBuffer(bufferID);
116+
auto sourceFile = swift_ASTGen_parseSourceFile(
117+
bufferContents.data(), bufferContents.size(), "module",
118+
displayName.str().c_str(), /*ctx*/ nullptr);
119+
120+
sourceFileSyntax[{&sourceMgr, bufferID}] = sourceFile;
121+
return sourceFile;
122+
}
123+
124+
void DiagnosticBridge::queueBuffer(SourceManager &sourceMgr,
125+
unsigned bufferID) {
126+
QueuedBuffer knownSourceFile = queuedBuffers[bufferID];
127+
if (knownSourceFile)
128+
return;
129+
130+
// Find the parent and position in parent, if there is one.
131+
int parentID = -1;
132+
int positionInParent = 0;
133+
std::string displayName;
134+
auto generatedSourceInfo = sourceMgr.getGeneratedSourceInfo(bufferID);
135+
if (generatedSourceInfo) {
136+
SourceLoc parentLoc = generatedSourceInfo->originalSourceRange.getEnd();
137+
if (parentLoc.isValid()) {
138+
parentID = sourceMgr.findBufferContainingLoc(parentLoc);
139+
positionInParent = sourceMgr.getLocOffsetInBuffer(parentLoc, parentID);
140+
141+
// Queue the parent buffer.
142+
queueBuffer(sourceMgr, parentID);
143+
}
144+
145+
if (!generatedSourceInfo->macroName.empty()) {
146+
if (generatedSourceInfo->attachedMacroCustomAttr)
147+
displayName = "macro expansion @" + generatedSourceInfo->macroName;
148+
else
149+
displayName = "macro expansion #" + generatedSourceInfo->macroName;
150+
}
151+
}
152+
153+
if (displayName.empty()) {
154+
displayName =
155+
sourceMgr.getDisplayNameForLoc(sourceMgr.getLocForBufferStart(bufferID))
156+
.str();
157+
}
158+
159+
auto sourceFile = getSourceFileSyntax(sourceMgr, bufferID, displayName);
160+
swift_ASTGen_addQueuedSourceFile(queuedDiagnostics, bufferID, sourceFile,
161+
(const uint8_t *)displayName.data(),
162+
displayName.size(), parentID,
163+
positionInParent);
164+
queuedBuffers[bufferID] = sourceFile;
165+
}
166+
167+
SmallVector<unsigned, 1>
168+
DiagnosticBridge::getSourceBufferStack(SourceManager &sourceMgr,
169+
SourceLoc loc) {
170+
SmallVector<unsigned, 1> stack;
171+
while (true) {
172+
if (loc.isInvalid())
173+
return stack;
174+
175+
unsigned bufferID = sourceMgr.findBufferContainingLoc(loc);
176+
stack.push_back(bufferID);
177+
178+
auto generatedSourceInfo = sourceMgr.getGeneratedSourceInfo(bufferID);
179+
if (!generatedSourceInfo)
180+
return stack;
181+
182+
loc = generatedSourceInfo->originalSourceRange.getStart();
183+
}
184+
}
185+
186+
DiagnosticBridge::~DiagnosticBridge() {
187+
assert(!queuedDiagnostics && "unflushed diagnostics");
188+
for (const auto &sourceFileSyntax : sourceFileSyntax) {
189+
swift_ASTGen_destroySourceFile(sourceFileSyntax.second);
190+
}
191+
}
192+
193+
#endif // SWIFT_BUILD_SWIFT_SYNTAX

0 commit comments

Comments
 (0)