Skip to content

Commit f0d306e

Browse files
committed
[ASTPrinter] Enforce pairing of pre/post decl callbacks
We can have multiple printDeclPre callbacks pending (e.g top-level-code decls), so use a vector to ensure we don't lose the earlier callbacks. We also may end up calling printDeclPost without forcing the corresponding printDeclPre first if the decl doesn't actually print anything (e.g. an if-config statement when skipping those). So add a wrapper callPrintDeclPost that can check for this and skip both callbacks. In theory, we could handle this case by instead making all ast nodes go through something like shouldPrint() and making an invariant that something will be printed if and only if shouldPrint returns true. However, that is not an obvious win, because it forces us to walk all the first-level statements and decls inside a top-level-code decl to determine if anything will be printed, and it also means we can never make local decisions about whether something will be printed. For now, I've chosen to maintain flexibility by recovering from unprinted decls. Finally, add a bunch of assertions to try to keep callbacks sane.
1 parent e7738bb commit f0d306e

File tree

3 files changed

+61
-20
lines changed

3 files changed

+61
-20
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ enum class PrintNameContext {
4747
class ASTPrinter {
4848
unsigned CurrentIndentation = 0;
4949
unsigned PendingNewlines = 0;
50-
const Decl *PendingDeclPreCallback = nullptr;
50+
/// The queue of pending printDeclPre callbacks that will be run when we print
51+
/// a non-whitespace character.
52+
SmallVector<const Decl *, 4> PendingDeclPreCallbacks;
5153
const Decl *PendingDeclLocCallback = nullptr;
5254
Optional<PrintNameContext> PendingNamePreCallback;
5355
const NominalTypeDecl *SynthesizeTarget = nullptr;
@@ -59,19 +61,29 @@ class ASTPrinter {
5961

6062
virtual void printText(StringRef Text) = 0;
6163

64+
// MARK: Callback interface.
65+
6266
/// Called after the printer decides not to print D.
67+
///
68+
/// Callers should use callAvoidPrintDeclPost().
6369
virtual void avoidPrintDeclPost(const Decl *D) {};
6470
/// Called before printing of a declaration.
71+
///
72+
/// Callers should use callPrintDeclPre().
6573
virtual void printDeclPre(const Decl *D) {}
6674
/// Called before printing at the point which would be considered the location
6775
/// of the declaration (normally the name of the declaration).
76+
///
77+
/// Callers should use callPrintDeclLoc().
6878
virtual void printDeclLoc(const Decl *D) {}
6979
/// Called after printing the name of the declaration.
7080
virtual void printDeclNameEndLoc(const Decl *D) {}
7181
/// Called after printing the name of a declaration, or in the case of
7282
/// functions its signature.
7383
virtual void printDeclNameOrSignatureEndLoc(const Decl *D) {}
7484
/// Called after finishing printing of a declaration.
85+
///
86+
/// Callers should use callPrintDeclPost().
7587
virtual void printDeclPost(const Decl *D) {}
7688

7789
/// Called before printing a type.
@@ -127,6 +139,9 @@ class ASTPrinter {
127139
}
128140

129141
void setSynthesizedTarget(NominalTypeDecl *Target) {
142+
assert((!SynthesizeTarget || !Target || Target == SynthesizeTarget) &&
143+
"unexpected change of setSynthesizedTarget");
144+
// FIXME: this can overwrite the original target with nullptr.
130145
SynthesizeTarget = Target;
131146
}
132147

@@ -136,21 +151,48 @@ class ASTPrinter {
136151

137152
virtual void printIndent();
138153

154+
// MARK: Callback interface wrappers that perform ASTPrinter bookkeeping.
155+
139156
/// Schedule a \c printDeclPre callback to be called as soon as a
140157
/// non-whitespace character is printed.
141158
void callPrintDeclPre(const Decl *D) {
142-
PendingDeclPreCallback = D;
159+
PendingDeclPreCallbacks.emplace_back(D);
160+
}
161+
162+
/// Make a callback to printDeclPost(), performing any necessary bookeeping.
163+
void callPrintDeclPost(const Decl *D) {
164+
if (!PendingDeclPreCallbacks.empty() &&
165+
PendingDeclPreCallbacks.back() == D) {
166+
// Nothing printed for D; skip both pre and post callbacks.
167+
// Ideally we wouldn't get as far as setting up the callback if we aren't
168+
// going to print anything, but currently that would mean walking the
169+
// children of top-level code decls to determine.
170+
PendingDeclPreCallbacks.pop_back();
171+
return;
172+
}
173+
printDeclPost(D);
174+
}
175+
176+
/// Make a callback to avoidPrintDeclPost(), performing any necessary
177+
/// bookkeeping.
178+
void callAvoidPrintDeclPost(const Decl *D) {
179+
assert((PendingDeclPreCallbacks.empty() ||
180+
PendingDeclPreCallbacks.back() != D) &&
181+
"printDeclPre should not be called on avoided decl");
182+
avoidPrintDeclPost(D);
143183
}
144184

145185
/// Schedule a \c printDeclLoc callback to be called as soon as a
146186
/// non-whitespace character is printed.
147187
void callPrintDeclLoc(const Decl *D) {
188+
assert(!PendingDeclLocCallback && "unexpected nested callPrintDeclLoc");
148189
PendingDeclLocCallback = D;
149190
}
150191

151192
/// Schedule a \c printNamePre callback to be called as soon as a
152193
/// non-whitespace character is printed.
153194
void callPrintNamePre(PrintNameContext Context) {
195+
assert(!PendingNamePreCallback && "unexpected nested callPrintNamePre");
154196
PendingNamePreCallback = Context;
155197
}
156198

lib/AST/ASTPrinter.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -330,26 +330,27 @@ void ASTPrinter::printTextImpl(StringRef Text) {
330330
printText(Str);
331331
printIndent();
332332
}
333-
334-
const Decl *PreD = PendingDeclPreCallback;
335-
const Decl *LocD = PendingDeclLocCallback;
336-
auto NameContext = PendingNamePreCallback;
337-
PendingDeclPreCallback = nullptr;
333+
334+
// Get the pending callbacks and remove them from the printer. They must all
335+
// be removed before calling any of them to ensure correct ordering.
336+
auto pendingDeclPre = PendingDeclPreCallbacks;
337+
PendingDeclPreCallbacks.clear();
338+
auto *LocD = PendingDeclLocCallback;
338339
PendingDeclLocCallback = nullptr;
340+
auto NameContext = PendingNamePreCallback;
339341
PendingNamePreCallback.reset();
340342

341-
if (PreD) {
343+
// Perform pending callbacks.
344+
for (const Decl *PreD : pendingDeclPre) {
342345
if (SynthesizeTarget && PreD->getKind() == DeclKind::Extension)
343346
printSynthesizedExtensionPre(cast<ExtensionDecl>(PreD), SynthesizeTarget);
344347
else
345348
printDeclPre(PreD);
346349
}
347-
if (LocD) {
350+
if (LocD)
348351
printDeclLoc(LocD);
349-
}
350-
if (NameContext) {
352+
if (NameContext)
351353
printNamePre(*NameContext);
352-
}
353354

354355
printText(Text);
355356
}
@@ -702,7 +703,7 @@ class PrintAST : public ASTVisitor<PrintAST> {
702703
Printer.printSynthesizedExtensionPost(cast<ExtensionDecl>(D),
703704
Options.TransformContext->getNominal());
704705
} else {
705-
Printer.printDeclPost(D);
706+
Printer.callPrintDeclPost(D);
706707
}
707708
return true;
708709
}
@@ -1039,7 +1040,7 @@ bool swift::shouldPrint(const Decl *D, PrintOptions &Options) {
10391040
bool PrintAST::shouldPrint(const Decl *D, bool Notify) {
10401041
auto Result = swift::shouldPrint(D, Options);
10411042
if (!Result && Notify)
1042-
Printer.avoidPrintDeclPost(D);
1043+
Printer.callAvoidPrintDeclPost(D);
10431044
return Result;
10441045
}
10451046

@@ -1826,9 +1827,7 @@ void PrintAST::visitParamDecl(ParamDecl *decl) {
18261827
void PrintAST::printOneParameter(const ParamDecl *param, bool Curried,
18271828
bool ArgNameIsAPIByDefault) {
18281829
Printer.callPrintDeclPre(param);
1829-
defer {
1830-
Printer.printDeclPost(param);
1831-
};
1830+
defer { Printer.callPrintDeclPost(param); };
18321831

18331832
auto printArgName = [&]() {
18341833
// Print argument name.

lib/IDE/ModuleInterfacePrinting.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ void swift::ide::printSubmoduleInterface(
420420
auto PrintDecl = [&](Decl *D) -> bool {
421421
ASTPrinter &Printer = *PrinterToUse;
422422
if (!shouldPrint(D, AdjustedOptions)) {
423-
Printer.avoidPrintDeclPost(D);
423+
Printer.callAvoidPrintDeclPost(D);
424424
return false;
425425
}
426426
if (auto Ext = dyn_cast<ExtensionDecl>(D)) {
@@ -451,7 +451,7 @@ void swift::ide::printSubmoduleInterface(
451451
// Print Ext and add sub-types of Ext.
452452
for (auto Ext : NTD->getExtensions()) {
453453
if (!shouldPrint(Ext, AdjustedOptions)) {
454-
Printer.avoidPrintDeclPost(Ext);
454+
Printer.callAvoidPrintDeclPost(Ext);
455455
continue;
456456
}
457457
if (Ext->hasClangNode())
@@ -618,7 +618,7 @@ void swift::ide::printHeaderInterface(
618618
for (auto *D : ClangDecls) {
619619
ASTPrinter &Printer = *PrinterToUse;
620620
if (!shouldPrint(D, AdjustedOptions)) {
621-
Printer.avoidPrintDeclPost(D);
621+
Printer.callAvoidPrintDeclPost(D);
622622
continue;
623623
}
624624
if (D->print(Printer, AdjustedOptions))

0 commit comments

Comments
 (0)