From e98b01e47bce28c8f07adac17019b457ce69053b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Fri, 24 Jan 2025 15:30:24 -0800 Subject: [PATCH 1/3] Add a flag to dump the AST as JSON. --- include/swift/AST/Attr.h | 6 + include/swift/AST/CatchNode.h | 5 + include/swift/AST/Decl.h | 16 + include/swift/AST/DiagnosticsFrontend.def | 10 + include/swift/AST/SourceFile.h | 3 + include/swift/Frontend/FrontendOptions.h | 10 + include/swift/Option/Options.td | 6 + lib/AST/ASTDumper.cpp | 4342 +++++++++++------ lib/AST/Attr.cpp | 8 + lib/AST/Decl.cpp | 29 + lib/Driver/ToolChains.cpp | 1 + .../ArgsToFrontendOptionsConverter.cpp | 27 + lib/FrontendTool/FrontendTool.cpp | 27 +- test/Frontend/ast-dump-json-macros.swift | 17 + test/Frontend/ast-dump-json-no-crash.swift | 469 ++ .../ast-dump-json-objc-no-crash.swift | 30 + test/Frontend/ast-dump-json-output-map.swift | 33 + test/Frontend/ast-dump-json-zlib.swift | 13 + test/Frontend/ast-dump-json.swift | 30 + test/Frontend/dump-parse.swift | 6 +- test/Frontend/module-alias-dump-ast.swift | 2 - .../ApplicationMain/attr_main_throws.swift | 2 +- 22 files changed, 3717 insertions(+), 1375 deletions(-) create mode 100644 test/Frontend/ast-dump-json-macros.swift create mode 100644 test/Frontend/ast-dump-json-no-crash.swift create mode 100644 test/Frontend/ast-dump-json-objc-no-crash.swift create mode 100644 test/Frontend/ast-dump-json-output-map.swift create mode 100644 test/Frontend/ast-dump-json-zlib.swift create mode 100644 test/Frontend/ast-dump-json.swift diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index fc5f8a886692d..b2653ab5c652d 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1709,6 +1709,12 @@ class ImplementsAttr : public DeclAttribute { DeclName MemberName); ProtocolDecl *getProtocol(DeclContext *dc) const; + + /// Returns the protocol declaration containing the requirement being + /// implemented by the attributed declaration if it has already been computed, + /// otherwise `nullopt`. This should only be used for dumping. + std::optional getCachedProtocol(DeclContext *dc) const; + TypeRepr *getProtocolTypeRepr() const { return TyR; } DeclName getMemberName() const { return MemberName; } diff --git a/include/swift/AST/CatchNode.h b/include/swift/AST/CatchNode.h index 6265fc963a179..67525f13acbeb 100644 --- a/include/swift/AST/CatchNode.h +++ b/include/swift/AST/CatchNode.h @@ -45,6 +45,11 @@ class CatchNode: public llvm::PointerUnion< /// needs to be inferred. Type getExplicitCaughtType(ASTContext &ctx) const; + /// Returns the explicitly-specified type error that will be caught by this + /// catch node, or `nullopt` if it has not yet been computed. This should only + /// be used for dumping. + std::optional getCachedExplicitCaughtType(ASTContext &ctx) const; + friend llvm::hash_code hash_value(CatchNode catchNode) { using llvm::hash_value; return hash_value(catchNode.getOpaqueValue()); diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0e662968c8db0..591df540c9f38 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3241,6 +3241,10 @@ class ValueDecl : public Decl { /// Get the decl for this value's opaque result type, if it has one. OpaqueTypeDecl *getOpaqueResultTypeDecl() const; + /// Gets the decl for this value's opaque result type if it has already been + /// computed, or `nullopt` otherwise. This should only be used for dumping. + std::optional getCachedOpaqueResultTypeDecl() const; + /// Get the representative for this value's opaque result type, if it has one. /// Returns a `TypeRepr` instead of an `OpaqueReturnTypeRepr` because 'some' /// types might appear in one or more structural positions, e.g. (some P, @@ -7657,6 +7661,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Retrieves the thrown interface type. Type getThrownInterfaceType() const; + /// Returns the thrown interface type of this function if it has already been + /// computed, otherwise `nullopt`. This should only be used for dumping. + std::optional getCachedThrownInterfaceType() const; + /// Retrieve the "effective" thrown interface type, or std::nullopt if /// this function cannot throw. /// @@ -8217,6 +8225,10 @@ class FuncDecl : public AbstractFunctionDecl { /// Retrieve the result interface type of this function. Type getResultInterfaceType() const; + /// Returns the result interface type of this function if it has already been + /// computed, otherwise `nullopt`. This should only be used for dumping. + std::optional getCachedResultInterfaceType() const; + /// isUnaryOperator - Determine whether this is a unary operator /// implementation. This check is a syntactic rather than type-based check, /// which looks at the number of parameters specified, in order to allow @@ -9417,6 +9429,10 @@ class MacroDecl : public GenericContext, public ValueDecl { /// Retrieve the interface type produced when expanding this macro. Type getResultInterfaceType() const; + /// Returns the result interface type of this macro if it has already been + /// computed, otherwise `nullopt`. This should only be used for dumping. + std::optional getCachedResultInterfaceType() const; + /// Determine the contexts in which this macro can be applied. MacroRoles getMacroRoles() const; diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index fedd76aed2d3d..9d1876c13167e 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -589,5 +589,15 @@ ERROR(experimental_not_supported_in_production,none, "experimental feature '%0' cannot be enabled in production compiler", (StringRef)) +ERROR(json_zlib_not_supported,none, + "this compiler was not built with zlib compression support enabled; " + "'-dump-ast-format json-zlib' cannot be used", ()) + +ERROR(ast_format_requires_dump_ast,none, + "structured AST formats are only supported when using -dump-ast", ()) + +ERROR(unknown_dump_ast_format,none, + "unknown format '%0' requested with '-dump-ast-format'", (StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 615da4088a6d9..a05c54b47edbe 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -677,6 +677,9 @@ class SourceFile final : public FileUnit { SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, bool parseIfNeeded = false) const; + /// Dumps this source file's AST in JSON format to the given output stream. + void dumpJSON(raw_ostream &os) const; + /// Pretty-print the contents of this source file. /// /// \param Printer The AST printer used for printing the contents. diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 8cae648d4d1aa..0702e67a82f3e 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -433,6 +433,16 @@ class FrontendOptions { /// -dump-scope-maps. SmallVector, 2> DumpScopeMapLocations; + /// The possible output formats supported for dumping ASTs. + enum class ASTFormat { + Default, ///< S-expressions for debugging + JSON, ///< Structured JSON for analysis + JSONZlib, ///< Like JSON, but zlib-compressed + }; + + /// The output format generated by the `-dump-ast` flag. + ASTFormat DumpASTFormat = ASTFormat::Default; + /// Determines whether the static or shared resource folder is used. /// When set to `true`, the default resource folder will be set to /// '.../lib/swift', otherwise '.../lib/swift_static'. diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index a95987547a297..4a098768d3824 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1343,6 +1343,12 @@ def emit_parse : Flag<["-"], "emit-parse">, Alias, def dump_ast : Flag<["-"], "dump-ast">, HelpText<"Parse and type-check input file(s) and dump AST(s)">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def dump_ast_format : Separate<["-"], "dump-ast-format">, + HelpText<"Desired format for -dump-ast output " + "('default', 'json', or 'json-zlib'); no format is guaranteed " + "stable across different compiler versions">, + MetaVarName<"">, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; def emit_ast : Flag<["-"], "emit-ast">, Alias, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; def dump_scope_maps : Separate<["-"], "dump-scope-maps">, diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 56704552fbaad..c4b1c883b5be6 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -28,16 +28,20 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeVisitor.h" +#include "swift/AST/USRGeneration.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" #include "swift/Basic/QuotedString.h" #include "swift/Basic/STLExtras.h" +#include "swift/Basic/SourceLoc.h" +#include "swift/Basic/SourceManager.h" #include "clang/AST/Type.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/Process.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" @@ -97,6 +101,7 @@ DEF_COLOR(Expr, MAGENTA, true) DEF_COLOR(ExprModifier, CYAN, false) DEF_COLOR(DeclModifier, CYAN, false) DEF_COLOR(ArgModifier, CYAN, false) +DEF_COLOR(DeclAttribute, CYAN, false) DEF_COLOR(ClosureModifier, CYAN, false) DEF_COLOR(FieldLabel, CYAN, false) DEF_COLOR(Location, CYAN, false) @@ -189,6 +194,71 @@ class escaping_ostream : public raw_ostream { virtual void anchor() override {} }; + +/// Returns the USR of the given declaration. Gracefully returns an empty +/// string if D is null or invalid. +std::string declUSR(const Decl *D) { + if (!D) + return ""; + + // Certain local synthesized declarations won't be assigned a local + // discriminator, later causing an assertion if we try to generate a USR + // for them. Avoid these. + // FIXME: USR generation should probably do a better job of avoiding the + // asserting code path? + if (auto VD = dyn_cast(D); + VD && VD->getDeclContext()->isLocalContext() && + (!VD->getLoc().isValid() || + (VD->getModuleContext() + ->getSourceFileContainingLocation(VD->getLoc()) + ->getFulfilledMacroRole() == std::nullopt))) { + return ""; + } + + std::string usr; + llvm::raw_string_ostream os(usr); + if (swift::ide::printDeclUSR(D, os)) + return ""; + return usr; +} + +/// Returns the USR of the given type. Gracefully returns an empty string +/// if the type is invalid. +std::string typeUSR(Type type) { + if (!type) + return ""; + + if (type->hasArchetype()) { + // We can't generate USRs for types that contain archetypes. Replace them + // with their interface types. + type = type.transformRec([&](TypeBase *t) -> std::optional { + if (auto AT = dyn_cast(t)) { + return AT->getInterfaceType(); + } + return std::nullopt; + }); + } + + std::string usr; + llvm::raw_string_ostream os(usr); + if (swift::ide::printTypeUSR(type, os)) + return ""; + return usr; +} + +/// Returns the USR of the given value declaration's type. Gracefully returns +/// the empty string if D is null. +std::string declTypeUSR(const ValueDecl *D) { + if (!D) + return ""; + + std::string usr; + llvm::raw_string_ostream os(usr); + if (swift::ide::printDeclTypeUSR(D, os)) + return ""; + return usr; +} + } // end anonymous namespace static StringRef getDumpString(SILFunctionType::Representation value) { @@ -407,9 +477,6 @@ static StringRef getDumpString(MagicIdentifierLiteralExpr::Kind kind) { static StringRef getDumpString(ObjectLiteralExpr::LiteralKind kind) { return ObjectLiteralExpr::getLiteralKindPlainName(kind); } -static StringRef getDumpString(FunctionRefKind kind) { - return getFunctionRefKindStr(kind); -} static StringRef getDumpString(ParamSpecifier specifier) { return ParamDecl::getSpecifierSpelling(specifier); } @@ -427,7 +494,9 @@ static StringRef getDumpString(ValueOwnership ownership) { llvm_unreachable("Unhandled ValueOwnership in switch."); } - +static StringRef getDumpString(PlatformKind kind) { + return platformString(kind); +} static StringRef getDumpString(ForeignErrorConvention::IsOwned_t owned) { switch (owned) { case swift::ForeignErrorConvention::IsNotOwned: @@ -449,6 +518,103 @@ static StringRef getDumpString(RequirementKind kind) { llvm_unreachable("Unhandled RequirementKind in switch."); } +static StringRef getDumpString(RequirementReprKind kind) { + switch (kind) { + case RequirementReprKind::TypeConstraint: return "type_constraint"; + case RequirementReprKind::SameType: return "same_type"; + case RequirementReprKind::LayoutConstraint: return "layout_constraint"; + } + + llvm_unreachable("Unhandled RequirementKind in switch."); +} +static StringRef getDumpString(ClangImporterSynthesizedTypeAttr::Kind kind) { + switch (kind) { + case ClangImporterSynthesizedTypeAttr::Kind::NSErrorWrapper: + return "NSErrorWrapper"; + case ClangImporterSynthesizedTypeAttr::Kind::NSErrorWrapperAnon: + return "NSErrorWrapperAnon"; + } + llvm_unreachable("unhandled ClangImporterSynthesizedTypeAttr::Kind"); +} +static StringRef getDumpString(FunctionRefKind kind) { + switch (kind) { + case FunctionRefKind::Unapplied: + return "unapplied"; + case FunctionRefKind::SingleApply: + return "single_apply"; + case FunctionRefKind::DoubleApply: + return "double_apply"; + case FunctionRefKind::Compound: + return "compound"; + } +} +static StringRef getDumpString(ExternKind kind) { + switch (kind) { + case ExternKind::C: + return "C"; + case ExternKind::Wasm: + return "Wasm"; + } + llvm_unreachable("unhandled ExternKind"); +} +static StringRef getDumpString(InlineKind kind) { + switch (kind) { + case InlineKind::Always: + return "always"; + case InlineKind::Never: + return "never"; + } + llvm_unreachable("unhandled InlineKind"); +} +static StringRef getDumpString(EffectsKind kind) { + switch (kind) { + case EffectsKind::ReadNone: + return "ReadNone"; + case EffectsKind::ReadOnly: + return "ReadOnly"; + case EffectsKind::ReleaseNone: + return "ReleaseNone"; + case EffectsKind::ReadWrite: + return "ReadWrite"; + case EffectsKind::Unspecified: + return "Unspecified"; + case EffectsKind::Custom: + return "Custom"; + } + llvm_unreachable("unhandled EffectsKind"); +} +static StringRef getDumpString(MacroRole role) { + return getMacroRoleString(role); +} +static StringRef getDumpString(ExclusivityAttr::Mode mode) { + switch (mode) { + case ExclusivityAttr::Mode::Checked: + return "checked"; + case ExclusivityAttr::Mode::Unchecked: + return "unchecked"; + } + llvm_unreachable("unhandled ExclusivityAttr::Mode"); +} +static StringRef getDumpString(OptimizationMode mode) { + switch (mode) { + case OptimizationMode::NotSet: + return ""; + case OptimizationMode::NoOptimization: + return "NoOptimization"; + case OptimizationMode::ForSpeed: + return "ForSpeed"; + case OptimizationMode::ForSize: + return "ForSize"; + } +} +static StringRef getDumpString(NonSendableKind kind) { + switch (kind) { + case NonSendableKind::Assumed: + return "Assumed"; + case NonSendableKind::Specific: + return "Specific"; + } +} static StringRef getDumpString(StringRef s) { return s; } @@ -460,6 +626,8 @@ static size_t getDumpString(size_t value) { } static void *getDumpString(void *value) { return value; } +static StringRef getDumpString(Identifier ident) { return ident.str(); } + //===----------------------------------------------------------------------===// // Decl printing. //===----------------------------------------------------------------------===// @@ -480,14 +648,334 @@ static Type defaultGetTypeOfKeyPathComponent(KeyPathExpr *E, unsigned index) { using VisitedConformances = llvm::SmallPtrSetImpl; namespace { + /// Represents a label attached to some data in the AST being dumped. + /// + /// This type exists to balance the simplified S-expression output with + /// the need for additional structure in more complex data formats like + /// JSON. The S-expression output, in most cases, prints nested data as + /// unlabeled children (indented one more step) and allows fields to be + /// printed without explicit names, but this isn't compatible with JSON where + /// each value needs its own unique label/key (or it needs to be an element + /// of an array). + class Label { + StringRef Text; + bool IsOptional; + + Label(StringRef text, bool isOptional) + : Text(text), IsOptional(isOptional) {} + + public: + /// Returns a new label that is always printed, even in the default + /// (S-expression) output. + static Label always(StringRef text) { + return Label(text, /*isOptional=*/ false); + } + + /// Returns a new label that is optional on output formats that allow + /// labels to be omitted. + static Label optional(StringRef text) { + return Label(text, /*isOptional=*/ true); + } + + /// Returns true if the label's text is empty. + bool empty() const { return Text.empty(); } + + /// Returns true if the label can be omitted by output formats that support + /// optional labels. + bool isOptional() const { return IsOptional; } + + /// Returns the label's text. + StringRef text() const { return Text; } + }; + + /// Defines the interface for low-level printing operations that take place + /// when dumping a Swift AST. + class PrintWriterBase { + protected: + /// Only used by the S-expression writer to change the dumper to use + /// single quotes when printing substitution maps in full. + char Quote = '\"'; + + /// Tracks the source buffer ID of the main source file, which subclasses + /// can use to distinguish ranges/locations in that file vs. ranges in other + /// buffers, like macro expansions. + unsigned MainBufferID; + + public: + virtual ~PrintWriterBase() {} + + char quote() const { return Quote; } + void setQuote(char quote) { Quote = quote; } + + void setMainBufferID(unsigned bufferID) { MainBufferID = bufferID; } + + /// Call `body` in a context where the printer is ready for a child to be + /// printed. + virtual void printRecArbitrary(std::function body, + Label label) = 0; + + /// Print a range of nodes as a single "array" child node. + virtual void printRecRange(std::function body, + Label label) = 0; + + /// Call `body` in a context where the printer is ready for a list of + /// children to be printed. + virtual void printListArbitrary(std::function body, Label label) = 0; + + /// Print the beginning of a new node, including its type and an optional + /// label for it. + virtual void printHead(StringRef name, TerminalColor color, Label label) = 0; + + /// Print the end of a new node. + virtual void printFoot() = 0; + + /// Print a field with a short keyword-style value, printing the value by + /// passing a closure that takes a \c raw_ostream. + virtual void printFieldRaw(std::function body, + Label label, TerminalColor color) = 0; + + /// Print a field with a long value that will be automatically quoted and + /// escaped, printing the value by passing a closure that takes a + /// \c raw_ostream. + virtual void printFieldQuotedRaw( + std::function body, Label name, + TerminalColor color) = 0; + + /// Print a simple boolean value, printing the value by passing a closure + /// that takes a \c raw_ostream. + virtual void printFlagRaw(std::function body, + TerminalColor color) = 0; + + /// Print a field containing a node's source location. + virtual void printSourceLoc(const SourceLoc L, const ASTContext *Ctx, + Label label) = 0; + + /// Print a field containing a node's source range. + virtual void printSourceRange(const SourceRange R, const ASTContext *Ctx, + Label label) = 0; + + virtual bool hasNonStandardOutput() const = 0; + + /// Indicates whether the output format is meant to be parsable. Parsable + /// output should use structure rather than stringification to convey + /// detailed information, and generally provides more information than the + /// non-parsable formats, which are usually meant for human debugging. + virtual bool isParsable() const = 0; + }; + + /// Implements the default (pseudo-S-expression) output format for `-dump-ast`. + class DefaultWriter : public PrintWriterBase { + raw_ostream &OS; + unsigned Indent; + + public: + DefaultWriter(raw_ostream &os, unsigned indent) : OS(os), Indent(indent) {} + + void printRecArbitrary(std::function body, + Label label) override { + Indent += 2; + OS << '\n'; + body(label); + Indent -= 2; + } + + void printRecRange(std::function body, + Label label) override { + printRecArbitrary([&](Label label) { + printHead("array", ASTNodeColor, label); + body(); + printFoot(); + }, label); + } + + void printListArbitrary(std::function body, Label label) override { + // This writer ignores the label and simply prints the list directly + // underneath its parent. + body(); + } + + void printHead(StringRef name, TerminalColor color, Label label) override { + OS.indent(Indent); + PrintWithColorRAII(OS, ParenthesisColor) << '('; + if (!label.isOptional() && !label.empty()) { + PrintWithColorRAII(OS, FieldLabelColor) << label.text(); + OS << "="; + } + PrintWithColorRAII(OS, color) << name; + } + + void printFoot() override { + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + + void printFieldRaw(std::function body, + Label label, TerminalColor color) override { + OS << " "; + if (!label.isOptional() && !label.empty()) + PrintWithColorRAII(OS, color) << label.text() << "="; + std::string value; + llvm::raw_string_ostream SOS(value); + body(SOS); + PrintWithColorRAII(OS, color) << value; + } + + void printFieldQuotedRaw(std::function body, + Label name, TerminalColor color) override { + printFieldRaw([&](raw_ostream &OS) { + OS << Quote; + { escaping_ostream escOS(OS); body(escOS); } + OS << Quote; + }, name, color); + } + + void printFlagRaw(std::function body, + TerminalColor color) override { + printFieldRaw(body, Label::always(""), color); + } + + void printSourceLoc(const SourceLoc L, const ASTContext *Ctx, + Label label) override { + printFieldRaw([&](raw_ostream &OS) { + escaping_ostream escOS(OS); + L.print(escOS, Ctx->SourceMgr); + }, label, LocationColor); + } + + void printSourceRange(const SourceRange R, const ASTContext *Ctx, + Label label) override { + printFieldRaw([&](raw_ostream &OS) { + escaping_ostream escOS(OS); + R.print(escOS, Ctx->SourceMgr, /*PrintText=*/false); + }, label, RangeColor); + } + + bool hasNonStandardOutput() const override { + return &OS != &llvm::errs() && &OS != &llvm::dbgs(); + } + + bool isParsable() const override { return false; } + }; + + /// Implements JSON formatted output for `-ast-dump`. + class JSONWriter : public PrintWriterBase { + llvm::json::OStream OS; + std::vector InObjectStack; + + public: + JSONWriter(raw_ostream &os, unsigned indent = 0) : OS(os, indent) {} + + void printRecArbitrary(std::function body, + Label label) override { + // The label is ignored if we're not printing inside an object (meaning + // we must be in an array). + assert(!InObjectStack.empty() && "printHead or printListArbitrary not called before printRecArbitrary"); + if (InObjectStack.back()) { + OS.attributeBegin(label.text()); + body(Label::optional("")); + OS.attributeEnd(); + } else { + body(Label::optional("")); + } + } + + void printRecRange(std::function body, Label label) override { + printListArbitrary([&]{ body(); }, label); + } + + void printListArbitrary(std::function body, Label label) override { + OS.attributeBegin(label.text()); + OS.arrayBegin(); + InObjectStack.push_back(false); + body(); + InObjectStack.pop_back(); + OS.arrayEnd(); + OS.attributeEnd(); + } + + void printHead(StringRef name, TerminalColor color, Label label) override { + OS.objectBegin(); + InObjectStack.push_back(true); + OS.attribute(label.empty() ? "_kind" : label.text(), name); + } + + void printFoot() override { + InObjectStack.pop_back(); + OS.objectEnd(); + } + + void printFieldRaw(std::function body, + Label label, TerminalColor color) override { + std::string value; + llvm::raw_string_ostream SOS(value); + body(SOS); + // The label is ignored if we're not printing inside an object (meaning + // we must be in an array). + if (InObjectStack.back()) { + OS.attribute(label.text(), value); + } else { + OS.value(value); + } + } + + void printFieldQuotedRaw(std::function body, + Label label, TerminalColor color) override { + // No need to do special quoting for complex values; the JSON output + // stream will do this for us. + printFieldRaw(body, label, color); + } + + void printFlagRaw(std::function body, + TerminalColor color) override { + std::string flag; + llvm::raw_string_ostream SOS(flag); + body(SOS); + OS.attribute(flag, true); + } + + void printSourceLoc(const SourceLoc L, const ASTContext *Ctx, + Label label) override { + // For compactness, we only print source ranges in JSON, since they + // provide a superset of this information. + } + + void printSourceRange(const SourceRange R, const ASTContext *Ctx, + Label label) override { + OS.attributeBegin(label.text()); + OS.objectBegin(); + + SourceManager &srcMgr = Ctx->SourceMgr; + unsigned startBufferID = srcMgr.findBufferContainingLoc(R.Start); + unsigned startOffset = srcMgr.getLocOffsetInBuffer(R.Start, + startBufferID); + OS.attribute("start", startOffset); + + unsigned endBufferID = srcMgr.findBufferContainingLoc(R.End); + unsigned endOffset = srcMgr.getLocOffsetInBuffer(R.End, endBufferID); + OS.attribute("end", endOffset); + + // Only print the buffer ID when it doesn't match the main ID, so that we + // distinguish macro expansions but don't bloat the output with the main + // file name repeated over and over. + if (startBufferID != MainBufferID) + OS.attribute("buffer_id", srcMgr.getIdentifierForBuffer(startBufferID)); + + OS.objectEnd(); + OS.attributeEnd(); + } + + bool hasNonStandardOutput() const override { return true; } + + bool isParsable() const override { return true; } + }; + /// PrintBase - Base type for recursive structured dumps of AST nodes. /// /// Please keep direct I/O, especially of structural elements like /// parentheses and quote marks, confined to this base class. This will help /// if we eventually support alternate output formats for AST dumps. class PrintBase { - raw_ostream &OS; - unsigned Indent; + protected: + PrintWriterBase &Writer; public: bool ParseIfNeeded; llvm::function_ref GetTypeOfExpr; @@ -497,49 +985,50 @@ namespace { char quote = '"'; explicit PrintBase( - raw_ostream &os, unsigned indent = 0, bool parseIfNeeded = false, + PrintWriterBase &writer, bool parseIfNeeded = false, llvm::function_ref getTypeOfExpr = defaultGetTypeOfExpr, llvm::function_ref getTypeOfTypeRepr = nullptr, llvm::function_ref getTypeOfKeyPathComponent = defaultGetTypeOfKeyPathComponent) - : OS(os), Indent(indent), ParseIfNeeded(parseIfNeeded), + : Writer(writer), ParseIfNeeded(parseIfNeeded), GetTypeOfExpr(getTypeOfExpr), GetTypeOfTypeRepr(getTypeOfTypeRepr), GetTypeOfKeyPathComponent(getTypeOfKeyPathComponent) {} bool hasNonStandardOutput() { - return &OS != &llvm::errs() && &OS != &llvm::dbgs(); + return Writer.hasNonStandardOutput(); } - /// Call `Body` in a context where the printer is ready for a child to be printed. - template - void printRecArbitrary(Fn Body, StringRef label = "") { - Indent += 2; - OS << '\n'; - Body(label); - Indent -= 2; + /// Call `Body` in a context where the printer is ready for a child to be + /// printed. + void printRecArbitrary(std::function body, Label label) { + Writer.printRecArbitrary(body, label); } /// Print a declaration as a child node. - void printRec(Decl *D, StringRef label = ""); + void printRec(Decl *D, Label label); /// Print an expression as a child node. - void printRec(Expr *E, StringRef label = ""); + void printRec(Expr *E, Label label); /// Print a statement as a child node. - void printRec(Stmt *S, const ASTContext *Ctx, StringRef label = ""); + void printRec(Stmt *S, const ASTContext *Ctx, Label label); /// Print a type representation as a child node. - void printRec(TypeRepr *T, StringRef label = ""); + void printRec(TypeRepr *T, Label label); /// Print a pattern as a child node. - void printRec(const Pattern *P, StringRef label = ""); + void printRec(const Pattern *P, Label label); /// Print a type as a child node. - void printRec(Type ty, StringRef label = ""); + void printRec(Type ty, Label label); + + /// Print an attribute as a child node. + void printRec(const DeclAttribute *Attr, const ASTContext *Ctx, + DeclContext *dc, Label label); /// Print an \c ASTNode as a child node. void printRec(const ASTNode &Elt, const ASTContext *Ctx, - StringRef label = "") { + Label label) { if (auto *SubExpr = Elt.dyn_cast()) printRec(SubExpr, label); else if (auto *SubStmt = Elt.dyn_cast()) @@ -550,117 +1039,139 @@ namespace { /// Print a statement condition element as a child node. void printRec(StmtConditionElement C, const ASTContext *Ctx, - StringRef Label = "") { + Label label) { switch (C.getKind()) { case StmtConditionElement::CK_Boolean: - return printRec(C.getBoolean()); + return printRec(C.getBoolean(), label); case StmtConditionElement::CK_PatternBinding: - printRecArbitrary([&](StringRef Label) { - printHead("pattern", PatternColor, Label); - printRec(C.getPattern()); - printRec(C.getInitializer()); + printRecArbitrary([&](Label label) { + printHead("pattern", PatternColor, label); + printRec(C.getPattern(), Label::optional("pattern")); + printRec(C.getInitializer(), Label::optional("initializer")); printFoot(); - }, Label); + }, label); break; case StmtConditionElement::CK_Availability: - printRecArbitrary([&](StringRef Label) { - printHead("#available", PatternColor, Label); - for (auto *Query : C.getAvailability()->getQueries()) { - OS << '\n'; - switch (Query->getKind()) { - case AvailabilitySpecKind::PlatformVersionConstraint: - cast(Query)->print(OS, Indent + 2); - break; - case AvailabilitySpecKind::LanguageVersionConstraint: - case AvailabilitySpecKind::PackageDescriptionVersionConstraint: - cast(Query)->print(OS, Indent + 2); - break; - case AvailabilitySpecKind::OtherPlatform: - cast(Query)->print(OS, Indent + 2); - break; - } - } + printRecArbitrary([&](Label label) { + printHead("#available", PatternColor, label); + printList(C.getAvailability()->getQueries(), + [&](auto *query, Label label) { printRec(query, label); }, + Label::optional("queries")); printFoot(); - }, Label); + }, label); break; case StmtConditionElement::CK_HasSymbol: - printRecArbitrary([&](StringRef Label) { - printHead("#_hasSymbol", PatternColor, Label); + printRecArbitrary([&](Label label) { + printHead("#_hasSymbol", PatternColor, label); printSourceRange(C.getSourceRange(), Ctx); - printRec(C.getHasSymbolInfo()->getSymbolExpr()); + printRec(C.getHasSymbolInfo()->getSymbolExpr(), + Label::optional("symbol_expr")); printFoot(); - }, Label); + }, label); break; } } + /// Print an availability spec as a child node. + void printRec(AvailabilitySpec *Spec, Label label) { + printRecArbitrary( + [&](Label label) { + switch (Spec->getKind()) { + case AvailabilitySpecKind::PlatformVersionConstraint: { + auto plat = cast(Spec); + printHead("platform_version_constraint_availability_spec", + PatternColor, label); + printField(platformString(plat->getPlatform()), + Label::always("platform")); + printFieldRaw( + [&](llvm::raw_ostream &OS) { OS << plat->getVersion(); }, + Label::always("version")); + printFoot(); + break; + } + case AvailabilitySpecKind::LanguageVersionConstraint: + case AvailabilitySpecKind::PackageDescriptionVersionConstraint: { + auto agnostic = + cast(Spec); + printHead("platform_agnostic_version_constraint_" + "availability_spec", + PatternColor, label); + printField(agnostic->isLanguageVersionSpecific() + ? "swift" + : "package_description", + Label::always("kind")); + printFieldRaw( + [&](llvm::raw_ostream &OS) { OS << agnostic->getVersion(); }, + Label::always("version")); + printFoot(); + break; + } + case AvailabilitySpecKind::OtherPlatform: + printHead("other_constraint_availability_spec", PatternColor, + label); + printFoot(); + break; + } + }, label); + } + /// Print a range of nodes as a single "array" child node. template - void printRecRange(const NodeRange &range, StringRef topLabel) { - printRecArbitrary([&](StringRef topLabel) { - printHead("array", ASTNodeColor, topLabel); + void printRecRange(const NodeRange &range, Label topLabel) { + Writer.printRecRange([&]() { for (auto node : range) { - printRec(node, ""); + printRec(node, Label::optional("")); } - printFoot(); }, topLabel); } /// Print a range of nodes as a single "array" child node. template - void printRecRange(const NodeRange &range, const ASTContext *Ctx, StringRef topLabel) { - printRecArbitrary([&](StringRef topLabel) { - printHead("array", ASTNodeColor, topLabel); + void printRecRange(const NodeRange &range, const ASTContext *Ctx, + Label topLabel) { + Writer.printRecRange([&]() { for (auto node : range) { - printRec(node, Ctx, ""); + printRec(node, Ctx, Label::optional("")); } - printFoot(); }, topLabel); } - /// Print the beginning of a new node, including its type and an optional label for it. - void printHead(StringRef Name, TerminalColor Color, - StringRef Label = "") { - OS.indent(Indent); - PrintWithColorRAII(OS, ParenthesisColor) << '('; - if (!Label.empty()) { - PrintWithColorRAII(OS, FieldLabelColor) << Label; - OS << "="; - } - - PrintWithColorRAII(OS, Color) << Name; + /// Print the beginning of a new node, including its type and an optional + /// label for it. + void printHead(StringRef Name, TerminalColor Color, Label label) { + Writer.printHead(Name, Color, label); } /// Print the end of a new node. void printFoot() { - PrintWithColorRAII(OS, ParenthesisColor) << ')'; + Writer.printFoot(); } /// Print a single argument as a child node. - void printRec(const Argument &arg) { - printRecArbitrary([&](StringRef L) { + void printRec(const Argument &arg, Label label) { + printRecArbitrary([&](Label L) { printHead("argument", ExprColor, L); auto label = arg.getLabel(); if (!label.empty()) { - printFieldQuoted(label.str(), "label", ArgumentsColor); + printFieldQuoted(label.str(), Label::always("label"), ArgumentsColor); } printFlag(arg.isInOut(), "inout", ArgModifierColor); - printRec(arg.getExpr()); + printRec(arg.getExpr(), Label::optional("expr")); printFoot(); - }); + }, label); } /// Print an argument list as a child node. - void printRec(const ArgumentList *argList, StringRef label = "") { - printRecArbitrary([&](StringRef label) { + void printRec(const ArgumentList *argList, Label label) { + printRecArbitrary([&](Label label) { visitArgumentList(argList, label); }, label); } /// Print an argument list node. - void visitArgumentList(const ArgumentList *argList, StringRef label = "") { + void visitArgumentList(const ArgumentList *argList, Label label) { printHead("argument_list", ExprColor, label); printFlag(argList->isImplicit(), "implicit", ArgModifierColor); @@ -671,164 +1182,272 @@ namespace { auto label = arg.getLabel(); OS << (label.empty() ? "_" : label.str()) << ":"; } - }, "labels", ArgumentsColor); + }, Label::always("labels"), ArgumentsColor); } - for (auto arg : *argList) { - printRec(arg); - } + printList(*argList, [&](auto arg, Label label) { + printRec(arg, label); + }, Label::optional("args")); printFoot(); } /// Print a parameter list as a child node. - void printRec(const ParameterList *params, const ASTContext *ctx = nullptr, - StringRef label = "") { - printRecArbitrary([&](StringRef label) { - visitParameterList(params, ctx, label); + void printRec(const ParameterList *params, Label label, + const ASTContext *ctx = nullptr) { + printRecArbitrary([&](Label label) { + visitParameterList(params, label, ctx); }, label); } /// Print a parameter list node. void visitParameterList(const ParameterList *params, - const ASTContext *ctx = nullptr, - StringRef label = "") { + Label label, const ASTContext *ctx = nullptr) { printHead("parameter_list", ParameterColor, label); if (!ctx && params->size() != 0 && params->get(0)) ctx = ¶ms->get(0)->getASTContext(); printSourceRange(params->getSourceRange(), ctx); - for (auto P : *params) { - printRec(const_cast(P)); - } + printList(*params, [&](auto P, Label label) { + printRec(const_cast(P), label); + }, Label::optional("params")); printFoot(); } /// Print an \c IfConfigClause as a child node. - void printRec(const IfConfigClause &Clause, const ASTContext *Ctx = nullptr, - StringRef Label = "") { - printRecArbitrary([&](StringRef Label) { - printHead((Clause.Cond ? "#if:" : "#else:"), StmtColor, Label); + void printRec(const IfConfigClause &Clause, Label label, + const ASTContext *Ctx = nullptr) { + printRecArbitrary([&](Label label) { + printHead((Clause.Cond ? "#if:" : "#else:"), StmtColor, label); printFlag(Clause.isActive, "active", DeclModifierColor); if (Clause.Cond) { - printRec(Clause.Cond); + printRec(Clause.Cond, Label::optional("cond")); } - printRecRange(Clause.Elements, Ctx, "elements"); + printRecRange(Clause.Elements, Ctx, Label::always("elements")); printFoot(); - }, Label); + }, label); } /// Print a substitution map as a child node. - void printRec(SubstitutionMap map, StringRef label = "") { + void printRec(SubstitutionMap map, Label label) { SmallPtrSet Dumped; printRec(map, Dumped, label); } /// Print a substitution map as a child node. void printRec(SubstitutionMap map, VisitedConformances &visited, - StringRef label = ""); + Label label); /// Print a substitution map as a child node. void printRec(const ProtocolConformanceRef &conf, - VisitedConformances &visited, StringRef label = ""); + VisitedConformances &visited, Label label); /// Print a conformance reference as a child node. - void printRec(const ProtocolConformanceRef &conf, StringRef label = "") { + void printRec(const ProtocolConformanceRef &conf, Label label) { SmallPtrSet Dumped; printRec(conf, Dumped, label); } /// Print a conformance reference as a child node. void printRec(const ProtocolConformance *conformance, - VisitedConformances &visited, StringRef label = ""); + VisitedConformances &visited, Label label); /// Print a requirement node. - void visitRequirement(const Requirement &requirement, StringRef label = "") { + void visitRequirement(const Requirement &requirement, Label label) { printHead("requirement", ASTNodeColor, label); PrintOptions opts; opts.ProtocolQualifiedDependentMemberTypes = true; + printTypeField(requirement.getFirstType(), Label::optional("first_type"), + opts); - printFieldQuotedRaw([&](raw_ostream &out) { - requirement.getFirstType().print(out, opts); - }, ""); - - printField(requirement.getKind(), ""); + printField(requirement.getKind(), Label::optional("kind")); if (requirement.getKind() != RequirementKind::Layout && requirement.getSecondType()) - printFieldQuotedRaw([&](raw_ostream &out) { - requirement.getSecondType().print(out, opts); - }, ""); + printTypeField(requirement.getSecondType(), + Label::optional("second_type"), opts); else if (requirement.getLayoutConstraint()) - printFieldQuoted(requirement.getLayoutConstraint(), ""); + printFieldQuoted(requirement.getLayoutConstraint(), + Label::optional("layout")); printFoot(); } /// Print a requirement as a child node. - void printRec(const Requirement &requirement, StringRef label = "") { - printRecArbitrary([&](StringRef label) { - visitRequirement(requirement); - }); + void printRec(const Requirement &requirement, Label label) { + printRecArbitrary([&](Label label) { + visitRequirement(requirement, label); + }, label); + } + + /// Print a requirement node. + void visitRequirementRepr(const RequirementRepr &requirement, Label label) { + printHead("requirement_repr", ASTNodeColor, label); + + printField(requirement.getKind(), Label::optional("kind")); + switch (requirement.getKind()) { + case RequirementReprKind::TypeConstraint: + printRec(requirement.getSubjectRepr(), Label::optional("first_type")); + printRec(requirement.getConstraintRepr(), Label::optional("second_type")); + break; + case RequirementReprKind::SameType: + printRec(requirement.getFirstTypeRepr(), Label::optional("first_type")); + printRec(requirement.getSecondTypeRepr(), Label::optional("second_type")); + break; + case RequirementReprKind::LayoutConstraint: + printRec(requirement.getSubjectRepr(), Label::optional("first_type")); + printFieldQuoted(requirement.getLayoutConstraint(), + Label::optional("layout")); + break; + } + + printFoot(); + } + + /// Print a requirement as a child node. + void printRec(const RequirementRepr &requirement, Label label) { + printRecArbitrary([&](Label label) { + visitRequirementRepr(requirement, label); + }, label); + } + + /// Print a protocol typealias node. + void visitProtocolTypeAlias(const ProtocolTypeAlias &TA, Label label) { + printHead("protocol_typealias", ASTNodeColor, label); + + printNameRaw([&](raw_ostream &os) { + os << TA.getName(); + }, Label::optional("name")); + + PrintOptions opts; + opts.ProtocolQualifiedDependentMemberTypes = true; + printTypeField(TA.getUnderlyingType(), + Label::always("underlying_type"), opts); + + printFoot(); + } + + /// Print a protocol typealias as a child node. + void printRec(const ProtocolTypeAlias &TA, Label label) { + printRecArbitrary([&](Label label) { + visitProtocolTypeAlias(TA, label); + }, label); } + /// Print a captured value as a child node. + void printRec(const CapturedValue &value, Label label) { + printRecArbitrary([&](Label label) { + printHead("captured_value", CapturesColor, label); + printFlag(value.isDirect(), "is_direct"); + printFlag(value.isNoEscape(), "is_no_escape"); + printFlag(value.isLocalCapture(), "is_local_capture"); + printFlag(value.isDynamicSelfMetadata(), "is_dynamic_self_metadata"); + if (auto *D = value.getDecl()) { + printRec(D, Label::always("decl")); + } + if (auto *E = value.getExpr()) { + printRec(E, Label::always("expr")); + } + if (auto *OV = value.getOpaqueValue()) { + printRec(OV, Label::always("opaque_value")); + } + if (value.isPackElement()) { + if (auto *PE = value.getPackElement()) { + printRec(PE, Label::always("pack_element")); + } + if (auto PET = value.getPackElementType()) { + printRec(PET, Label::always("pack_element_type")); + } + } + printFoot(); + }, label); + } + + /// Print a field containing function capture info. + void printCaptureInfoField(const std::optional &captureInfo, + Label label) { + if (!captureInfo.has_value()) + return; + if (captureInfo->isTrivial()) + return; + + if (Writer.isParsable()) { + printList(captureInfo->getCaptures(), [&](auto capture, Label label) { + printRec(capture, label); + }, label); + } else { + printFieldRaw([&](raw_ostream &OS) { + captureInfo->print(OS); + }, label, CapturesColor); + } + } + + /// Print the requirements of a trailing where clause. + void printWhereClause(const TrailingWhereClause *Where, Label label) { + if (!Where) { + return; + } + if (Writer.isParsable()) { + printList(Where->getRequirements(), [&](auto req, Label label) { + printRec(req, label); + }, label); + } else { + printFieldQuotedRaw([&](raw_ostream &OS) { + Where->print(OS, /*printWhereKeyword*/ false); + }, label); + } + }; + /// Print a field with a short keyword-style value, printing the value by /// passing a closure that takes a \c raw_ostream. template - void printFieldRaw(Fn body, StringRef name, + void printFieldRaw(Fn body, Label label, TerminalColor color = FieldLabelColor) { - OS << " "; - if (!name.empty()) - PrintWithColorRAII(OS, color) << name << "="; - body(PrintWithColorRAII(OS, color).getOS()); + Writer.printFieldRaw([&](raw_ostream &OS) { body(OS); }, label, color); } /// Print a field with a short keyword-style value. The value will be /// formatted using a \c getDumpString() overload. template - void printField(const T &value, StringRef name, + void printField(const T &value, Label label, TerminalColor color = FieldLabelColor) { printFieldRaw([&](raw_ostream &OS) { OS << getDumpString(value); }, - name, color); + label, color); } /// Print a field with a long value that will be automatically quoted and /// escaped, printing the value by passing a closure that takes a /// \c raw_ostream. template - void printFieldQuotedRaw(Fn body, StringRef name, - TerminalColor color = FieldLabelColor) { - printFieldRaw([&](raw_ostream &OS) { - OS << quote; - { escaping_ostream escOS(OS); body(escOS); } - OS << quote; - }, name, color); + void printFieldQuotedRaw(Fn body, Label label, + TerminalColor color = FieldLabelColor) { + Writer.printFieldQuotedRaw(body, label, color); } /// Print a field with a long value that will be automatically quoted and /// escaped. template - void printFieldQuoted(const T &value, StringRef name, + void printFieldQuoted(const T &value, Label label, TerminalColor color = FieldLabelColor) { - printFieldQuotedRaw([&](raw_ostream &OS) { OS << value; }, name, color); + printFieldQuotedRaw([&](raw_ostream &OS) { OS << value; }, label, color); } /// Print a simple boolean value, printing the value by passing a closure /// that takes a \c raw_ostream. template void printFlagRaw(Fn body, TerminalColor color = FieldLabelColor) { - printFieldRaw(body, "", color); + Writer.printFlagRaw(body, color); } /// Print a simple boolean value unconditionally. void printFlag(StringRef name, TerminalColor color = FieldLabelColor) { - printFieldRaw([&](raw_ostream &OS) { OS << name; }, "", color); + printFlagRaw([&](raw_ostream &OS) { OS << name; }, color); } /// Print a simple boolean value. @@ -838,75 +1457,217 @@ namespace { printFlag(name, color); } + /// Prints any structure necessary to render a list of items, calling + /// the given function to produce the contents of the list. + void printListArbitrary(std::function body, Label label) { + Writer.printListArbitrary(body, label); + } + + /// Prints a list of values. + template + void printList(T &&list, F fn, Label label) { + if (list.begin() == list.end()) + return; + Writer.printListArbitrary([&]{ + for (const auto &elem : list) { + fn(elem, Label::optional("")); + } + }, label); + } + + /// Prints a list of strings in a compact form depending on the output + /// format, where the given function returns a string for each element in + /// the sequence. The default format will interleave these strings with + /// commas, whereas parsable outputs will render them as a structured list + /// of strings. + template + void printStringListField(T &&list, F fn, Label label, + StringRef delimiter = ",", + TerminalColor color = FieldLabelColor) { + if (list.begin() == list.end()) + return; + + if (Writer.isParsable()) { + Writer.printListArbitrary([&]{ + for (const auto &elem : list) { + printField(fn(elem), Label::optional("")); + } + }, label); + return; + } + + printFieldQuotedRaw([&](raw_ostream &OS) { + interleave(list, OS, [&](auto item) { + auto str = fn(item); + OS << (str.empty() ? "''" : str); + }, delimiter); + }, label, color); + } + /// Print a field containing a node's source location. void printSourceLoc(const SourceLoc L, const ASTContext *Ctx, - StringRef label = "location") { + Label label = Label::always("location")) { if (!L.isValid() || !Ctx) return; - - printFieldRaw([&](raw_ostream &OS) { - escaping_ostream escOS(OS); - L.print(escOS, Ctx->SourceMgr); - }, label, LocationColor); + Writer.printSourceLoc(L, Ctx, label); } /// Print a field containing a node's source range. - void printSourceRange(const SourceRange R, const ASTContext *Ctx) { + void printSourceRange(const SourceRange R, const ASTContext *Ctx, + Label label = Label::always("range")) { if (!R.isValid() || !Ctx) return; - - printFieldRaw([&](raw_ostream &OS) { - escaping_ostream escOS(OS); - R.print(escOS, Ctx->SourceMgr, /*PrintText=*/false); - }, "range", RangeColor); + Writer.printSourceRange(R, Ctx, label); } /// Print a field containing a node's name, printing the node's name by /// passing a closure that takes a \c raw_ostream. template - void printNameRaw(Fn body, bool leadingSpace = true) { - if (leadingSpace) - OS << ' '; - PrintWithColorRAII colored(OS, IdentifierColor); - OS << quote; - { - escaping_ostream escaping_os(OS); - body(escaping_os); + void printNameRaw(Fn body, Label label) { + if (label.empty()) { + // If we were given an empty name, make sure we have a suitable default + // fallback for parsable output formats. + label = Label::optional("name"); } - OS << quote; + printFieldQuotedRaw([&](raw_ostream &OS) { body(OS); }, label, + IdentifierColor); } /// Print a field containing a node's name. - void printName(DeclName name, bool leadingSpace = true) { + void printName(DeclName name, Label label) { + if (Writer.isParsable()) { + if (label.empty()) { + // If we were given an empty name, make sure we have a suitable + // default fallback for parsable output formats. + label = Label::optional("name"); + } + printRecArbitrary([&](Label label) { + printHead("decl_name", IdentifierColor, label); + printDeclBaseName(name.getBaseName(), Label::optional("base_name")); + printList(name.getArgumentNames(), [&](auto arg, Label label) { + printField(arg.str(), label); + }, Label::optional("args")); + printFoot(); + }, label); + return; + } printNameRaw([&](raw_ostream &OS) { ::printName(OS, name); - }, leadingSpace); + }, label); + } + + /// Prints a structured representation of a `DeclBaseName`, accounting for + /// special names. + void printDeclBaseName(DeclBaseName Basename, Label label) { + printRecArbitrary([&](Label label) { + printHead("base_name", IdentifierColor, label); + switch (Basename.getKind()) { + case DeclBaseName::Kind::Constructor: + printField((StringRef)"init", Label::optional("special")); + break; + case DeclBaseName::Kind::Destructor: + printField((StringRef)"deinit", Label::optional("special")); + break; + case DeclBaseName::Kind::Subscript: + printField((StringRef)"subscript", Label::optional("special")); + break; + case DeclBaseName::Kind::Normal: + printField(Basename.getIdentifier(), Label::optional("name")); + printFlag(Basename.isOperator(), "is_operator"); + } + printFoot(); + }, label); } /// Print an unnamed field containing a node's name, read from a declaration. - void printDeclName(const ValueDecl *D, bool leadingSpace = true) { - if (D->getName()) { - printName(D->getName(), leadingSpace); + void printDeclName(const Decl *D, Label label) { + auto VD = dyn_cast(D); + if (VD && VD->getName()) { + printName(VD->getName(), label); + } else if (!Writer.isParsable()) { + // We don't print a name field for anonymous decls in parsable outputs. + printFieldRaw([&](raw_ostream &OS) { + OS << "'; + }, label, IdentifierColor); + } + } + + /// Prints a field containing the name or the USR (based on parsability of + /// the output) of a decl that is being referenced elsewhere. + template + void printReferencedDeclField(const T *D, Label label) { + if (Writer.isParsable()) { + printFieldQuoted(declUSR(D), label); } else { - if (leadingSpace) - OS << ' '; - PrintWithColorRAII(OS, IdentifierColor) - << "'; + printFieldQuoted(D->getName(), label); } } - /// Print a field containing a node's name, read from a declaration. - void printDeclNameField(const ValueDecl *D, StringRef name) { - printFieldRaw([&](raw_ostream &os) { - printDeclName(D, /*leadingSpace=*/false); - }, name); + /// Prints a field containing the name or the USR (based on parsability of + /// the output) of a decl that is being referenced elsewhere. + template + void printReferencedDeclWithContextField(const T *D, Label label, + TerminalColor Color = DeclColor) { + if (Writer.isParsable()) { + printFieldQuoted(declUSR(D), label, Color); + } else { + printFieldQuoted(D->printRef(), label, Color); + } } /// Print a field containing a concrete reference to a declaration. - void printDeclRefField(ConcreteDeclRef declRef, StringRef label, + void printDeclRefField(ConcreteDeclRef declRef, Label label, TerminalColor Color = DeclColor) { - printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label, - Color); + if (Writer.isParsable()) { + // Just omit the key/value for parsable formats if there's no decl. + if (!declRef.getDecl()) + return; + + // For parsable outputs, print much more detailed structured information + // about the declref instead of just a human-readable string. + printRecArbitrary([&](Label label) { + printHead("decl_ref", Color, label); + if (auto D = declRef.getDecl()) { + printFieldQuoted(D->getBaseName(), Label::always("base_name"), + IdentifierColor); + printFieldQuoted(declUSR(D), Label::always("decl_usr"), + FieldLabelColor); + printFieldQuoted(declTypeUSR(D), Label::always("type_usr"), + TypeColor); + } + if (!declRef.getSubstitutions().empty()) { + printRec(declRef.getSubstitutions(), Label::always("substitutions")); + } + printFoot(); + }, label); + } else { + printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label, + Color); + } + } + + /// Prints a type as a field. If writing a parsable output format, the + /// `PrintOptions` are ignored and the type is written as a USR; otherwise, + /// the type is stringified using the `PrintOptions`. + void printTypeField(Type Ty, Label label, PrintOptions opts = PrintOptions(), + TerminalColor Color = TypeColor) { + if (Writer.isParsable()) { + printField(typeUSR(Ty), label, Color); + } else { + printFieldQuotedRaw([&](raw_ostream &out) { Ty.print(out, opts); }, + label, Color); + } + } + + /// Prints a `Type` if it is present, falling back to the `TypeRepr` + /// otherwise (and lastly, doing nothing if the `TypeRepr` was also null). + void printTypeOrTypeRepr(std::optional Ty, TypeRepr *Repr, + Label label) { + if (Ty.has_value()) { + printTypeField(*Ty, label); + } else if (Repr) { + printRec(Repr, label); + } } void printThrowDest(ThrownErrorDestination throws, bool wantNothrow) { @@ -919,11 +1680,26 @@ namespace { auto thrownError = throws.getThrownErrorType(); auto contextError = throws.getContextErrorType(); + if (Writer.isParsable()) { + // For parsable outputs, just print the full thrown and contextual error + // information as a nice structured object, even if they're the same. + printRecArbitrary( + [&](Label label) { + printHead("thrown_error_destination", IdentifierColor, label); + printTypeField(thrownError, Label::always("thrown_type")); + printTypeField(contextError, Label::always("context_type")); + printFoot(); + }, + Label::always("throws")); + return; + } + if (thrownError->isEqual(contextError)) { - // No translation of the thrown error type is required, so ony print + // No translation of the thrown error type is required, so only print // the thrown error type. Type errorExistentialType = contextError->getASTContext().getErrorExistentialType(); + if (errorExistentialType && thrownError->isEqual(errorExistentialType)) printFlag("throws", ExprModifierColor); else { @@ -937,131 +1713,136 @@ namespace { } }; - class PrintPattern : public PatternVisitor, + class PrintPattern : public PatternVisitor, public PrintBase { public: using PrintBase::PrintBase; - void printCommon(Pattern *P, const char *Name, StringRef Label) { - printHead(Name, PatternColor, Label); + void printCommon(Pattern *P, const char *Name, Label label) { + printHead(Name, PatternColor, label); printFlag(P->isImplicit(), "implicit", ExprModifierColor); if (P->hasType()) { - printFieldQuoted(P->getType(), "type", TypeColor); + printTypeField(P->getType(), Label::always("type")); } } - void visitParenPattern(ParenPattern *P, StringRef label) { + void visitParenPattern(ParenPattern *P, Label label) { printCommon(P, "pattern_paren", label); - printRec(P->getSubPattern()); + printRec(P->getSubPattern(), Label::optional("sub_pattern")); printFoot(); } - void visitTuplePattern(TuplePattern *P, StringRef label) { + void visitTuplePattern(TuplePattern *P, Label label) { printCommon(P, "pattern_tuple", label); - printFieldQuotedRaw([&](raw_ostream &OS) { - interleave(P->getElements(), OS, - [&](const TuplePatternElt &elt) { - auto name = elt.getLabel(); - OS << (name.empty() ? "''" : name.str()); - }, ","); - }, "names"); - - for (auto &elt : P->getElements()) { - printRec(elt.getPattern()); - } + printStringListField(P->getElements(), [&](const TuplePatternElt &elt) { + return elt.getLabel().str(); + }, Label::always("names")); + printList(P->getElements(), [&](auto &elt, Label label) { + printRec(elt.getPattern(), label); + }, Label::optional("elements")); printFoot(); } - void visitNamedPattern(NamedPattern *P, StringRef label) { + void visitNamedPattern(NamedPattern *P, Label label) { printCommon(P, "pattern_named", label); - printDeclName(P->getDecl()); + printDeclName(P->getDecl(), Label::optional("name")); printFoot(); } - void visitAnyPattern(AnyPattern *P, StringRef label) { + void visitAnyPattern(AnyPattern *P, Label label) { if (P->isAsyncLet()) { printCommon(P, "async_let ", label); } printCommon(P, "pattern_any", label); printFoot(); } - void visitTypedPattern(TypedPattern *P, StringRef label) { + void visitTypedPattern(TypedPattern *P, Label label) { printCommon(P, "pattern_typed", label); - printRec(P->getSubPattern()); - if (auto *repr = P->getTypeRepr()) { - printRec(repr); + printRec(P->getSubPattern(), Label::optional("sub_pattern")); + if (!Writer.isParsable()) { + // In general, we don't want the TypeRepr for parsable outputs. They + // reflect syntactic rather than semantic information, the same + // information can be obtained through a syntax parse, and we print the + // type USR instead. + if (auto *repr = P->getTypeRepr()) { + printRec(repr, Label::optional("type_repr")); + } } printFoot(); } - void visitIsPattern(IsPattern *P, StringRef label) { + void visitIsPattern(IsPattern *P, Label label) { printCommon(P, "pattern_is", label); - printField(P->getCastKind(), "cast_kind"); - printFieldQuoted(P->getCastType(), "cast_to", TypeColor); + printField(P->getCastKind(), Label::always("cast_kind")); + printTypeField(P->getCastType(), Label::always("cast_to")); if (auto sub = P->getSubPattern()) { - printRec(sub); + printRec(sub, Label::optional("sub_pattern")); } printFoot(); } - void visitExprPattern(ExprPattern *P, StringRef label) { + void visitExprPattern(ExprPattern *P, Label label) { printCommon(P, "pattern_expr", label); switch (P->getCachedMatchOperandOwnership()) { case ValueOwnership::Default: break; case ValueOwnership::Shared: printFieldRaw([](llvm::raw_ostream &os) { os << "borrowing"; }, - "ownership"); + Label::always("ownership")); break; case ValueOwnership::InOut: printFieldRaw([](llvm::raw_ostream &os) { os << "mutating"; }, - "ownership"); + Label::always("ownership")); break; case ValueOwnership::Owned: printFieldRaw([](llvm::raw_ostream &os) { os << "consuming"; }, - "ownership"); + Label::always("ownership")); break; } if (auto m = P->getCachedMatchExpr()) - printRec(m); + printRec(m, Label::optional("match_expr")); else - printRec(P->getSubExpr()); + printRec(P->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitBindingPattern(BindingPattern *P, StringRef label) { + void visitBindingPattern(BindingPattern *P, Label label) { printCommon(P, "pattern_binding", label); - printField(P->getIntroducerStringRef(), "kind"); - printRec(P->getSubPattern()); + printField(P->getIntroducerStringRef(), Label::always("kind")); + printRec(P->getSubPattern(), Label::optional("sub_pattern")); printFoot(); } - void visitEnumElementPattern(EnumElementPattern *P, StringRef label) { + void visitEnumElementPattern(EnumElementPattern *P, Label label) { printCommon(P, "pattern_enum_element", label); - printFieldQuotedRaw([&](raw_ostream &OS) { - P->getParentType().print(PrintWithColorRAII(OS, TypeColor).getOS()); - OS << '.'; - PrintWithColorRAII(OS, IdentifierColor) << P->getName(); - }, "element"); + if (Writer.isParsable()) { + printName(P->getName().getFullName(), Label::always("element")); + } else { + printFieldQuotedRaw([&](raw_ostream &OS) { + P->getParentType().print(PrintWithColorRAII(OS, TypeColor).getOS()); + OS << '.'; + PrintWithColorRAII(OS, IdentifierColor) << P->getName(); + }, Label::always("element")); + } if (P->hasSubPattern()) { - printRec(P->getSubPattern()); + printRec(P->getSubPattern(), Label::optional("sub_pattern")); } printFoot(); } - void visitOptionalSomePattern(OptionalSomePattern *P, StringRef label) { + void visitOptionalSomePattern(OptionalSomePattern *P, Label label) { printCommon(P, "pattern_optional_some", label); - printRec(P->getSubPattern()); + printRec(P->getSubPattern(), Label::optional("sub_pattern")); printFoot(); } - void visitBoolPattern(BoolPattern *P, StringRef label) { + void visitBoolPattern(BoolPattern *P, Label label) { printCommon(P, "pattern_bool", label); - printField(P->getValue(), "value"); + printField(P->getValue(), Label::always("value")); printFoot(); } }; /// PrintDecl - Visitor implementation of Decl::print. - class PrintDecl : public DeclVisitor, + class PrintDecl : public DeclVisitor, public PrintBase { public: using PrintBase::PrintBase; @@ -1070,25 +1851,28 @@ namespace { void printWhereRequirements( PointerUnion Owner ) { - const auto printWhere = [&](const TrailingWhereClause *Where) { - if (Where) { - printFieldQuotedRaw([&](raw_ostream &OS) { - Where->print(OS, /*printWhereKeyword*/ false); - }, "where_requirements"); - } - }; - if (const auto GC = Owner.dyn_cast()) { - printWhere(GC->getTrailingWhereClause()); + printWhereClause(GC->getTrailingWhereClause(), + Label::always("where_requirements")); } else { const auto ATD = Owner.get(); - printWhere(ATD->getTrailingWhereClause()); + printWhereClause(ATD->getTrailingWhereClause(), + Label::always("where_requirements")); } } - void printCommon(Decl *D, const char *Name, StringRef Label, + void printCommon(Decl *D, const char *Name, Label label, TerminalColor Color = DeclColor) { - printHead(Name, Color, Label); + printHead(Name, Color, label); + + // Parsable outputs include the USR for each decl since they can be used + // to cross-reference them (within the AST dump itself and with other data + // sources like indexstore and SourceKit). + if (Writer.isParsable()) { + if (auto usr = declUSR(D); !usr.empty()) { + printFieldQuoted(usr, Label::always("usr")); + } + } printFlag(D->isImplicit(), "implicit", DeclModifierColor); printFlag(D->isHoisted(), "hoisted", DeclModifierColor); @@ -1099,7 +1883,7 @@ namespace { if (implAttr->CategoryName.empty()) printFlag(label); else - printFieldQuoted(implAttr->CategoryName.str(), label); + printFieldQuoted(implAttr->CategoryName.str(), Label::always(label)); } printSourceRange(D->getSourceRange(), &D->getASTContext()); @@ -1108,51 +1892,76 @@ namespace { } void printInherited(InheritedTypes Inherited) { - if (Inherited.empty()) + printStringListField(Inherited.getEntries(), [&](InheritedEntry Super) { + if (Writer.isParsable()) { + return typeUSR(Super.getType()); + } else { + std::string value; + llvm::raw_string_ostream SOS(value); + Super.getType().print(SOS); + return value; + } + }, Label::always("inherits"), /*delimiter=*/ ", "); + } + + void printImportPath(ImportDecl *ID, Label label) { + // We use getRealModulePath/getRealImportPath to handle module aliasing + // for the given imported module; for example, if + // '-module-alias Foo=Bar' was passed and this module has 'import Foo', + // its corresponding real module name 'Bar' is printed. + + if (Writer.isParsable()) { + // For parsable formats, write the module path and the access path as + // distinct lists. + ImportPath::Builder scratch; + auto modulePath = ID->getRealModulePath(scratch); + printList(modulePath, [&](auto component, Label label) { + printField(component.Item.str(), label); + }, Label::always("module_path")); + + auto accessPath = ID->getAccessPath(); + printList(accessPath, [&](auto component, Label label) { + printField(component.Item.str(), label); + }, Label::always("access_path")); return; + } + printFieldQuotedRaw([&](raw_ostream &OS) { - interleave(Inherited.getEntries(), OS, - [&](InheritedEntry Super) { Super.getType().print(OS); }, - ", "); - }, "inherits"); + ImportPath::Builder scratch; + ID->getRealImportPath(scratch).print(OS); + }, label, IdentifierColor); } public: - void visitImportDecl(ImportDecl *ID, StringRef label) { + void visitImportDecl(ImportDecl *ID, Label label) { printCommon(ID, "import_decl", label); printFlag(ID->isExported(), "exported"); if (ID->getImportKind() != ImportKind::Module) - printField(ID->getImportKind(), "kind"); - - printFieldQuotedRaw([&](raw_ostream &OS) { - // Check if module aliasing was used for the given imported module; for - // example, if '-module-alias Foo=Bar' was passed and this module has - // 'import Foo', its corresponding real module name 'Bar' should be printed. - ImportPath::Builder scratch; - ID->getRealImportPath(scratch).print(OS); - }, "module", IdentifierColor); + printField(ID->getImportKind(), Label::always("kind")); + printImportPath(ID, Label::always("module")); printFoot(); } - void visitExtensionDecl(ExtensionDecl *ED, StringRef label) { + void visitExtensionDecl(ExtensionDecl *ED, Label label) { printCommon(ED, "extension_decl", label, ExtensionColor); printFlag(!ED->hasBeenBound(), "unbound"); - printNameRaw([&](raw_ostream &OS) { - if (ED->hasBeenBound()) - ED->getExtendedType().print(OS); - else + if (ED->hasBeenBound()) { + printTypeField(ED->getExtendedType(), Label::optional("extended_type")); + } else { + printNameRaw([&](raw_ostream &OS) { ED->getExtendedTypeRepr()->print(OS); - }); + }, Label::optional("extended_type")); + } printCommonPost(ED); } - void visitTypeAliasDecl(TypeAliasDecl *TAD, StringRef label) { + void visitTypeAliasDecl(TypeAliasDecl *TAD, Label label) { printCommon(TAD, "typealias", label); if (auto underlying = TAD->getCachedUnderlyingType()) { - printFieldQuoted(underlying, "type", TypeColor); + printTypeField(underlying, Label::always("type")); } else { printFlag("unresolved_type", TypeColor); } @@ -1161,77 +1970,101 @@ namespace { printFoot(); } - void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD, StringRef label) { + void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD, Label label) { printCommon(OTD, "opaque_type", label); - printDeclNameField(OTD->getNamingDecl(), "naming_decl"); - printFieldQuotedRaw([&](raw_ostream &OS) { - OS << OTD->getDeclaredInterfaceType() << " in " - << OTD->getOpaqueInterfaceGenericSignature()->getAsString(); + printDeclName(OTD->getNamingDecl(), Label::always("naming_decl")); - }, "opaque_interface", TypeColor); + if (Writer.isParsable()) { + printTypeField(OTD->getDeclaredInterfaceType(), + Label::always("declared_interface_type")); - if (auto underlyingSubs = OTD->getUniqueUnderlyingTypeSubstitutions()) { - printRec(*underlyingSubs); + auto genericSig = OTD->getOpaqueInterfaceGenericSignature(); + printList(genericSig.getGenericParams(), [&](auto GP, Label label) { + printTypeField(GP, label); + }, Label::always("generic_params")); + printList(genericSig.getRequirements(), [&](const auto &req, Label label) { + printRec(req, label); + }, Label::optional("reqs")); + } else { + printFieldQuotedRaw([&](raw_ostream &OS) { + OS << OTD->getDeclaredInterfaceType() << " in " + << OTD->getOpaqueInterfaceGenericSignature()->getAsString(); + }, Label::always("opaque_interface"), TypeColor); } + printAttributes(OTD); printFoot(); } - void visitGenericTypeParamDecl(GenericTypeParamDecl *decl, StringRef label) { + void visitGenericTypeParamDecl(GenericTypeParamDecl *decl, Label label) { printCommon(decl, "generic_type_param", label); - printField(decl->getDepth(), "depth"); - printField(decl->getIndex(), "index"); + printField(decl->getDepth(), Label::always("depth")); + printField(decl->getIndex(), Label::always("index")); switch (decl->getParamKind()) { case GenericTypeParamKind::Type: - printField((StringRef)"type", "param_kind"); + printField((StringRef)"type", Label::always("param_kind")); break; case GenericTypeParamKind::Pack: - printField((StringRef)"pack", "param_kind"); + printField((StringRef)"pack", Label::always("param_kind")); break; case GenericTypeParamKind::Value: - printField((StringRef)"value", "param_kind"); + printField((StringRef)"value", Label::always("param_kind")); break; } printFoot(); } - void visitAssociatedTypeDecl(AssociatedTypeDecl *decl, StringRef label) { + void visitAssociatedTypeDecl(AssociatedTypeDecl *decl, Label label) { printCommon(decl, "associated_type_decl", label); - StringRef fieldName("default"); + Label fieldName = Label::always("default"); if (auto defaultDef = decl->getCachedDefaultDefinitionType()) { - printFieldQuoted(*defaultDef, fieldName); + printTypeField(*defaultDef, fieldName); } else { printField("", fieldName); } printWhereRequirements(decl); if (decl->overriddenDeclsComputed()) { - printFieldQuotedRaw([&](raw_ostream &OS) { - interleave(decl->getOverriddenDecls(), OS, - [&](AssociatedTypeDecl *overridden) { - OS << overridden->getProtocol()->getName(); - }, ", "); - }, "overridden"); + printStringListField(decl->getOverriddenDecls(), + [&](AssociatedTypeDecl *overridden) { + if (Writer.isParsable()) { + return declUSR(overridden->getProtocol()); + } + return std::string(overridden->getProtocol()->getName().str()); + }, Label::always("overridden"), /*delimiter=*/ ", "); } printFoot(); } - void visitProtocolDecl(ProtocolDecl *PD, StringRef label) { + void visitProtocolDecl(ProtocolDecl *PD, Label label) { printCommon(PD, "protocol", label); if (PD->isRequirementSignatureComputed()) { auto reqSig = PD->getRequirementSignature(); - std::string reqSigStr; - llvm::raw_string_ostream out(reqSigStr); - reqSig.print(PD, out); + if (Writer.isParsable()) { + printRecArbitrary([&](Label label) { + printHead("requirement_signature", ASTNodeColor, label); + printList(reqSig.getRequirements(), [&](auto req, Label label) { + printRec(req, label); + }, Label::always("requirements")); + printList(reqSig.getTypeAliases(), [&](auto typeAlias, Label label) { + printRec(typeAlias, label); + }, Label::always("typealiases")); + printFoot(); + }, Label::always("requirement_signature")); + } else { + std::string reqSigStr; + llvm::raw_string_ostream out(reqSigStr); + reqSig.print(PD, out); - printFieldQuoted(out.str(), "requirement_signature"); + printFieldQuoted(out.str(), Label::always("requirement_signature")); + } } else { printFlag("uncomputed_requirement_signature"); } @@ -1239,46 +2072,87 @@ namespace { printCommonPost(PD); } - void printGenericParameters(GenericParamList *Params) { + void printGenericSignature(const GenericSignature &Sig, Label label) { + if (!Sig) + return; + + printRecArbitrary([&](Label label) { + printHead("generic_signature", ASTNodeColor, label); + printList(Sig.getGenericParams(), [&](auto GP, Label label) { + printTypeField(GP, label); + }, Label::always("generic_params")); + printGenericRequirements(Sig.getRequirements()); + printFoot(); + }, label); + } + + void printParsedGenericParams(GenericParamList *Params) { if (!Params) return; - printFieldQuotedRaw([&](raw_ostream &OS) { - Params->print(OS); - }, "", TypeColor); + if (Writer.isParsable()) { + printList(*Params, [&](auto GP, Label label) { + printRec(const_cast(GP), label); + }, Label::optional("generic_params")); + } else { + printFieldQuotedRaw([&](raw_ostream &OS) { + Params->print(OS); + }, Label::optional("generic_params"), TypeColor); + } + } + + void printGenericRequirements(ArrayRef Reqs) { + if (Reqs.empty() || !Writer.isParsable()) + return; + + printList(Reqs, [&](auto Req, Label label) { + printRec(Req, label); + }, Label::optional("generic_reqs")); } - void printCommon(ValueDecl *VD, const char *Name, StringRef Label, + void printAttributes(const Decl *D) { + ASTContext *Ctx = &D->getASTContext(); + DeclContext *DC = D->getDeclContext(); + printList(D->getAttrs(), [&](auto *attr, Label label) { + printRec(attr, Ctx, DC, label); + }, Label::optional("attrs")); + } + + void printCommon(ValueDecl *VD, const char *Name, Label label, TerminalColor Color = DeclColor) { - printCommon((Decl*)VD, Name, Label, Color); + printCommon((Decl*)VD, Name, label, Color); - printDeclName(VD); - if (auto *AFD = dyn_cast(VD)) - printGenericParameters(AFD->getParsedGenericParams()); - if (auto *GTD = dyn_cast(VD)) - printGenericParameters(GTD->getParsedGenericParams()); - if (auto *MD = dyn_cast(VD)) - printGenericParameters(MD->getParsedGenericParams()); + printDeclName(VD, Label::optional("name")); + if (auto *GC = VD->getAsGenericContext()) { + if (Writer.isParsable() && GC->hasComputedGenericSignature()) { + printGenericSignature(GC->getGenericSignature(), + Label::optional("generic_signature")); + } else { + printParsedGenericParams(GC->getParsedGenericParams()); + } + } if (VD->hasInterfaceType()) { - printFieldQuoted(VD->getInterfaceType(), "interface type", - InterfaceTypeColor); + printTypeField(VD->getInterfaceType(), Label::always("interface type"), + PrintOptions(), InterfaceTypeColor); } if (VD->hasAccess()) { - printField(VD->getFormalAccess(), "access", AccessLevelColor); + printField(VD->getFormalAccess(), Label::always("access"), + AccessLevelColor); } if (VD->overriddenDeclsComputed()) { auto overridden = VD->getOverriddenDecls(); - if (!overridden.empty()) { - printFieldQuotedRaw([&](raw_ostream &OS) { - interleave(overridden, OS, - [&](ValueDecl *overridden) { - overridden->dumpRef(OS); - }, ", "); - }, "override", OverrideColor); - } + printStringListField(overridden, [&](ValueDecl *overridden) { + if (Writer.isParsable()) { + return declUSR(overridden); + } + std::string value; + llvm::raw_string_ostream SOS(value); + overridden->dumpRef(SOS); + return value; + }, Label::always("override"), /*delimiter=*/ ", ", OverrideColor); } auto VarD = dyn_cast(VD); @@ -1287,21 +2161,25 @@ namespace { "final"); printFlag(attrs.hasAttribute(), "@objc"); printFlag(attrs.hasAttribute(), "dynamic"); - if (auto *attr = attrs.getAttribute()) { - printFlagRaw([&](raw_ostream &OS) { - OS << "@_dynamicReplacement(for: \""; - OS << attr->getReplacedFunctionName(); - OS << "\")"; - }); + if (!Writer.isParsable()) { + // This format isn't suitable for parsable formats, and we already print + // the full attribute as its own record so we can omit this. + if (auto *attr = attrs.getAttribute()) { + printFlagRaw([&](raw_ostream &OS) { + OS << "@_dynamicReplacement(for: \""; + OS << attr->getReplacedFunctionName(); + OS << "\")"; + }); + } } auto lifetimeString = getDumpString(VD->getLifetimeAnnotation()); if (!lifetimeString.empty()) printFlag(lifetimeString); } - void printCommon(NominalTypeDecl *NTD, const char *Name, StringRef Label, + void printCommon(NominalTypeDecl *NTD, const char *Name, Label label, TerminalColor Color = DeclColor) { - printCommon((ValueDecl *)NTD, Name, Label, Color); + printCommon((ValueDecl *)NTD, Name, label, Color); if (NTD->hasInterfaceType()) printFlag(NTD->isResilient() ? "resilient" : "non_resilient"); @@ -1322,41 +2200,69 @@ namespace { break; } - auto members = ParseIfNeeded ? IDC->getMembers() - : IDC->getCurrentMembersWithoutLoading(); - for (Decl *D : members) { - printRec(D); + printAttributes(IDC->getDecl()); + + if (Writer.isParsable()) { + // Parsable outputs are meant to be used for semantic analysis, so we + // want the full list of members, including macro-generated ones. + printList(IDC->getAllMembers(), [&](Decl *D, Label label) { + printRec(D, label); + }, Label::optional("members")); + } else { + auto members = ParseIfNeeded ? IDC->getMembers() + : IDC->getCurrentMembersWithoutLoading(); + printList(members, [&](Decl *D, Label label) { + printRec(D, label); + }, Label::optional("members")); } printFoot(); } void visitSourceFile(const SourceFile &SF) { - printHead("source_file", ASTNodeColor); + Writer.setMainBufferID(SF.getBufferID()); + + printHead("source_file", ASTNodeColor, Label::optional("")); printNameRaw([&](raw_ostream &OS) { OS << SF.getFilename(); - }); + }, Label::optional("filename")); + + if (Writer.isParsable()) { + // Print additional information about the compiler for parsable formats, + // so that they can use it to determine their compatibility with the + // output. + printRecArbitrary([&](Label label) { + auto version = version::getSwiftNumericVersion(); + printHead("compiler_version", FieldLabelColor, label); + printField(version.first, Label::always("major")); + printField(version.second, Label::always("minor")); + printFieldQuoted(version::getSwiftFullVersion( + version::Version::getCurrentLanguageVersion()), + Label::always("full")); + printFoot(); + }, Label::always("compiler_version")); + } auto items = ParseIfNeeded ? SF.getTopLevelItems() : SF.getCachedTopLevelItems(); if (items) { - for (auto item : *items) { + printList(*items, [&](ASTNode item, Label label) { if (item.isImplicit()) - continue; + return; if (auto decl = item.dyn_cast()) { - printRec(decl); + printRec(decl, label); } else if (auto stmt = item.dyn_cast()) { - printRec(stmt, &SF.getASTContext()); + printRec(stmt, &SF.getASTContext(), label); } else { auto expr = item.get(); - printRec(expr); + printRec(expr, label); } - } + }, Label::optional("items")); } printFoot(); } - void visitVarDecl(VarDecl *VD, StringRef label) { + void visitVarDecl(VarDecl *VD, Label label) { printCommon(VD, "var_decl", label); printFlag(VD->isDistributed(), "distributed", DeclModifierColor); @@ -1381,36 +2287,40 @@ namespace { } void printStorageImpl(AbstractStorageDecl *D) { - printFlag(D->isStatic(), "type", DeclModifierColor); + printFlag(D->isStatic(), "static", DeclModifierColor); if (D->hasInterfaceType()) { auto impl = D->getImplInfo(); - printField(impl.getReadImpl(), "readImpl", DeclModifierColor); + printField(impl.getReadImpl(), Label::always("readImpl"), + DeclModifierColor); if (!impl.supportsMutation()) { printFlag("immutable", DeclModifierColor); } else { - printField(impl.getWriteImpl(), "writeImpl", DeclModifierColor); - printField(impl.getReadWriteImpl(), "readWriteImpl", + printField(impl.getWriteImpl(), Label::always("writeImpl"), + DeclModifierColor); + printField(impl.getReadWriteImpl(), Label::always("readWriteImpl"), DeclModifierColor); } } } void printAccessors(AbstractStorageDecl *D) { - for (auto accessor : D->getAllAccessors()) { - printRec(accessor); - } + printList(D->getAllAccessors(), [&](auto accessor, Label label) { + printRec(accessor, label); + }, Label::optional("accessors")); } - void visitParamDecl(ParamDecl *PD, StringRef label) { + void visitParamDecl(ParamDecl *PD, Label label) { printHead("parameter", ParameterColor, label); - printDeclName(PD); + printDeclName(PD, Label::optional("name")); if (!PD->getArgumentName().empty()) - printFieldQuoted(PD->getArgumentName(), "apiName", IdentifierColor); + printFieldQuoted(PD->getArgumentName(), Label::always("apiName"), + IdentifierColor); if (PD->hasInterfaceType()) { - printFieldQuoted(PD->getInterfaceType(), "interface type", - InterfaceTypeColor); + printTypeField(PD->getInterfaceType(), + Label::always("interface type"), PrintOptions(), + InterfaceTypeColor); } if (auto specifier = PD->getCachedSpecifier()) { @@ -1432,57 +2342,54 @@ namespace { printFlag(PD->isNoImplicitCopy(), "noImplicitCopy"); if (PD->getDefaultArgumentKind() != DefaultArgumentKind::None) { - printField(PD->getDefaultArgumentKind(), "default_arg"); + printField(PD->getDefaultArgumentKind(), Label::always("default_arg")); } - if (PD->hasDefaultExpr() && - PD->getCachedDefaultArgumentCaptureInfo() && - !PD->getCachedDefaultArgumentCaptureInfo()->isTrivial()) { - printFieldRaw([&](raw_ostream &OS) { - PD->getCachedDefaultArgumentCaptureInfo()->print(OS); - }, "", CapturesColor); + if (PD->hasDefaultExpr()) { + printCaptureInfoField(PD->getCachedDefaultArgumentCaptureInfo(), + Label::optional("default_arg_capture_info")); } printFlag(PD->getAttrs().hasAttribute(), "known_to_be_local", DeclModifierColor); if (auto init = PD->getStructuralDefaultExpr()) { - printRec(init, "expression"); + printRec(init, Label::always("expression")); } printFoot(); } - void visitParameterList(ParameterList *PL, StringRef label) { - PrintBase::visitParameterList(PL, /*ctx=*/nullptr, label); + void visitParameterList(ParameterList *PL, Label label) { + PrintBase::visitParameterList(PL, label, /*ctx=*/nullptr); } - void visitEnumCaseDecl(EnumCaseDecl *ECD, StringRef label) { + void visitEnumCaseDecl(EnumCaseDecl *ECD, Label label) { printCommon(ECD, "enum_case_decl", label); - for (EnumElementDecl *D : ECD->getElements()) { - printRec(D); - } + printList(ECD->getElements(), [&](EnumElementDecl *D, Label label) { + printRec(D, label); + }, Label::optional("elements")); printFoot(); } - void visitEnumDecl(EnumDecl *ED, StringRef label) { + void visitEnumDecl(EnumDecl *ED, Label label) { printCommon(ED, "enum_decl", label); printCommonPost(ED); } - void visitEnumElementDecl(EnumElementDecl *EED, StringRef label) { + void visitEnumElementDecl(EnumElementDecl *EED, Label label) { printCommon(EED, "enum_element_decl", label); if (auto *paramList = EED->getParameterList()) { - printRec(paramList); + printRec(paramList, Label::optional("params")); } printFoot(); } - void visitStructDecl(StructDecl *SD, StringRef label) { + void visitStructDecl(StructDecl *SD, Label label) { printCommon(SD, "struct_decl", label); printCommonPost(SD); } - void visitClassDecl(ClassDecl *CD, StringRef label) { + void visitClassDecl(ClassDecl *CD, Label label) { printCommon(CD, "class_decl", label); printFlag(CD->isExplicitDistributedActor(), "distributed"); @@ -1493,41 +2400,45 @@ namespace { printCommonPost(CD); } - void visitBuiltinTupleDecl(BuiltinTupleDecl *BTD, StringRef label) { + void visitBuiltinTupleDecl(BuiltinTupleDecl *BTD, Label label) { printCommon(BTD, "builtin_tuple_decl", label); printCommonPost(BTD); } - void visitPatternBindingDecl(PatternBindingDecl *PBD, StringRef label) { + void visitPatternBindingDecl(PatternBindingDecl *PBD, Label label) { printCommon(PBD, "pattern_binding_decl", label); - for (auto idx : range(PBD->getNumPatternEntries())) { - printRec(PBD->getPattern(idx)); + printList(range(PBD->getNumPatternEntries()), [&](auto idx, Label label) { + // Ensure that we have an object structure printed in parsable modes + // so that the children aren't directly rendered as array elements. + if (Writer.isParsable()) + printHead("pattern_entry", FieldLabelColor, label); + + printRec(PBD->getPattern(idx), Label::optional("pattern")); if (PBD->getOriginalInit(idx)) { - printRec(PBD->getOriginalInit(idx), "original_init"); + printRec(PBD->getOriginalInit(idx), Label::always("original_init")); } if (PBD->getInit(idx)) { - printRec(PBD->getInit(idx), "processed_init"); + printRec(PBD->getInit(idx), Label::always("processed_init")); } - } + + if (Writer.isParsable()) + printFoot(); + }, Label::optional("pattern_entries")); printFoot(); } - void visitSubscriptDecl(SubscriptDecl *SD, StringRef label) { + void visitSubscriptDecl(SubscriptDecl *SD, Label label) { printCommon(SD, "subscript_decl", label); printStorageImpl(SD); printAccessors(SD); printFoot(); } - void printCommonAFD(AbstractFunctionDecl *D, const char *Type, StringRef Label) { - printCommon(D, Type, Label, FuncColor); + void printCommonAFD(AbstractFunctionDecl *D, const char *Type, Label label) { + printCommon(D, Type, label, FuncColor); if (auto captureInfo = D->getCachedCaptureInfo()) { - if (!captureInfo->isTrivial()) { - printFlagRaw([&](raw_ostream &OS) { - captureInfo->print(OS); - }); - } + printCaptureInfoField(captureInfo, Label::optional("captures")); } if (auto *attr = D->getAttrs().getAttribute()) { @@ -1540,40 +2451,41 @@ namespace { void printAbstractFunctionDecl(AbstractFunctionDecl *D) { if (auto *P = D->getImplicitSelfDecl()) { - printRec(P); + printRec(P, Label::optional("implicit_self_decl")); } - printRec(D->getParameters(), &D->getASTContext()); - if (auto FD = dyn_cast(D)) { - if (FD->getResultTypeRepr()) { - printRec(FD->getResultTypeRepr(), "result"); - if (auto opaque = FD->getOpaqueResultTypeDecl()) { - printRec(opaque, "opaque_result_decl"); - } + printTypeOrTypeRepr(FD->getCachedResultInterfaceType(), + FD->getResultTypeRepr(), Label::always("result")); + if (auto opaque = FD->getCachedOpaqueResultTypeDecl(); + opaque && *opaque != nullptr) { + printRec(*opaque, Label::always("opaque_result_decl")); } } - if (auto thrownTypeRepr = D->getThrownTypeRepr()) { - printRec(thrownTypeRepr, "thrown_type"); - } + printTypeOrTypeRepr(D->getCachedThrownInterfaceType(), + D->getThrownTypeRepr(), Label::always("thrown_type")); + + printRec(D->getParameters(), Label::optional("params"), + &D->getASTContext()); if (auto fac = D->getForeignAsyncConvention()) { - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("foreign_async_convention", ASTNodeColor, label); if (auto type = fac->completionHandlerType()) - printFieldQuoted(type, "completion_handler_type", TypeColor); + printFieldQuoted(type, Label::always("completion_handler_type"), + TypeColor); printField(fac->completionHandlerParamIndex(), - "completion_handler_param"); + Label::always("completion_handler_param")); if (auto errorParamIndex = fac->completionHandlerErrorParamIndex()) - printField(*errorParamIndex, "error_param"); + printField(*errorParamIndex, Label::always("error_param")); printFoot(); - }); + }, Label::optional("foreign_async_convention")); } if (auto fec = D->getForeignErrorConvention()) { - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("foreign_error_convention", ASTNodeColor, label); - printField(fec->getKind(), "kind"); + printField(fec->getKind(), Label::always("kind")); bool wantResultType = ( fec->getKind() == ForeignErrorConvention::ZeroResult || @@ -1581,87 +2493,103 @@ namespace { printFlag(getDumpString(fec->isErrorOwned())); - printField(fec->getErrorParameterIndex(), "param"); - printFieldQuoted(fec->getErrorParameterType(), "paramtype"); + printField(fec->getErrorParameterIndex(), Label::always("param")); + printFieldQuoted(fec->getErrorParameterType(), + Label::always("paramtype")); if (wantResultType) - printFieldQuoted(fec->getResultType(), "resulttype"); + printFieldQuoted(fec->getResultType(), Label::always("resulttype")); printFoot(); - }); + }, Label::optional("foreign_error_convention")); } auto canParse = ParseIfNeeded && !D->isBodySkipped(); if (auto Body = D->getBody(canParse)) { - printRec(Body, &D->getASTContext()); + printRec(Body, &D->getASTContext(), Label::optional("body")); } } - void printCommonFD(FuncDecl *FD, const char *type, StringRef Label) { - printCommonAFD(FD, type, Label); - printFlag(FD->isStatic(), "type"); + void printCommonFD(FuncDecl *FD, const char *type, Label label) { + printCommonAFD(FD, type, label); + printFlag(FD->isStatic(), "static", DeclModifierColor); } - void visitFuncDecl(FuncDecl *FD, StringRef label) { + void visitFuncDecl(FuncDecl *FD, Label label) { printCommonFD(FD, "func_decl", label); printAbstractFunctionDecl(FD); printFoot(); } - void visitAccessorDecl(AccessorDecl *AD, StringRef label) { + void visitAccessorDecl(AccessorDecl *AD, Label label) { printCommonFD(AD, "accessor_decl", label); printFlag(getDumpString(AD->getAccessorKind())); - printDeclNameField(AD->getStorage(), "for"); + printDeclName(AD->getStorage(), Label::always("for")); printAbstractFunctionDecl(AD); printFoot(); } - void visitConstructorDecl(ConstructorDecl *CD, StringRef label) { + void visitConstructorDecl(ConstructorDecl *CD, Label label) { printCommonAFD(CD, "constructor_decl", label); printFlag(CD->isRequired(), "required", DeclModifierColor); printFlag(getDumpString(CD->getInitKind()), DeclModifierColor); if (CD->isFailable()) printField((CD->isImplicitlyUnwrappedOptional() ? "ImplicitlyUnwrappedOptional" - : "Optional"), "failable", DeclModifierColor); + : "Optional"), Label::always("failable"), + DeclModifierColor); printAbstractFunctionDecl(CD); printFoot(); } - void visitDestructorDecl(DestructorDecl *DD, StringRef label) { + void visitDestructorDecl(DestructorDecl *DD, Label label) { printCommonAFD(DD, "destructor_decl", label); printAbstractFunctionDecl(DD); printFoot(); } - void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD, StringRef label) { + void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD, Label label) { printCommon(TLCD, "top_level_code_decl", label); if (TLCD->getBody()) { - printRec(TLCD->getBody(), &static_cast(TLCD)->getASTContext()); + printRec(TLCD->getBody(), &static_cast(TLCD)->getASTContext(), + Label::optional("body")); } printFoot(); } - void visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD, StringRef label) { + void visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD, Label label) { printCommon(PDD, "pound_diagnostic_decl", label); - printField(PDD->isError() ? "error" : "warning", "kind"); - printRec(PDD->getMessage()); + printField(PDD->isError() ? "error" : "warning", Label::always("kind")); + printRec(PDD->getMessage(), Label::optional("message")); printFoot(); } - void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD, StringRef label) { + void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD, Label label) { printCommon(PGD, "precedence_group_decl", label); - printName(PGD->getName()); - printField(PGD->getAssociativity(), "associativity"); - printField(PGD->isAssignment(), "assignment"); + printName(PGD->getName(), Label::optional("name")); + printField(PGD->getAssociativity(), Label::always("associativity")); + printField(PGD->isAssignment(), Label::always("assignment")); auto printRelationsRec = [&](ArrayRef rels, StringRef name) { if (rels.empty()) return; - printRecArbitrary([&](StringRef label) { + // The S-expression representation doesn't render cleanly in JSON + // because it uses "flags" to just write out the names of the groups + // in order. Impose a little more structure for JSON. + if (Writer.isParsable()) { + printList(rels, [&](auto &rel, Label label) { + printRecArbitrary([&](Label label) { + printHead(rel.Name.str(), FieldLabelColor, label); + printFoot(); + }, label); + }, Label::always(name)); + return; + } + + printRecArbitrary([&](Label label) { printHead(name, FieldLabelColor, label); for (auto &rel : rels) printFlag(rel.Name.str()); printFoot(); - }); + }, Label::optional("relations")); }; printRelationsRec(PGD->getHigherThan(), "higherThan"); printRelationsRec(PGD->getLowerThan(), "lowerThan"); @@ -1669,54 +2597,59 @@ namespace { printFoot(); } - void visitInfixOperatorDecl(InfixOperatorDecl *IOD, StringRef label) { + void visitInfixOperatorDecl(InfixOperatorDecl *IOD, Label label) { printCommon(IOD, "infix_operator_decl", label); - printName(IOD->getName()); + printName(IOD->getName(), Label::optional("name")); if (!IOD->getPrecedenceGroupName().empty()) printFieldQuoted(IOD->getPrecedenceGroupName(), - "precedence_group_name"); + Label::always("precedence_group_name")); printFoot(); } - void visitPrefixOperatorDecl(PrefixOperatorDecl *POD, StringRef label) { + void visitPrefixOperatorDecl(PrefixOperatorDecl *POD, Label label) { printCommon(POD, "prefix_operator_decl", label); - printName(POD->getName()); + printName(POD->getName(), Label::optional("name")); printFoot(); } - void visitPostfixOperatorDecl(PostfixOperatorDecl *POD, StringRef label) { + void visitPostfixOperatorDecl(PostfixOperatorDecl *POD, Label label) { printCommon(POD, "postfix_operator_decl", label); - printName(POD->getName()); + printName(POD->getName(), Label::optional("name")); printFoot(); } - void visitModuleDecl(ModuleDecl *MD, StringRef label) { + void visitModuleDecl(ModuleDecl *MD, Label label) { printCommon(MD, "module", label); printFlag(MD->isNonSwiftModule(), "non_swift"); printFoot(); } - void visitMissingDecl(MissingDecl *missing, StringRef label) { + void visitMissingDecl(MissingDecl *missing, Label label) { printCommon(missing, "missing_decl", label); printFoot(); } - void visitMissingMemberDecl(MissingMemberDecl *MMD, StringRef label) { + void visitMissingMemberDecl(MissingMemberDecl *MMD, Label label) { printCommon(MMD, "missing_member_decl ", label); - printName(MMD->getName()); + printName(MMD->getName(), Label::optional("name")); printFoot(); } - void visitMacroDecl(MacroDecl *MD, StringRef label) { + void visitMacroDecl(MacroDecl *MD, Label label) { printCommon(MD, "macro_decl", label); - // TODO: Fill this in? + printAttributes(MD); + printRec(MD->getParameterList(), Label::optional("params"), + &MD->getASTContext()); + printTypeOrTypeRepr(MD->getCachedResultInterfaceType(), + MD->getResultTypeRepr(), Label::always("result")); + printRec(MD->definition, Label::always("definition")); printFoot(); } - void visitMacroExpansionDecl(MacroExpansionDecl *MED, StringRef label) { + void visitMacroExpansionDecl(MacroExpansionDecl *MED, Label label) { printCommon(MED, "macro_expansion_decl", label); - printName(MED->getMacroName().getFullName()); - printRec(MED->getArgs()); + printName(MED->getMacroName().getFullName(), Label::optional("name")); + printRec(MED->getArgs(), Label::optional("args")); printFoot(); } }; @@ -1728,8 +2661,9 @@ void ParameterList::dump() const { } void ParameterList::dump(raw_ostream &OS, unsigned Indent) const { - PrintDecl(OS, Indent) - .visitParameterList(const_cast(this), ""); + DefaultWriter writer(OS, Indent); + PrintDecl(writer).visitParameterList(const_cast(this), + Label::optional("")); } void Decl::dump() const { @@ -1748,7 +2682,8 @@ void Decl::dump(const char *filename) const { } void Decl::dump(raw_ostream &OS, unsigned Indent) const { - PrintDecl(OS, Indent).visit(const_cast(this), ""); + DefaultWriter writer(OS, Indent); + PrintDecl(writer).visit(const_cast(this), Label::optional("")); OS << '\n'; } @@ -1876,16 +2811,23 @@ void SourceFile::dump() const { } void SourceFile::dump(llvm::raw_ostream &OS, bool parseIfNeeded) const { - PrintDecl(OS, /*indent*/ 0, parseIfNeeded).visitSourceFile(*this); + DefaultWriter writer(OS, /*indent*/ 0); + PrintDecl(writer, parseIfNeeded).visitSourceFile(*this); llvm::errs() << '\n'; } +void SourceFile::dumpJSON(llvm::raw_ostream &OS) const { + JSONWriter writer(OS, /*indent*/ 0); + PrintDecl(writer, /*parseIfNeeded*/ true).visitSourceFile(*this); +} + void Pattern::dump() const { dump(llvm::errs()); } void Pattern::dump(raw_ostream &OS, unsigned Indent) const { - PrintPattern(OS, Indent).visit(const_cast(this), ""); + DefaultWriter writer(OS, Indent); + PrintPattern(writer).visit(const_cast(this), Label::optional("")); OS << '\n'; } @@ -1895,164 +2837,167 @@ void Pattern::dump(raw_ostream &OS, unsigned Indent) const { namespace { /// PrintStmt - Visitor implementation of Stmt::dump. -class PrintStmt : public StmtVisitor, +class PrintStmt : public StmtVisitor, public PrintBase { public: using PrintBase::PrintBase; const ASTContext *Ctx; PrintStmt( - raw_ostream &os, const ASTContext *ctx, unsigned indent = 0, + PrintWriterBase &writer, const ASTContext *ctx, bool parseIfNeeded = false, llvm::function_ref getTypeOfExpr = defaultGetTypeOfExpr, llvm::function_ref getTypeOfTypeRepr = nullptr, llvm::function_ref getTypeOfKeyPathComponent = defaultGetTypeOfKeyPathComponent) - : PrintBase(os, indent, parseIfNeeded, getTypeOfExpr, getTypeOfTypeRepr, + : PrintBase(writer, parseIfNeeded, getTypeOfExpr, getTypeOfTypeRepr, getTypeOfKeyPathComponent), Ctx(ctx) {} using PrintBase::printRec; - void printRec(Stmt *S, StringRef Label = "") { - PrintBase::printRec(S, Ctx, Label); + void printRec(Stmt *S, Label label) { + PrintBase::printRec(S, Ctx, label); } - void printCommon(Stmt *S, const char *Name, StringRef Label) { - printHead(Name, StmtColor, Label); + void printCommon(Stmt *S, const char *Name, Label label) { + printHead(Name, StmtColor, label); printFlag(S->isImplicit(), "implicit"); printSourceRange(S->getSourceRange(), Ctx); printFlag(S->TrailingSemiLoc.isValid(), "trailing_semi"); } - void visitBraceStmt(BraceStmt *S, StringRef label) { + void visitBraceStmt(BraceStmt *S, Label label) { printCommon(S, "brace_stmt", label); - for (auto &Elt : S->getElements()) - printRec(Elt, Ctx); + printList(S->getElements(), [&](auto &Elt, Label label) { + printRec(Elt, Ctx, label); + }, Label::optional("elements")); printFoot(); } - void visitReturnStmt(ReturnStmt *S, StringRef label) { + void visitReturnStmt(ReturnStmt *S, Label label) { printCommon(S, "return_stmt", label); if (S->hasResult()) { - printRec(S->getResult()); + printRec(S->getResult(), Label::optional("result")); } printFoot(); } - void visitYieldStmt(YieldStmt *S, StringRef label) { + void visitYieldStmt(YieldStmt *S, Label label) { printCommon(S, "yield_stmt", label); - for (auto yield : S->getYields()) { - printRec(yield); - } + printList(S->getYields(), [&](auto yield, Label label) { + printRec(yield, label); + }, Label::optional("yields")); printFoot(); } - void visitThenStmt(ThenStmt *S, StringRef label) { + void visitThenStmt(ThenStmt *S, Label label) { printCommon(S, "then_stmt", label); - printRec(S->getResult()); + printRec(S->getResult(), Label::optional("result")); printFoot(); } - void visitDeferStmt(DeferStmt *S, StringRef label) { + void visitDeferStmt(DeferStmt *S, Label label) { printCommon(S, "defer_stmt", label); - printRec(S->getTempDecl()); - printRec(S->getCallExpr()); + printRec(S->getTempDecl(), Label::optional("temp_decl")); + printRec(S->getCallExpr(), Label::optional("call_expr")); printFoot(); } - void visitIfStmt(IfStmt *S, StringRef label) { + void visitIfStmt(IfStmt *S, Label label) { printCommon(S, "if_stmt", label); - printRecRange(S->getCond(), Ctx, "conditions"); - printRec(S->getThenStmt()); + printRecRange(S->getCond(), Ctx, Label::always("conditions")); + printRec(S->getThenStmt(), Label::optional("then_stmt")); if (S->getElseStmt()) { - printRec(S->getElseStmt()); + printRec(S->getElseStmt(), Label::optional("else_stmt")); } printFoot(); } - void visitGuardStmt(GuardStmt *S, StringRef label) { + void visitGuardStmt(GuardStmt *S, Label label) { printCommon(S, "guard_stmt", label); - printRecRange(S->getCond(), Ctx, "conditions"); - printRec(S->getBody()); + printRecRange(S->getCond(), Ctx, Label::always("conditions")); + printRec(S->getBody(), Label::optional("body")); printFoot(); } - void visitDoStmt(DoStmt *S, StringRef label) { + void visitDoStmt(DoStmt *S, Label label) { printCommon(S, "do_stmt", label); - printRec(S->getBody()); + printRec(S->getBody(), Label::optional("body")); printFoot(); } - void visitWhileStmt(WhileStmt *S, StringRef label) { + void visitWhileStmt(WhileStmt *S, Label label) { printCommon(S, "while_stmt", label); - printRecRange(S->getCond(), Ctx, "conditions"); - printRec(S->getBody()); + printRecRange(S->getCond(), Ctx, Label::always("conditions")); + printRec(S->getBody(), Label::optional("body")); printFoot(); } - void visitRepeatWhileStmt(RepeatWhileStmt *S, StringRef label) { + void visitRepeatWhileStmt(RepeatWhileStmt *S, Label label) { printCommon(S, "repeat_while_stmt", label); - printRec(S->getBody()); - printRec(S->getCond()); + printRec(S->getBody(), Label::optional("body")); + printRec(S->getCond(), Label::optional("cond")); printFoot(); } - void visitForEachStmt(ForEachStmt *S, StringRef label) { + void visitForEachStmt(ForEachStmt *S, Label label) { printCommon(S, "for_each_stmt", label); - printRec(S->getPattern()); + printRec(S->getPattern(), Label::optional("pattern")); if (S->getWhere()) { - printRec(S->getWhere(), "where"); + printRec(S->getWhere(), Label::always("where")); } - printRec(S->getParsedSequence()); + printRec(S->getParsedSequence(), Label::optional("parsed_sequence")); if (S->getIteratorVar()) { - printRec(S->getIteratorVar()); + printRec(S->getIteratorVar(), Label::optional("iterator_var")); } if (S->getNextCall()) { - printRec(S->getNextCall()); + printRec(S->getNextCall(), Label::optional("next_call")); } if (S->getConvertElementExpr()) { - printRec(S->getConvertElementExpr()); + printRec(S->getConvertElementExpr(), + Label::optional("convert_element_expr")); } if (S->getElementExpr()) { - printRec(S->getElementExpr()); + printRec(S->getElementExpr(), Label::optional("element_expr")); } - printRec(S->getBody()); + printRec(S->getBody(), Label::optional("body")); printFoot(); } - void visitBreakStmt(BreakStmt *S, StringRef label) { + void visitBreakStmt(BreakStmt *S, Label label) { printCommon(S, "break_stmt", label); printFoot(); } - void visitContinueStmt(ContinueStmt *S, StringRef label) { + void visitContinueStmt(ContinueStmt *S, Label label) { printCommon(S, "continue_stmt", label); printFoot(); } - void visitFallthroughStmt(FallthroughStmt *S, StringRef label) { + void visitFallthroughStmt(FallthroughStmt *S, Label label) { printCommon(S, "fallthrough_stmt", label); printFoot(); } - void visitSwitchStmt(SwitchStmt *S, StringRef label) { + void visitSwitchStmt(SwitchStmt *S, Label label) { printCommon(S, "switch_stmt", label); - printRec(S->getSubjectExpr()); - for (auto N : S->getRawCases()) { + printRec(S->getSubjectExpr(), Label::optional("subject_expr")); + printList(S->getRawCases(), [&](ASTNode N, Label label) { if (N.is()) - printRec(N.get()); + printRec(N.get(), label); else - printRec(N.get()); - } + printRec(N.get(), label); + }, Label::optional("cases")); printFoot(); } - void visitCaseStmt(CaseStmt *S, StringRef label) { + void visitCaseStmt(CaseStmt *S, Label label) { printCommon(S, "case_stmt", label); printFlag(S->hasUnknownAttr(), "@unknown"); if (S->hasCaseBodyVariables()) { - printRecRange(S->getCaseBodyVariables(), "case_body_variables"); + printRecRange(S->getCaseBodyVariables(), + Label::always("case_body_variables")); } - for (const auto &LabelItem : S->getCaseLabelItems()) { - printRecArbitrary([&](StringRef label) { + printList(S->getCaseLabelItems(), [&](const auto &LabelItem, Label label) { + printRecArbitrary([&](Label label) { printHead("case_label_item", StmtColor, label); printFlag(LabelItem.isDefault(), "default"); @@ -2062,59 +3007,59 @@ class PrintStmt : public StmtVisitor, break; case ValueOwnership::Shared: printFieldRaw([](llvm::raw_ostream &os) { os << "borrowing"; }, - "ownership"); + Label::always("ownership")); break; case ValueOwnership::InOut: printFieldRaw([](llvm::raw_ostream &os) { os << "mutating"; }, - "ownership"); + Label::always("ownership")); break; case ValueOwnership::Owned: printFieldRaw([](llvm::raw_ostream &os) { os << "consuming"; }, - "ownership"); + Label::always("ownership")); break; } - printRec(CasePattern); + printRec(CasePattern, Label::optional("pattern")); } if (auto *Guard = LabelItem.getGuardExpr()) { - printRec(const_cast(Guard)); + printRec(const_cast(Guard), Label::optional("where_expr")); } printFoot(); - }); - } + }, label); + }, Label::optional("case_label_items")); - printRec(S->getBody()); + printRec(S->getBody(), Label::optional("body")); printFoot(); } - void visitFailStmt(FailStmt *S, StringRef label) { + void visitFailStmt(FailStmt *S, Label label) { printCommon(S, "fail_stmt", label); printFoot(); } - void visitThrowStmt(ThrowStmt *S, StringRef label) { + void visitThrowStmt(ThrowStmt *S, Label label) { printCommon(S, "throw_stmt", label); - printRec(S->getSubExpr()); + printRec(S->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitDiscardStmt(DiscardStmt *S, StringRef label) { + void visitDiscardStmt(DiscardStmt *S, Label label) { printCommon(S, "discard_stmt", label); - printRec(S->getSubExpr()); + printRec(S->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitPoundAssertStmt(PoundAssertStmt *S, StringRef label) { + void visitPoundAssertStmt(PoundAssertStmt *S, Label label) { printCommon(S, "pound_assert", label); - printFieldQuoted(S->getMessage(), "message"); - printRec(S->getCondition()); + printFieldQuoted(S->getMessage(), Label::always("message")); + printRec(S->getCondition(), Label::optional("condition")); printFoot(); } - void visitDoCatchStmt(DoCatchStmt *S, StringRef label) { + void visitDoCatchStmt(DoCatchStmt *S, Label label) { printCommon(S, "do_catch_stmt", label); printThrowDest(S->rethrows(), /*wantNothrow=*/true); - printRec(S->getBody(), "body"); - printRecRange(S->getCatches(), Ctx, "catch_stmts"); + printRec(S->getBody(), Label::always("body")); + printRecRange(S->getCatches(), Ctx, Label::always("catch_stmts")); printFoot(); } }; @@ -2127,7 +3072,8 @@ void Stmt::dump() const { } void Stmt::dump(raw_ostream &OS, const ASTContext *Ctx, unsigned Indent) const { - PrintStmt(OS, Ctx, Indent).visit(const_cast(this), ""); + DefaultWriter writer(OS, Indent); + PrintStmt(writer, Ctx).visit(const_cast(this), Label::optional("")); } //===----------------------------------------------------------------------===// @@ -2136,21 +3082,21 @@ void Stmt::dump(raw_ostream &OS, const ASTContext *Ctx, unsigned Indent) const { namespace { /// PrintExpr - Visitor implementation of Expr::dump. -class PrintExpr : public ExprVisitor, +class PrintExpr : public ExprVisitor, public PrintBase { public: using PrintBase::PrintBase; /// FIXME: This should use ExprWalker to print children. - void printCommon(Expr *E, const char *C, StringRef label) { + void printCommon(Expr *E, const char *C, Label label) { PrintOptions PO; PO.PrintTypesForDebugging = true; printHead(C, ExprColor, label); printFlag(E->isImplicit(), "implicit", ExprModifierColor); - printFieldQuoted(GetTypeOfExpr(E).getString(PO), "type", TypeColor); + printTypeField(GetTypeOfExpr(E), Label::always("type"), PO, TypeColor); // If we have a source range and an ASTContext, print the source range. if (auto Ty = GetTypeOfExpr(E)) { @@ -2162,322 +3108,325 @@ class PrintExpr : public ExprVisitor, printFlag(E->TrailingSemiLoc.isValid(), "trailing_semi"); } - void printSemanticExpr(Expr * semanticExpr) { - if (semanticExpr == nullptr) { - return; - } - - printRec(semanticExpr, "semantic_expr"); - } - - void visitErrorExpr(ErrorExpr *E, StringRef label) { + void visitErrorExpr(ErrorExpr *E, Label label) { printCommon(E, "error_expr", label); printFoot(); } - void visitCodeCompletionExpr(CodeCompletionExpr *E, StringRef label) { + void visitCodeCompletionExpr(CodeCompletionExpr *E, Label label) { printCommon(E, "code_completion_expr", label); if (E->getBase()) { - printRec(E->getBase()); + printRec(E->getBase(), Label::optional("base")); } printFoot(); } - void printInitializerField(ConcreteDeclRef declRef, StringRef label) { - printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label, - ExprModifierColor); + void printInitializerField(ConcreteDeclRef declRef, Label label) { + printDeclRefField(declRef, label, ExprModifierColor); } - void visitNilLiteralExpr(NilLiteralExpr *E, StringRef label) { + void visitNilLiteralExpr(NilLiteralExpr *E, Label label) { printCommon(E, "nil_literal_expr", label); - printInitializerField(E->getInitializer(), "initializer"); + printInitializerField(E->getInitializer(), Label::always("initializer")); printFoot(); } - void visitIntegerLiteralExpr(IntegerLiteralExpr *E, StringRef label) { + void visitIntegerLiteralExpr(IntegerLiteralExpr *E, Label label) { printCommon(E, "integer_literal_expr", label); printFlag(E->isNegative(), "negative", LiteralValueColor); Type T = GetTypeOfExpr(E); if (T.isNull() || !T->is()) - printFieldQuoted(E->getDigitsText(), "value", LiteralValueColor); + printFieldQuoted(E->getDigitsText(), Label::always("value"), + LiteralValueColor); else - printFieldQuoted(E->getValue(), "value", LiteralValueColor); - printInitializerField(E->getBuiltinInitializer(), "builtin_initializer"); - printInitializerField(E->getInitializer(), "initializer"); + printFieldQuoted(E->getValue(), Label::always("value"), LiteralValueColor); + printInitializerField(E->getBuiltinInitializer(), + Label::always("builtin_initializer")); + printInitializerField(E->getInitializer(), Label::always("initializer")); printFoot(); } - void visitFloatLiteralExpr(FloatLiteralExpr *E, StringRef label) { + void visitFloatLiteralExpr(FloatLiteralExpr *E, Label label) { printCommon(E, "float_literal_expr", label); printFlag(E->isNegative(), "negative", LiteralValueColor); - printFieldQuoted(E->getDigitsText(), "value", LiteralValueColor); - printInitializerField(E->getBuiltinInitializer(), "builtin_initializer"); - printInitializerField(E->getInitializer(), "initializer"); + printFieldQuoted(E->getDigitsText(), Label::always("value"), + LiteralValueColor); + printInitializerField(E->getBuiltinInitializer(), + Label::always("builtin_initializer")); + printInitializerField(E->getInitializer(), Label::always("initializer")); if (!E->getBuiltinType().isNull()) { - printFieldQuoted(E->getBuiltinType(), "builtin_type", ExprModifierColor); + printFieldQuoted(E->getBuiltinType(), Label::always("builtin_type"), + ExprModifierColor); } printFoot(); } - void visitBooleanLiteralExpr(BooleanLiteralExpr *E, StringRef label) { + void visitBooleanLiteralExpr(BooleanLiteralExpr *E, Label label) { printCommon(E, "boolean_literal_expr", label); - printField(E->getValue(), "value", LiteralValueColor); - printInitializerField(E->getBuiltinInitializer(), "builtin_initializer"); - printInitializerField(E->getInitializer(), "initializer"); + printField(E->getValue(), Label::always("value"), LiteralValueColor); + printInitializerField(E->getBuiltinInitializer(), + Label::always("builtin_initializer")); + printInitializerField(E->getInitializer(), Label::always("initializer")); printFoot(); } - void visitStringLiteralExpr(StringLiteralExpr *E, StringRef label) { + void visitStringLiteralExpr(StringLiteralExpr *E, Label label) { printCommon(E, "string_literal_expr", label); - printField(E->getEncoding(), "encoding", ExprModifierColor); - printFieldQuoted(E->getValue(), "value", LiteralValueColor); - printInitializerField(E->getBuiltinInitializer(), "builtin_initializer"); - printInitializerField(E->getInitializer(), "initializer"); + printField(E->getEncoding(), Label::always("encoding"), ExprModifierColor); + printFieldQuoted(E->getValue(), Label::always("value"), LiteralValueColor); + printInitializerField(E->getBuiltinInitializer(), + Label::always("builtin_initializer")); + printInitializerField(E->getInitializer(), Label::always("initializer")); printFoot(); } - void visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E, StringRef label) { + void visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E, Label label) { printCommon(E, "interpolated_string_literal_expr", label); - printField(E->getLiteralCapacity(), "literal_capacity", ExprModifierColor); - printField(E->getInterpolationCount(), "interpolation_count", + printField(E->getLiteralCapacity(), Label::always("literal_capacity"), + ExprModifierColor); + printField(E->getInterpolationCount(), Label::always("interpolation_count"), ExprModifierColor); - printInitializerField(E->getBuilderInit(), "builder_init"); - printInitializerField(E->getInitializer(), "result_init"); + printInitializerField(E->getBuilderInit(), Label::always("builder_init")); + printInitializerField(E->getInitializer(), Label::always("result_init")); - printRec(E->getAppendingExpr()); + printRec(E->getAppendingExpr(), Label::optional("appending_expr")); printFoot(); } - void visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, StringRef label) { + void visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, Label label) { printCommon(E, "magic_identifier_literal_expr", label); - printField(E->getKind(), "kind", ExprModifierColor); + printField(E->getKind(), Label::always("kind"), ExprModifierColor); if (E->isString()) { - printField(E->getStringEncoding(), "encoding", ExprModifierColor); + printField(E->getStringEncoding(), Label::always("encoding"), + ExprModifierColor); } - printInitializerField(E->getBuiltinInitializer(), "builtin_initializer"); - printInitializerField(E->getInitializer(), "initializer"); + printInitializerField(E->getBuiltinInitializer(), + Label::always("builtin_initializer")); + printInitializerField(E->getInitializer(), Label::always("initializer")); printFoot(); } - void visitRegexLiteralExpr(RegexLiteralExpr *E, StringRef label) { + void visitRegexLiteralExpr(RegexLiteralExpr *E, Label label) { printCommon(E, "regex_literal_expr", label); - printFieldQuoted(E->getParsedRegexText(), "text", LiteralValueColor); - printInitializerField(E->getInitializer(), "initializer"); + printFieldQuoted(E->getParsedRegexText(), Label::always("text"), + LiteralValueColor); + printInitializerField(E->getInitializer(), Label::always("initializer")); printFoot(); } - void visitObjectLiteralExpr(ObjectLiteralExpr *E, StringRef label) { + void visitObjectLiteralExpr(ObjectLiteralExpr *E, Label label) { printCommon(E, "object_literal", label); - printField(E->getLiteralKind(), "kind"); - printInitializerField(E->getInitializer(), "initializer"); + printField(E->getLiteralKind(), Label::always("kind")); + printInitializerField(E->getInitializer(), Label::always("initializer")); - printRec(E->getArgs()); + printRec(E->getArgs(), Label::optional("args")); printFoot(); } - void visitDiscardAssignmentExpr(DiscardAssignmentExpr *E, StringRef label) { + void visitDiscardAssignmentExpr(DiscardAssignmentExpr *E, Label label) { printCommon(E, "discard_assignment_expr", label); printFoot(); } - void visitDeclRefExpr(DeclRefExpr *E, StringRef label) { + void visitDeclRefExpr(DeclRefExpr *E, Label label) { printCommon(E, "declref_expr", label); printThrowDest(E->throws(), /*wantNothrow=*/false); - printDeclRefField(E->getDeclRef(), "decl"); + printDeclRefField(E->getDeclRef(), Label::always("decl")); if (E->getAccessSemantics() != AccessSemantics::Ordinary) printFlag(getDumpString(E->getAccessSemantics()), AccessLevelColor); - printField(E->getFunctionRefKind(), "function_ref", - ExprModifierColor); + printField(E->getFunctionRefKind(), Label::always("function_ref")); printFoot(); } - void visitSuperRefExpr(SuperRefExpr *E, StringRef label) { + void visitSuperRefExpr(SuperRefExpr *E, Label label) { printCommon(E, "super_ref_expr", label); printFoot(); } - void visitTypeExpr(TypeExpr *E, StringRef label) { + void visitTypeExpr(TypeExpr *E, Label label) { printCommon(E, "type_expr", label); - if (E->getTypeRepr()) - printFieldQuotedRaw([&](raw_ostream &OS) { E->getTypeRepr()->print(OS); }, - "typerepr", TypeReprColor); - else - printFlag("null_typerepr"); + // For parsable outputs, the type is already printed by `printCommon`. + if (!Writer.isParsable()) { + if (E->getTypeRepr()) + printFieldQuotedRaw([&](raw_ostream &OS) { E->getTypeRepr()->print(OS); }, + Label::always("typerepr"), TypeReprColor); + else + printFlag("null_typerepr"); + } printFoot(); } - void visitOtherConstructorDeclRefExpr(OtherConstructorDeclRefExpr *E, StringRef label) { + void visitOtherConstructorDeclRefExpr(OtherConstructorDeclRefExpr *E, Label label) { printCommon(E, "other_constructor_ref_expr", label); - printDeclRefField(E->getDeclRef(), "decl"); + printDeclRefField(E->getDeclRef(), Label::always("decl")); printFoot(); } - void visitOverloadedDeclRefExpr(OverloadedDeclRefExpr *E, StringRef label) { + void visitOverloadedDeclRefExpr(OverloadedDeclRefExpr *E, Label label) { printCommon(E, "overloaded_decl_ref_expr", label); - printFieldQuoted(E->getDecls()[0]->getBaseName(), "name", IdentifierColor); - printField(E->getDecls().size(), "number_of_decls", ExprModifierColor); - printField(E->getFunctionRefKind(), "function_ref", ExprModifierColor); + printFieldQuoted(E->getDecls()[0]->getBaseName(), Label::always("name"), + IdentifierColor); + printField(E->getDecls().size(), Label::always("number_of_decls"), + ExprModifierColor); + printField(E->getFunctionRefKind(), Label::always("function_ref")); if (!E->isForOperator()) { - for (auto D : E->getDecls()) { - printRecArbitrary([&](StringRef label) { + printList(E->getDecls(), [&](auto D, Label label) { + printRecArbitrary([&](Label label) { printHead("candidate_decl", DeclModifierColor, label); - printNameRaw([&](raw_ostream &OS) { D->dumpRef(OS); }); + printNameRaw([&](raw_ostream &OS) { D->dumpRef(OS); }, + Label::optional("name")); printFoot(); - }); - } + }, Label::optional("decl")); + }, Label::optional("candidates")); } printFoot(); } - void visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *E, StringRef label) { + void visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *E, Label label) { printCommon(E, "unresolved_decl_ref_expr", label); - printFieldQuoted(E->getName(), "name", IdentifierColor); - printField(E->getFunctionRefKind(), "function_ref", ExprModifierColor); + printFieldQuoted(E->getName(), Label::always("name"), IdentifierColor); + printField(E->getFunctionRefKind(), Label::always("function_ref")); printFoot(); } - void visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *E, StringRef label) { + void visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *E, Label label) { printCommon(E, "unresolved_specialize_expr", label); - printRec(E->getSubExpr()); - for (TypeLoc T : E->getUnresolvedParams()) { - printRec(T.getTypeRepr()); - } + printRec(E->getSubExpr(), Label::optional("sub_expr")); + printList(E->getUnresolvedParams(), [&](TypeLoc T, Label label) { + printRec(T.getTypeRepr(), label); + }, Label::optional("unresolved_params")); printFoot(); } - void visitMemberRefExpr(MemberRefExpr *E, StringRef label) { + void visitMemberRefExpr(MemberRefExpr *E, Label label) { printCommon(E, "member_ref_expr", label); printThrowDest(E->throws(), /*wantNothrow=*/false); - printDeclRefField(E->getMember(), "decl"); + printDeclRefField(E->getMember(), Label::always("decl")); if (E->getAccessSemantics() != AccessSemantics::Ordinary) printFlag(getDumpString(E->getAccessSemantics()), AccessLevelColor); printFlag(E->isSuper(), "super"); - printRec(E->getBase()); + printRec(E->getBase(), Label::optional("base")); printFoot(); } - void visitDynamicMemberRefExpr(DynamicMemberRefExpr *E, StringRef label) { + void visitDynamicMemberRefExpr(DynamicMemberRefExpr *E, Label label) { printCommon(E, "dynamic_member_ref_expr", label); printThrowDest(E->throws(), /*wantNothrow=*/false); - printDeclRefField(E->getMember(), "decl"); + printDeclRefField(E->getMember(), Label::always("decl")); - printRec(E->getBase()); + printRec(E->getBase(), Label::optional("base")); printFoot(); } - void visitUnresolvedMemberExpr(UnresolvedMemberExpr *E, StringRef label) { + void visitUnresolvedMemberExpr(UnresolvedMemberExpr *E, Label label) { printCommon(E, "unresolved_member_expr", label); - printFieldQuoted(E->getName(), "name", ExprModifierColor); - printField(E->getFunctionRefKind(), "function_ref", ExprModifierColor); + printFieldQuoted(E->getName(), Label::always("name"), ExprModifierColor); + printField(E->getFunctionRefKind(), Label::always("function_ref")); printFoot(); } - void visitDotSelfExpr(DotSelfExpr *E, StringRef label) { + void visitDotSelfExpr(DotSelfExpr *E, Label label) { printCommon(E, "dot_self_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitParenExpr(ParenExpr *E, StringRef label) { + void visitParenExpr(ParenExpr *E, Label label) { printCommon(E, "paren_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitAwaitExpr(AwaitExpr *E, StringRef label) { + void visitAwaitExpr(AwaitExpr *E, Label label) { printCommon(E, "await_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitConsumeExpr(ConsumeExpr *E, StringRef label) { + void visitConsumeExpr(ConsumeExpr *E, Label label) { printCommon(E, "consume_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitCopyExpr(CopyExpr *E, StringRef label) { + void visitCopyExpr(CopyExpr *E, Label label) { printCommon(E, "copy_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitBorrowExpr(BorrowExpr *E, StringRef label) { + void visitBorrowExpr(BorrowExpr *E, Label label) { printCommon(E, "borrow_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitUnresolvedMemberChainResultExpr(UnresolvedMemberChainResultExpr *E, StringRef label){ + void visitUnresolvedMemberChainResultExpr(UnresolvedMemberChainResultExpr *E, Label label){ printCommon(E, "unresolved_member_chain_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitTupleExpr(TupleExpr *E, StringRef label) { + void visitTupleExpr(TupleExpr *E, Label label) { printCommon(E, "tuple_expr", label); if (E->hasElementNames()) { - printFieldQuotedRaw([&](raw_ostream &OS) { - interleave(E->getElementNames(), OS, - [&](Identifier name) { - OS << (name.empty()?"''":name.str()); - }, - ","); - }, "names", IdentifierColor); + printStringListField(E->getElementNames(), [&](Identifier name) { + return name.str(); + }, Label::optional("names")); } - for (unsigned i = 0, e = E->getNumElements(); i != e; ++i) { + printList(range(E->getNumElements()), [&](unsigned i, Label label) { if (E->getElement(i)) - printRec(E->getElement(i)); + printRec(E->getElement(i), label); else { - printRecArbitrary([&](StringRef label) { - printHead("", ExprColor); + printRecArbitrary([&](Label label) { + printHead("", ExprColor, label); printFoot(); - }); + }, label); } - } + }, Label::optional("elements")); printFoot(); } - void visitArrayExpr(ArrayExpr *E, StringRef label) { + void visitArrayExpr(ArrayExpr *E, Label label) { printCommon(E, "array_expr", label); - printInitializerField(E->getInitializer(), "initializer"); + printInitializerField(E->getInitializer(), Label::always("initializer")); - for (auto elt : E->getElements()) { - printRec(elt); - } + printList(E->getElements(), [&](auto elt, Label label) { + printRec(elt, label); + }, Label::optional("elements")); printFoot(); } - void visitDictionaryExpr(DictionaryExpr *E, StringRef label) { + void visitDictionaryExpr(DictionaryExpr *E, Label label) { printCommon(E, "dictionary_expr", label); - printInitializerField(E->getInitializer(), "initializer"); + printInitializerField(E->getInitializer(), Label::always("initializer")); - for (auto elt : E->getElements()) { - printRec(elt); - } + printList(E->getElements(), [&](auto elt, Label label) { + printRec(elt, label); + }, Label::optional("elements")); printFoot(); } - void visitSubscriptExpr(SubscriptExpr *E, StringRef label) { + void visitSubscriptExpr(SubscriptExpr *E, Label label) { printCommon(E, "subscript_expr", label); printThrowDest(E->throws(), /*wantNothrow=*/false); @@ -2485,338 +3434,350 @@ class PrintExpr : public ExprVisitor, printFlag(getDumpString(E->getAccessSemantics()), AccessLevelColor); printFlag(E->isSuper(), "super"); if (E->hasDecl()) { - printDeclRefField(E->getDecl(), "decl"); + printDeclRefField(E->getDecl(), Label::always("decl")); } - printRec(E->getBase()); - printRec(E->getArgs()); + printRec(E->getBase(), Label::optional("base")); + printRec(E->getArgs(), Label::optional("args")); printFoot(); } - void visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, StringRef label) { + void visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, Label label) { printCommon(E, "keypath_application_expr", label); - printRec(E->getBase()); - printRec(E->getKeyPath()); + printRec(E->getBase(), Label::optional("base")); + printRec(E->getKeyPath(), Label::optional("keypath")); printFoot(); } - void visitDynamicSubscriptExpr(DynamicSubscriptExpr *E, StringRef label) { + void visitDynamicSubscriptExpr(DynamicSubscriptExpr *E, Label label) { printCommon(E, "dynamic_subscript_expr", label); printThrowDest(E->throws(), /*wantNothrow=*/false); - printDeclRefField(E->getMember(), "decl"); + printDeclRefField(E->getMember(), Label::always("decl")); - printRec(E->getBase()); - printRec(E->getArgs()); + printRec(E->getBase(), Label::optional("base")); + printRec(E->getArgs(), Label::optional("args")); printFoot(); } - void visitUnresolvedDotExpr(UnresolvedDotExpr *E, StringRef label) { + void visitUnresolvedDotExpr(UnresolvedDotExpr *E, Label label) { printCommon(E, "unresolved_dot_expr", label); - printFieldQuoted(E->getName(), "field"); - printField(E->getFunctionRefKind(), "function_ref", ExprModifierColor); + printFieldQuoted(E->getName(), Label::always("field")); + printField(E->getFunctionRefKind(), Label::always("function_ref")); if (E->getBase()) { - printRec(E->getBase()); + printRec(E->getBase(), Label::optional("base")); } printFoot(); } - void visitTupleElementExpr(TupleElementExpr *E, StringRef label) { + void visitTupleElementExpr(TupleElementExpr *E, Label label) { printCommon(E, "tuple_element_expr", label); - printField(E->getFieldNumber(), "field #"); + printField(E->getFieldNumber(), Label::always("field #")); - printRec(E->getBase()); + printRec(E->getBase(), Label::optional("base")); printFoot(); } - void visitDestructureTupleExpr(DestructureTupleExpr *E, StringRef label) { + void visitDestructureTupleExpr(DestructureTupleExpr *E, Label label) { printCommon(E, "destructure_tuple_expr", label); - printRecRange(E->getDestructuredElements(), "destructured"); - printRec(E->getSubExpr()); - printRec(E->getResultExpr()); + printRecRange(E->getDestructuredElements(), Label::always("destructured")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); + printRec(E->getResultExpr(), Label::optional("result_expr")); printFoot(); } - void visitUnresolvedTypeConversionExpr(UnresolvedTypeConversionExpr *E, StringRef label) { + void visitUnresolvedTypeConversionExpr(UnresolvedTypeConversionExpr *E, + Label label) { printCommon(E, "unresolvedtype_conversion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitFunctionConversionExpr(FunctionConversionExpr *E, StringRef label) { + void visitFunctionConversionExpr(FunctionConversionExpr *E, Label label) { printCommon(E, "function_conversion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitCovariantFunctionConversionExpr(CovariantFunctionConversionExpr *E, StringRef label){ + void visitCovariantFunctionConversionExpr(CovariantFunctionConversionExpr *E, + Label label){ printCommon(E, "covariant_function_conversion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitCovariantReturnConversionExpr(CovariantReturnConversionExpr *E, StringRef label){ + void visitCovariantReturnConversionExpr(CovariantReturnConversionExpr *E, + Label label){ printCommon(E, "covariant_return_conversion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, StringRef label){ + void visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, Label label){ printCommon(E, "underlying_to_opaque_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitErasureExpr(ErasureExpr *E, StringRef label) { + void visitErasureExpr(ErasureExpr *E, Label label) { printCommon(E, "erasure_expr", label); - for (auto conf : E->getConformances()) { - printRec(conf); - } - printRec(E->getSubExpr()); + printList(E->getConformances(), [&](auto conf, Label label) { + printRec(conf, label); + }, Label::optional("conformances")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitAnyHashableErasureExpr(AnyHashableErasureExpr *E, StringRef label) { + void visitAnyHashableErasureExpr(AnyHashableErasureExpr *E, Label label) { printCommon(E, "any_hashable_erasure_expr", label); - printRec(E->getConformance()); - printRec(E->getSubExpr()); + printRec(E->getConformance(), Label::optional("conformance")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitConditionalBridgeFromObjCExpr(ConditionalBridgeFromObjCExpr *E, StringRef label) { + void visitConditionalBridgeFromObjCExpr(ConditionalBridgeFromObjCExpr *E, + Label label) { printCommon(E, "conditional_bridge_from_objc_expr", label); - printDeclRefField(E->getConversion(), "conversion"); + printDeclRefField(E->getConversion(), Label::always("conversion")); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitBridgeFromObjCExpr(BridgeFromObjCExpr *E, StringRef label) { + void visitBridgeFromObjCExpr(BridgeFromObjCExpr *E, Label label) { printCommon(E, "bridge_from_objc_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitBridgeToObjCExpr(BridgeToObjCExpr *E, StringRef label) { + void visitBridgeToObjCExpr(BridgeToObjCExpr *E, Label label) { printCommon(E, "bridge_to_objc_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitLoadExpr(LoadExpr *E, StringRef label) { + void visitLoadExpr(LoadExpr *E, Label label) { printCommon(E, "load_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitABISafeConversionExpr(ABISafeConversionExpr *E, StringRef label) { + void visitABISafeConversionExpr(ABISafeConversionExpr *E, Label label) { printCommon(E, "abi_safe_conversion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitMetatypeConversionExpr(MetatypeConversionExpr *E, StringRef label) { + void visitMetatypeConversionExpr(MetatypeConversionExpr *E, Label label) { printCommon(E, "metatype_conversion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitCollectionUpcastConversionExpr(CollectionUpcastConversionExpr *E, StringRef label) { + void visitCollectionUpcastConversionExpr(CollectionUpcastConversionExpr *E, + Label label) { printCommon(E, "collection_upcast_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); if (auto keyConversion = E->getKeyConversion()) { - printRec(keyConversion.Conversion, "key_conversion"); + printRec(keyConversion.Conversion, Label::always("key_conversion")); } if (auto valueConversion = E->getValueConversion()) { - printRec(valueConversion.Conversion, "value_conversion"); + printRec(valueConversion.Conversion, Label::always("value_conversion")); } printFoot(); } - void visitDerivedToBaseExpr(DerivedToBaseExpr *E, StringRef label) { + void visitDerivedToBaseExpr(DerivedToBaseExpr *E, Label label) { printCommon(E, "derived_to_base_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, StringRef label) { + void visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, Label label) { printCommon(E, "archetype_to_super_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitInjectIntoOptionalExpr(InjectIntoOptionalExpr *E, StringRef label) { + void visitInjectIntoOptionalExpr(InjectIntoOptionalExpr *E, Label label) { printCommon(E, "inject_into_optional", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitClassMetatypeToObjectExpr(ClassMetatypeToObjectExpr *E, StringRef label) { + void visitClassMetatypeToObjectExpr(ClassMetatypeToObjectExpr *E, + Label label) { printCommon(E, "class_metatype_to_object", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitExistentialMetatypeToObjectExpr(ExistentialMetatypeToObjectExpr *E, StringRef label) { + void visitExistentialMetatypeToObjectExpr(ExistentialMetatypeToObjectExpr *E, + Label label) { printCommon(E, "existential_metatype_to_object", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitProtocolMetatypeToObjectExpr(ProtocolMetatypeToObjectExpr *E, StringRef label) { + void visitProtocolMetatypeToObjectExpr(ProtocolMetatypeToObjectExpr *E, + Label label) { printCommon(E, "protocol_metatype_to_object", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitInOutToPointerExpr(InOutToPointerExpr *E, StringRef label) { + void visitInOutToPointerExpr(InOutToPointerExpr *E, Label label) { printCommon(E, "inout_to_pointer", label); printFlag(E->isNonAccessing(), "nonaccessing"); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitArrayToPointerExpr(ArrayToPointerExpr *E, StringRef label) { + void visitArrayToPointerExpr(ArrayToPointerExpr *E, Label label) { printCommon(E, "array_to_pointer", label); printFlag(E->isNonAccessing(), "nonaccessing"); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitStringToPointerExpr(StringToPointerExpr *E, StringRef label) { + void visitStringToPointerExpr(StringToPointerExpr *E, Label label) { printCommon(E, "string_to_pointer", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitPointerToPointerExpr(PointerToPointerExpr *E, StringRef label) { + void visitPointerToPointerExpr(PointerToPointerExpr *E, Label label) { printCommon(E, "pointer_to_pointer", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitForeignObjectConversionExpr(ForeignObjectConversionExpr *E, StringRef label) { + void visitForeignObjectConversionExpr(ForeignObjectConversionExpr *E, + Label label) { printCommon(E, "foreign_object_conversion", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitUnevaluatedInstanceExpr(UnevaluatedInstanceExpr *E, StringRef label) { + void visitUnevaluatedInstanceExpr(UnevaluatedInstanceExpr *E, Label label) { printCommon(E, "unevaluated_instance", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitUnreachableExpr(UnreachableExpr *E, StringRef label) { + void visitUnreachableExpr(UnreachableExpr *E, Label label) { printCommon(E, "unreachable", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitDifferentiableFunctionExpr(DifferentiableFunctionExpr *E, StringRef label) { + void visitDifferentiableFunctionExpr(DifferentiableFunctionExpr *E, + Label label) { printCommon(E, "differentiable_function", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitLinearFunctionExpr(LinearFunctionExpr *E, StringRef label) { + void visitLinearFunctionExpr(LinearFunctionExpr *E, Label label) { printCommon(E, "linear_function", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } void visitDifferentiableFunctionExtractOriginalExpr( - DifferentiableFunctionExtractOriginalExpr *E, StringRef label) { + DifferentiableFunctionExtractOriginalExpr *E, Label label) { printCommon(E, "differentiable_function_extract_original", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } void visitLinearFunctionExtractOriginalExpr( - LinearFunctionExtractOriginalExpr *E, StringRef label) { + LinearFunctionExtractOriginalExpr *E, Label label) { printCommon(E, "linear_function_extract_original", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } void visitLinearToDifferentiableFunctionExpr( - LinearToDifferentiableFunctionExpr *E, StringRef label) { + LinearToDifferentiableFunctionExpr *E, Label label) { printCommon(E, "linear_to_differentiable_function", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } void visitActorIsolationErasureExpr(ActorIsolationErasureExpr *E, - StringRef label) { + Label label) { printCommon(E, "actor_isolation_erasure_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitUnsafeCastExpr(UnsafeCastExpr *E, StringRef label) { + void visitUnsafeCastExpr(UnsafeCastExpr *E, Label label) { printCommon(E, "unsafe_cast_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } void visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *E, - StringRef label) { + Label label) { printCommon(E, "extract_function_isolation", label); - printRec(E->getFunctionExpr()); + printRec(E->getFunctionExpr(), Label::optional("function_expr")); printFoot(); } - void visitInOutExpr(InOutExpr *E, StringRef label) { + void visitInOutExpr(InOutExpr *E, Label label) { printCommon(E, "inout_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitVarargExpansionExpr(VarargExpansionExpr *E, StringRef label) { + void visitVarargExpansionExpr(VarargExpansionExpr *E, Label label) { printCommon(E, "vararg_expansion_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitPackExpansionExpr(PackExpansionExpr *E, StringRef label) { + void visitPackExpansionExpr(PackExpansionExpr *E, Label label) { printCommon(E, "pack_expansion_expr", label); - printRec(E->getPatternExpr()); + printRec(E->getPatternExpr(), Label::optional("pattern_expr")); printFoot(); } - void visitPackElementExpr(PackElementExpr *E, StringRef label) { + void visitPackElementExpr(PackElementExpr *E, Label label) { printCommon(E, "pack_element_expr", label); - printRec(E->getPackRefExpr()); + printRec(E->getPackRefExpr(), Label::optional("pack_ref_expr")); printFoot(); } - void visitMaterializePackExpr(MaterializePackExpr *E, StringRef label) { + void visitMaterializePackExpr(MaterializePackExpr *E, Label label) { printCommon(E, "materialize_pack_expr", label); - printRec(E->getFromExpr()); + printRec(E->getFromExpr(), Label::optional("from_expr")); printFoot(); } - void visitForceTryExpr(ForceTryExpr *E, StringRef label) { + void visitForceTryExpr(ForceTryExpr *E, Label label) { printCommon(E, "force_try_expr", label); PrintOptions PO; PO.PrintTypesForDebugging = true; - printFieldQuoted(E->getThrownError().getString(PO), "thrown_error", TypeColor); + printTypeField(E->getThrownError(), Label::always("thrown_error"), PO, + TypeColor); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitOptionalTryExpr(OptionalTryExpr *E, StringRef label) { + void visitOptionalTryExpr(OptionalTryExpr *E, Label label) { printCommon(E, "optional_try_expr", label); PrintOptions PO; PO.PrintTypesForDebugging = true; - printFieldQuoted(E->getThrownError().getString(PO), "thrown_error", TypeColor); + printTypeField(E->getThrownError(), Label::always("thrown_error"), PO, + TypeColor); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitTryExpr(TryExpr *E, StringRef label) { + void visitTryExpr(TryExpr *E, Label label) { printCommon(E, "try_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitSequenceExpr(SequenceExpr *E, StringRef label) { + void visitSequenceExpr(SequenceExpr *E, Label label) { printCommon(E, "sequence_expr", label); - for (unsigned i = 0, e = E->getNumElements(); i != e; ++i) { - printRec(E->getElement(i)); - } + printList(range(E->getNumElements()), [&](unsigned i, Label label) { + printRec(E->getElement(i), label); + }, Label::optional("elements")); printFoot(); } - void visitCaptureListExpr(CaptureListExpr *E, StringRef label) { + void visitCaptureListExpr(CaptureListExpr *E, Label label) { printCommon(E, "capture_list", label); - for (auto capture : E->getCaptureList()) { - printRec(capture.PBD); - } - printRec(E->getClosureBody()); + printList(E->getCaptureList(), [&](auto capture, Label label) { + printRec(capture.PBD, label); + }, Label::optional("captures")); + printRec(E->getClosureBody(), Label::optional("closure_body")); printFoot(); } void printClosure(AbstractClosureExpr *E, char const *name, - StringRef label) { + Label label) { printCommon(E, name, label); // If we aren't printing to standard error or the debugger output stream, @@ -2824,7 +3785,8 @@ class PrintExpr : public ExprVisitor, if (hasNonStandardOutput()) (void)E->getDiscriminator(); - printField(E->getRawDiscriminator(), "discriminator", DiscriminatorColor); + printField(E->getRawDiscriminator(), Label::always("discriminator"), + DiscriminatorColor); switch (auto isolation = E->getActorIsolation()) { case ActorIsolation::Unspecified: @@ -2840,22 +3802,20 @@ class PrintExpr : public ExprVisitor, break; case ActorIsolation::ActorInstance: - printFieldQuoted(isolation.getActorInstance()->printRef(), - "actor_isolated", CapturesColor); + printReferencedDeclWithContextField(isolation.getActorInstance(), + Label::always("actor_isolated"), + CapturesColor); break; case ActorIsolation::GlobalActor: - printFieldQuoted(isolation.getGlobalActor().getString(), - "global_actor_isolated", CapturesColor); + printTypeField(isolation.getGlobalActor(), + Label::always("global_actor_isolated"), PrintOptions(), + CapturesColor); break; } if (auto captureInfo = E->getCachedCaptureInfo()) { - if (!captureInfo->isTrivial()) { - printFieldRaw([&](raw_ostream &OS) { - captureInfo->print(OS); - }, "", CapturesColor); - } + printCaptureInfoField(captureInfo, Label::optional("captures")); } // Printing a function type doesn't indicate whether it's escaping because it doesn't // matter in 99% of contexts. AbstractClosureExpr nodes are one of the only exceptions. @@ -2869,7 +3829,7 @@ class PrintExpr : public ExprVisitor, } } - void visitClosureExpr(ClosureExpr *E, StringRef label) { + void visitClosureExpr(ClosureExpr *E, Label label) { printClosure(E, "closure_expr", label); printFlag(E->hasSingleExpressionBody(), "single_expression", ClosureModifierColor); @@ -2879,59 +3839,65 @@ class PrintExpr : public ExprVisitor, ClosureModifierColor); if (E->getParameters()) { - printRec(E->getParameters(), &E->getASTContext()); + printRec(E->getParameters(), Label::optional("params"), + &E->getASTContext()); } - printRec(E->getBody(), &E->getASTContext()); + printRec(E->getBody(), &E->getASTContext(), Label::optional("body")); printFoot(); } - void visitAutoClosureExpr(AutoClosureExpr *E, StringRef label) { + void visitAutoClosureExpr(AutoClosureExpr *E, Label label) { printClosure(E, "autoclosure_expr", label); if (E->getParameters()) { - printRec(E->getParameters(), &E->getASTContext()); + printRec(E->getParameters(), Label::optional("params"), + &E->getASTContext()); } - printRec(E->getSingleExpressionBody()); + printRec(E->getSingleExpressionBody(), Label::optional("single_expr_body")); printFoot(); } - void visitDynamicTypeExpr(DynamicTypeExpr *E, StringRef label) { + void visitDynamicTypeExpr(DynamicTypeExpr *E, Label label) { printCommon(E, "metatype_expr", label); - printRec(E->getBase()); + printRec(E->getBase(), Label::optional("base")); printFoot(); } - void visitOpaqueValueExpr(OpaqueValueExpr *E, StringRef label) { + void visitOpaqueValueExpr(OpaqueValueExpr *E, Label label) { printCommon(E, "opaque_value_expr", label); - printNameRaw([&](raw_ostream &OS) { OS << (void*)E; }); + printNameRaw([&](raw_ostream &OS) { OS << (void*)E; }, + Label::optional("identity")); printFoot(); } void visitPropertyWrapperValuePlaceholderExpr( - PropertyWrapperValuePlaceholderExpr *E, StringRef label) { + PropertyWrapperValuePlaceholderExpr *E, Label label) { printCommon(E, "property_wrapper_value_placeholder_expr", label); - printRec(E->getOpaqueValuePlaceholder()); + printRec(E->getOpaqueValuePlaceholder(), + Label::optional("opaque_value_placeholder")); if (auto *value = E->getOriginalWrappedValue()) { - printRec(value); + printRec(value, Label::optional("original_wrapped_value")); } printFoot(); } - void visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *E, StringRef label) { + void visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *E, + Label label) { printCommon(E, "applied_property_wrapper_expr", label); - printRec(E->getValue()); + printRec(E->getValue(), Label::optional("value")); printFoot(); } - void visitDefaultArgumentExpr(DefaultArgumentExpr *E, StringRef label) { + void visitDefaultArgumentExpr(DefaultArgumentExpr *E, Label label) { printCommon(E, "default_argument_expr", label); - printDeclRefField(E->getDefaultArgsOwner(), "default_args_owner"); - printField(E->getParamIndex(), "param"); + printDeclRefField(E->getDefaultArgsOwner(), + Label::always("default_args_owner")); + printField(E->getParamIndex(), Label::always("param")); printFoot(); } - void printApplyExpr(ApplyExpr *E, const char *NodeName, StringRef label) { + void printApplyExpr(ApplyExpr *E, const char *NodeName, Label label) { printCommon(E, NodeName, label); if (E->isThrowsSet()) { printThrowDest(E->throws(), /*wantNothrow=*/true); @@ -2947,333 +3913,352 @@ class PrintExpr : public ExprVisitor, } else { OS << "none"; } - }, "isolation_crossing", ExprModifierColor); + }, Label::always("isolation_crossing"), ExprModifierColor); - printRec(E->getFn()); - printRec(E->getArgs()); + printRec(E->getFn(), Label::optional("fn")); + printRec(E->getArgs(), Label::optional("args")); printFoot(); } - void visitCallExpr(CallExpr *E, StringRef label) { + void visitCallExpr(CallExpr *E, Label label) { printApplyExpr(E, "call_expr", label); } - void visitPrefixUnaryExpr(PrefixUnaryExpr *E, StringRef label) { + void visitPrefixUnaryExpr(PrefixUnaryExpr *E, Label label) { printApplyExpr(E, "prefix_unary_expr", label); } - void visitPostfixUnaryExpr(PostfixUnaryExpr *E, StringRef label) { + void visitPostfixUnaryExpr(PostfixUnaryExpr *E, Label label) { printApplyExpr(E, "postfix_unary_expr", label); } - void visitBinaryExpr(BinaryExpr *E, StringRef label) { + void visitBinaryExpr(BinaryExpr *E, Label label) { printApplyExpr(E, "binary_expr", label); } - void visitDotSyntaxCallExpr(DotSyntaxCallExpr *E, StringRef label) { + void visitDotSyntaxCallExpr(DotSyntaxCallExpr *E, Label label) { printApplyExpr(E, "dot_syntax_call_expr", label); } - void visitConstructorRefCallExpr(ConstructorRefCallExpr *E, StringRef label) { + void visitConstructorRefCallExpr(ConstructorRefCallExpr *E, Label label) { printApplyExpr(E, "constructor_ref_call_expr", label); } - void visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *E, StringRef label) { + void visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *E, Label label) { printCommon(E, "dot_syntax_base_ignored", label); - printRec(E->getLHS()); - printRec(E->getRHS()); + printRec(E->getLHS(), Label::optional("lhs")); + printRec(E->getRHS(), Label::optional("rhs")); printFoot(); } - void printExplicitCastExpr(ExplicitCastExpr *E, const char *name, StringRef label) { + void printExplicitCastExpr(ExplicitCastExpr *E, const char *name, + Label label) { printCommon(E, name, label); if (auto checkedCast = dyn_cast(E)) printFlag(getDumpString(checkedCast->getCastKind())); - printFieldQuotedRaw([&](raw_ostream &OS) { - if (GetTypeOfTypeRepr) - GetTypeOfTypeRepr(E->getCastTypeRepr()).print(OS); - else - E->getCastType().print(OS); - }, "written_type", TypeReprColor); + if (GetTypeOfTypeRepr) + printTypeField(GetTypeOfTypeRepr(E->getCastTypeRepr()), + Label::always("written_type")); + else + printTypeField(E->getCastType(), Label::always("written_type")); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitForcedCheckedCastExpr(ForcedCheckedCastExpr *E, StringRef label) { + void visitForcedCheckedCastExpr(ForcedCheckedCastExpr *E, Label label) { printExplicitCastExpr(E, "forced_checked_cast_expr", label); } - void visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *E, StringRef label) { + void visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *E, + Label label) { printExplicitCastExpr(E, "conditional_checked_cast_expr", label); } - void visitIsExpr(IsExpr *E, StringRef label) { + void visitIsExpr(IsExpr *E, Label label) { printExplicitCastExpr(E, "is_subtype_expr", label); } - void visitCoerceExpr(CoerceExpr *E, StringRef label) { + void visitCoerceExpr(CoerceExpr *E, Label label) { printExplicitCastExpr(E, "coerce_expr", label); } - void visitArrowExpr(ArrowExpr *E, StringRef label) { + void visitArrowExpr(ArrowExpr *E, Label label) { printCommon(E, "arrow", label); printFlag(E->getAsyncLoc().isValid(), "async"); printFlag(E->getThrowsLoc().isValid(), "throws"); - printRec(E->getArgsExpr()); - printRec(E->getResultExpr()); + printRec(E->getArgsExpr(), Label::optional("args")); + printRec(E->getResultExpr(), Label::optional("result_expr")); printFoot(); } - void visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E, StringRef label) { + void visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E, + Label label) { printCommon(E, "rebind_self_in_constructor_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitTernaryExpr(TernaryExpr *E, StringRef label) { + void visitTernaryExpr(TernaryExpr *E, Label label) { printCommon(E, "ternary_expr", label); - printRec(E->getCondExpr()); - printRec(E->getThenExpr()); - printRec(E->getElseExpr()); + printRec(E->getCondExpr(), Label::optional("cond_expr")); + printRec(E->getThenExpr(), Label::optional("then_expr")); + printRec(E->getElseExpr(), Label::optional("else_expr")); printFoot(); } - void visitAssignExpr(AssignExpr *E, StringRef label) { + void visitAssignExpr(AssignExpr *E, Label label) { printCommon(E, "assign_expr", label); - printRec(E->getDest()); - printRec(E->getSrc()); + printRec(E->getDest(), Label::optional("dest")); + printRec(E->getSrc(), Label::optional("src")); printFoot(); } - void visitEnumIsCaseExpr(EnumIsCaseExpr *E, StringRef label) { + void visitEnumIsCaseExpr(EnumIsCaseExpr *E, Label label) { printCommon(E, "enum_is_case_expr", label); - printName(E->getEnumElement()->getBaseIdentifier()); - printRec(E->getSubExpr()); + printName(E->getEnumElement()->getBaseIdentifier(), Label::optional("name")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitUnresolvedPatternExpr(UnresolvedPatternExpr *E, StringRef label) { + void visitUnresolvedPatternExpr(UnresolvedPatternExpr *E, Label label) { printCommon(E, "unresolved_pattern_expr", label); - printRec(E->getSubPattern()); + printRec(E->getSubPattern(), Label::optional("sub_pattern")); printFoot(); } - void visitBindOptionalExpr(BindOptionalExpr *E, StringRef label) { + void visitBindOptionalExpr(BindOptionalExpr *E, Label label) { printCommon(E, "bind_optional_expr", label); - printField(E->getDepth(), "depth"); - printRec(E->getSubExpr()); + printField(E->getDepth(), Label::always("depth")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitOptionalEvaluationExpr(OptionalEvaluationExpr *E, StringRef label) { + void visitOptionalEvaluationExpr(OptionalEvaluationExpr *E, Label label) { printCommon(E, "optional_evaluation_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitForceValueExpr(ForceValueExpr *E, StringRef label) { + void visitForceValueExpr(ForceValueExpr *E, Label label) { printCommon(E, "force_value_expr", label); printFlag(E->isForceOfImplicitlyUnwrappedOptional(), "implicit_iuo_unwrap", ExprModifierColor); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitOpenExistentialExpr(OpenExistentialExpr *E, StringRef label) { + void visitOpenExistentialExpr(OpenExistentialExpr *E, Label label) { printCommon(E, "open_existential_expr", label); - printRec(E->getOpaqueValue()); - printRec(E->getExistentialValue()); - printRec(E->getSubExpr()); + printRec(E->getOpaqueValue(), Label::optional("opaque_value")); + printRec(E->getExistentialValue(), Label::optional("existential_value")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitMakeTemporarilyEscapableExpr(MakeTemporarilyEscapableExpr *E, StringRef label) { + void visitMakeTemporarilyEscapableExpr(MakeTemporarilyEscapableExpr *E, + Label label) { printCommon(E, "make_temporarily_escapable_expr", label); - printRec(E->getOpaqueValue()); - printRec(E->getNonescapingClosureValue()); - printRec(E->getSubExpr()); + printRec(E->getOpaqueValue(), Label::optional("opaque_value")); + printRec(E->getNonescapingClosureValue(), + Label::optional("nonescaping_closure_value")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, StringRef label) { + void visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, Label label) { printCommon(E, "editor_placeholder_expr", label); auto *TyR = E->getPlaceholderTypeRepr(); auto *ExpTyR = E->getTypeForExpansion(); if (TyR) - printRec(TyR); + printRec(TyR, Label::optional("placeholder_type_repr")); if (ExpTyR && ExpTyR != TyR) { - printRec(ExpTyR); + printRec(ExpTyR, Label::optional("type_repr_for_expansion")); + } + if (auto *SE = E->getSemanticExpr()) { + printRec(SE, Label::always("semantic_expr")); } - printSemanticExpr(E->getSemanticExpr()); printFoot(); } - void visitLazyInitializerExpr(LazyInitializerExpr *E, StringRef label) { + void visitLazyInitializerExpr(LazyInitializerExpr *E, Label label) { printCommon(E, "lazy_initializer_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitObjCSelectorExpr(ObjCSelectorExpr *E, StringRef label) { + void visitObjCSelectorExpr(ObjCSelectorExpr *E, Label label) { printCommon(E, "objc_selector_expr", label); - printField(E->getSelectorKind(), "kind"); - printDeclRefField(E->getMethod(), "decl"); + printField(E->getSelectorKind(), Label::always("kind")); + printDeclRefField(E->getMethod(), Label::always("decl")); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitKeyPathExpr(KeyPathExpr *E, StringRef label) { + void visitKeyPathExpr(KeyPathExpr *E, Label label) { printCommon(E, "keypath_expr", label); printFlag(E->isObjC(), "objc"); - printRecArbitrary([&](StringRef label) { - printHead("components", ExprColor, label); - for (unsigned i : indices(E->getComponents())) { + auto printComponents = [&]{ + printList(indices(E->getComponents()), [&](unsigned i, Label label) { auto &component = E->getComponents()[i]; - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { switch (component.getKind()) { case KeyPathExpr::Component::Kind::Invalid: - printHead("invalid", ASTNodeColor); + printHead("invalid", ASTNodeColor, label); break; case KeyPathExpr::Component::Kind::OptionalChain: - printHead("optional_chain", ASTNodeColor); + printHead("optional_chain", ASTNodeColor, label); break; case KeyPathExpr::Component::Kind::OptionalForce: - printHead("optional_force", ASTNodeColor); + printHead("optional_force", ASTNodeColor, label); break; case KeyPathExpr::Component::Kind::OptionalWrap: - printHead("optional_wrap", ASTNodeColor); + printHead("optional_wrap", ASTNodeColor, label); break; case KeyPathExpr::Component::Kind::Property: - printHead("property", ASTNodeColor); - printDeclRefField(component.getDeclRef(), "decl"); + printHead("property", ASTNodeColor, label); + printDeclRefField(component.getDeclRef(), Label::always("decl")); break; case KeyPathExpr::Component::Kind::Subscript: - printHead("subscript", ASTNodeColor); - printDeclRefField(component.getDeclRef(), "decl"); + printHead("subscript", ASTNodeColor, label); + printDeclRefField(component.getDeclRef(), Label::always("decl")); break; case KeyPathExpr::Component::Kind::UnresolvedProperty: - printHead("unresolved_property", ASTNodeColor); - printFieldQuoted(component.getUnresolvedDeclName(), "decl_name", - IdentifierColor); + printHead("unresolved_property", ASTNodeColor, label); + printFieldQuoted(component.getUnresolvedDeclName(), + Label::always("decl_name"), IdentifierColor); break; case KeyPathExpr::Component::Kind::UnresolvedSubscript: - printHead("unresolved_subscript", ASTNodeColor); + printHead("unresolved_subscript", ASTNodeColor, label); break; case KeyPathExpr::Component::Kind::Identity: - printHead("identity", ASTNodeColor); + printHead("identity", ASTNodeColor, label); break; case KeyPathExpr::Component::Kind::TupleElement: - printHead("tuple_element", ASTNodeColor); - printField(component.getTupleIndex(), "index", DiscriminatorColor); + printHead("tuple_element", ASTNodeColor, label); + printField(component.getTupleIndex(), Label::always("index"), + DiscriminatorColor); break; case KeyPathExpr::Component::Kind::DictionaryKey: - printHead("dict_key", ASTNodeColor); - printFieldQuoted(component.getUnresolvedDeclName(), "key", - IdentifierColor); + printHead("dict_key", ASTNodeColor, label); + printFieldQuoted(component.getUnresolvedDeclName(), + Label::always("key"), IdentifierColor); break; case KeyPathExpr::Component::Kind::CodeCompletion: - printHead("completion", ASTNodeColor); + printHead("completion", ASTNodeColor, label); break; } - printFieldQuoted(GetTypeOfKeyPathComponent(E, i), "type"); + printTypeField(GetTypeOfKeyPathComponent(E, i), Label::always("type")); if (auto *args = component.getSubscriptArgs()) { - printRec(args); + printRec(args, Label::optional("subscript_args")); } printFoot(); - }); - } + }, Label::optional("component")); + }, Label::optional("components")); + }; - printFoot(); - }); + if (Writer.isParsable()) { + printComponents(); + } else { + // `printList` in default mode simply indents, so we need to preserve the + // additional level of `(components ...)` for that mode. + printRecArbitrary([&](Label label) { + printHead("components", ExprColor, label); + printComponents(); + printFoot(); + }, Label::optional("components")); + } if (auto stringLiteral = E->getObjCStringLiteralExpr()) { - printRec(stringLiteral, "objc_string_literal"); + printRec(stringLiteral, Label::always("objc_string_literal")); } if (!E->isObjC()) { if (auto root = E->getParsedRoot()) { - printRec(root, "parsed_root"); + printRec(root, Label::always("parsed_root")); } if (auto path = E->getParsedPath()) { - printRec(path, "parsed_path"); + printRec(path, Label::always("parsed_path")); } } printFoot(); } void visitCurrentContextIsolationExpr( - CurrentContextIsolationExpr *E, StringRef label) { + CurrentContextIsolationExpr *E, Label label) { printCommon(E, "current_context_isolation_expr", label); if (auto actor = E->getActor()) - printRec(actor); + printRec(actor, Label::optional("actor")); printFoot(); } - void visitKeyPathDotExpr(KeyPathDotExpr *E, StringRef label) { + void visitKeyPathDotExpr(KeyPathDotExpr *E, Label label) { printCommon(E, "key_path_dot_expr", label); printFoot(); } - void visitSingleValueStmtExpr(SingleValueStmtExpr *E, StringRef label) { + void visitSingleValueStmtExpr(SingleValueStmtExpr *E, Label label) { printCommon(E, "single_value_stmt_expr", label); - printRec(E->getStmt(), &E->getDeclContext()->getASTContext()); + printRec(E->getStmt(), &E->getDeclContext()->getASTContext(), + Label::optional("stmt")); printFoot(); } - void visitOneWayExpr(OneWayExpr *E, StringRef label) { + void visitOneWayExpr(OneWayExpr *E, Label label) { printCommon(E, "one_way_expr", label); - printRec(E->getSubExpr()); + printRec(E->getSubExpr(), Label::optional("sub_expr")); printFoot(); } - void visitTapExpr(TapExpr *E, StringRef label) { + void visitTapExpr(TapExpr *E, Label label) { printCommon(E, "tap_expr", label); - printDeclRefField(E->getVar(), "var"); - printRec(E->getSubExpr()); - printRec(E->getBody(), &E->getVar()->getDeclContext()->getASTContext()); + printDeclRefField(E->getVar(), Label::always("var")); + printRec(E->getSubExpr(), Label::optional("sub_expr")); + printRec(E->getBody(), &E->getVar()->getDeclContext()->getASTContext(), + Label::optional("body")); printFoot(); } - void visitTypeJoinExpr(TypeJoinExpr *E, StringRef label) { + void visitTypeJoinExpr(TypeJoinExpr *E, Label label) { printCommon(E, "type_join_expr", label); if (auto *var = E->getVar()) { - printRec(var, "var"); + printRec(var, Label::always("var")); } if (auto *SVE = E->getSingleValueStmtExpr()) { - printRec(SVE, "single_value_stmt_expr"); + printRec(SVE, Label::always("single_value_stmt_expr")); } - for (auto *member : E->getElements()) { - printRec(member); - } + printList(E->getElements(), [&](auto *member, Label label) { + printRec(member, label); + }, Label::optional("elements")); printFoot(); } - void visitMacroExpansionExpr(MacroExpansionExpr *E, StringRef label) { + void visitMacroExpansionExpr(MacroExpansionExpr *E, Label label) { printCommon(E, "macro_expansion_expr", label); - printFieldQuoted(E->getMacroName(), "name", IdentifierColor); - printField(E->getRawDiscriminator(), "discriminator", DiscriminatorColor); + printFieldQuoted(E->getMacroName(), Label::always("name"), IdentifierColor); + printField(E->getRawDiscriminator(), Label::always("discriminator"), + DiscriminatorColor); if (E->getArgs()) { - printRec(E->getArgs()); + printRec(E->getArgs(), Label::optional("args")); } if (auto rewritten = E->getRewritten()) { - printRec(rewritten, "rewritten"); + printRec(rewritten, Label::always("rewritten")); } printFoot(); } - void visitTypeValueExpr(TypeValueExpr *E, StringRef label) { + void visitTypeValueExpr(TypeValueExpr *E, Label label) { printCommon(E, "type_value_expr", label); PrintOptions PO; PO.PrintTypesForDebugging = true; - printFieldQuoted(Type(E->getParamType()).getString(PO), "param_type", - TypeColor); + printTypeField(E->getParamType(), Label::always("param_type"), PO, + TypeColor); printFoot(); } @@ -3291,9 +4276,10 @@ void Expr::dump(raw_ostream &OS, llvm::function_ref getTypeOfExpr, llvm::function_ref getTypeOfKeyPathComponent, unsigned Indent) const { - PrintExpr(OS, Indent, /*parseIfNeeded*/ false, getTypeOfExpr, + DefaultWriter writer(OS, Indent); + PrintExpr(writer, /*parseIfNeeded*/ false, getTypeOfExpr, getTypeOfTypeRepr, getTypeOfKeyPathComponent) - .visit(const_cast(this), ""); + .visit(const_cast(this), Label::optional("")); } void Expr::dump(raw_ostream &OS, unsigned Indent) const { @@ -3315,7 +4301,8 @@ void ArgumentList::dump() const { } void ArgumentList::dump(raw_ostream &OS, unsigned Indent) const { - PrintBase(OS, Indent).visitArgumentList(this); + DefaultWriter writer(OS, Indent); + PrintBase(writer).visitArgumentList(this, Label::optional("")); } //===----------------------------------------------------------------------===// @@ -3323,104 +4310,107 @@ void ArgumentList::dump(raw_ostream &OS, unsigned Indent) const { //===----------------------------------------------------------------------===// namespace { -class PrintTypeRepr : public TypeReprVisitor, +class PrintTypeRepr : public TypeReprVisitor, public PrintBase { public: using PrintBase::PrintBase; - void printCommon(const char *Name, StringRef Label) { - printHead(Name, TypeReprColor, Label); + void printCommon(const char *Name, Label label) { + printHead(Name, TypeReprColor, label); } - void visitErrorTypeRepr(ErrorTypeRepr *T, StringRef label) { + void visitErrorTypeRepr(ErrorTypeRepr *T, Label label) { printCommon("type_error", label); } - void visitAttributedTypeRepr(AttributedTypeRepr *T, StringRef label) { + void visitAttributedTypeRepr(AttributedTypeRepr *T, Label label) { printCommon("type_attributed", label); - printFieldQuotedRaw([&](raw_ostream &OS) { T->printAttrs(OS); }, "attrs"); - printRec(T->getTypeRepr()); + printFieldQuotedRaw([&](raw_ostream &OS) { T->printAttrs(OS); }, + Label::always("attrs")); + printRec(T->getTypeRepr(), Label::optional("type_repr")); } - void visitDeclRefTypeRepr(DeclRefTypeRepr *T, StringRef label) { + void visitDeclRefTypeRepr(DeclRefTypeRepr *T, Label label) { printCommon(isa(T) ? "type_unqualified_ident" : "type_qualified_ident", label); - printFieldQuoted(T->getNameRef(), "id", IdentifierColor); - if (T->isBound()) - printFieldQuoted(T->getBoundDecl()->printRef(), "bind"); - else + printFieldQuoted(T->getNameRef(), Label::always("id"), IdentifierColor); + if (T->isBound()) { + printReferencedDeclWithContextField(T->getBoundDecl(), Label::always("bind")); + } else { printFlag("unbound"); + } if (auto *qualIdentTR = dyn_cast(T)) { - printRec(qualIdentTR->getBase()); + printRec(qualIdentTR->getBase(), Label::optional("base")); } - for (auto *genArg : T->getGenericArgs()) { - printRec(genArg); - } + printList(T->getGenericArgs(), [&](auto *genArg, Label label) { + printRec(genArg, label); + }, Label::optional("generic_args")); printFoot(); } - void visitFunctionTypeRepr(FunctionTypeRepr *T, StringRef label) { + void visitFunctionTypeRepr(FunctionTypeRepr *T, Label label) { printCommon("type_function", label); printFlag(T->isAsync(), "async"); printFlag(T->isThrowing(), "throws"); - printRec(T->getArgsTypeRepr()); - printRec(T->getResultTypeRepr()); + printRec(T->getArgsTypeRepr(), Label::optional("args_type_repr")); + printRec(T->getResultTypeRepr(), Label::optional("result_type_repr")); printFoot(); } - void visitInverseTypeRepr(InverseTypeRepr *T, StringRef label) { + void visitInverseTypeRepr(InverseTypeRepr *T, Label label) { printCommon("inverse", label); - printRec(T->getConstraint()); + printRec(T->getConstraint(), Label::optional("constraint")); printFoot(); } - void visitArrayTypeRepr(ArrayTypeRepr *T, StringRef label) { + void visitArrayTypeRepr(ArrayTypeRepr *T, Label label) { printCommon("type_array", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("element_type")); printFoot(); } - void visitDictionaryTypeRepr(DictionaryTypeRepr *T, StringRef label) { + void visitDictionaryTypeRepr(DictionaryTypeRepr *T, Label label) { printCommon("type_dictionary", label); - printRec(T->getKey()); - printRec(T->getValue()); + printRec(T->getKey(), Label::optional("key")); + printRec(T->getValue(), Label::optional("value")); printFoot(); } - void visitVarargTypeRepr(VarargTypeRepr *T, StringRef label) { + void visitVarargTypeRepr(VarargTypeRepr *T, Label label) { printCommon("vararg", label); - printRec(T->getElementType()); + printRec(T->getElementType(), Label::optional("element_type")); printFoot(); } - void visitPackTypeRepr(PackTypeRepr *T, StringRef label) { + void visitPackTypeRepr(PackTypeRepr *T, Label label) { printCommon("pack", label); - for (auto elt : T->getElements()) - printRec(elt); + printList(T->getElements(), [&](auto elt, Label label) { + printRec(elt, label); + }, Label::optional("elements")); printFoot(); } - void visitPackExpansionTypeRepr(PackExpansionTypeRepr *T, StringRef label) { + void visitPackExpansionTypeRepr(PackExpansionTypeRepr *T, Label label) { printCommon("pack_expansion", label); - printRec(T->getPatternType()); + printRec(T->getPatternType(), Label::optional("pattern_type")); printFoot(); } - void visitPackElementTypeRepr(PackElementTypeRepr *T, StringRef label) { + void visitPackElementTypeRepr(PackElementTypeRepr *T, Label label) { printCommon("pack_element", label); - printRec(T->getPackType()); + printRec(T->getPackType(), Label::optional("pack_type")); printFoot(); } - void visitTupleTypeRepr(TupleTypeRepr *T, StringRef label) { + void visitTupleTypeRepr(TupleTypeRepr *T, Label label) { printCommon("type_tuple", label); if (T->hasElementNames()) { @@ -3433,98 +4423,99 @@ class PrintTypeRepr : public TypeReprVisitor, else OS << (name.empty() ? "''" : name.str()); }, ","); - }, "names"); + }, Label::always("names")); } - for (auto elem : T->getElements()) { - printRec(elem.Type); - } + printList(T->getElements(), [&](auto elem, Label label) { + printRec(elem.Type, label); + }, Label::optional("elements")); printFoot(); } - void visitCompositionTypeRepr(CompositionTypeRepr *T, StringRef label) { + void visitCompositionTypeRepr(CompositionTypeRepr *T, Label label) { printCommon("type_composite", label); - for (auto elem : T->getTypes()) { - printRec(elem); - } + printList(T->getTypes(), [&](auto elem, Label label) { + printRec(elem, label); + }, Label::optional("types")); printFoot(); } - void visitMetatypeTypeRepr(MetatypeTypeRepr *T, StringRef label) { + void visitMetatypeTypeRepr(MetatypeTypeRepr *T, Label label) { printCommon("type_metatype", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitProtocolTypeRepr(ProtocolTypeRepr *T, StringRef label) { + void visitProtocolTypeRepr(ProtocolTypeRepr *T, Label label) { printCommon("type_protocol", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitOwnershipTypeRepr(OwnershipTypeRepr *T, StringRef label) { + void visitOwnershipTypeRepr(OwnershipTypeRepr *T, Label label) { printCommon("type_ownership", label); printFlag(getDumpString(T->getSpecifier())); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitIsolatedTypeRepr(IsolatedTypeRepr *T, StringRef label) { + void visitIsolatedTypeRepr(IsolatedTypeRepr *T, Label label) { printCommon("isolated", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitSendingTypeRepr(SendingTypeRepr *T, StringRef label) { + void visitSendingTypeRepr(SendingTypeRepr *T, Label label) { printCommon("sending", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitCompileTimeConstTypeRepr(CompileTimeConstTypeRepr *T, StringRef label) { + void visitCompileTimeConstTypeRepr(CompileTimeConstTypeRepr *T, Label label) { printCommon("_const", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitOptionalTypeRepr(OptionalTypeRepr *T, StringRef label) { + void visitOptionalTypeRepr(OptionalTypeRepr *T, Label label) { printCommon("type_optional", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } void visitImplicitlyUnwrappedOptionalTypeRepr( - ImplicitlyUnwrappedOptionalTypeRepr *T, StringRef label) { + ImplicitlyUnwrappedOptionalTypeRepr *T, Label label) { printCommon("type_implicitly_unwrapped_optional", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitOpaqueReturnTypeRepr(OpaqueReturnTypeRepr *T, StringRef label) { + void visitOpaqueReturnTypeRepr(OpaqueReturnTypeRepr *T, Label label) { printCommon("type_opaque_return", label); - printRec(T->getConstraint()); + printRec(T->getConstraint(), Label::optional("constraint")); printFoot(); } - void visitNamedOpaqueReturnTypeRepr(NamedOpaqueReturnTypeRepr *T, StringRef label) { + void visitNamedOpaqueReturnTypeRepr(NamedOpaqueReturnTypeRepr *T, + Label label) { printCommon("type_named_opaque_return", label); - printRec(T->getBase()); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitExistentialTypeRepr(ExistentialTypeRepr *T, StringRef label) { + void visitExistentialTypeRepr(ExistentialTypeRepr *T, Label label) { printCommon("type_existential", label); - printRec(T->getConstraint()); + printRec(T->getConstraint(), Label::optional("constraint")); printFoot(); } - void visitPlaceholderTypeRepr(PlaceholderTypeRepr *T, StringRef label) { + void visitPlaceholderTypeRepr(PlaceholderTypeRepr *T, Label label) { printCommon("type_placeholder", label); printFoot(); } - void visitFixedTypeRepr(FixedTypeRepr *T, StringRef label) { + void visitFixedTypeRepr(FixedTypeRepr *T, Label label) { printCommon("type_fixed", label); auto Ty = T->getType(); @@ -3532,12 +4523,12 @@ class PrintTypeRepr : public TypeReprVisitor, printSourceLoc(T->getLoc(), &Ty->getASTContext()); } - printRec(Ty, "type"); + printRec(Ty, Label::always("type")); printFoot(); } - void visitSelfTypeRepr(SelfTypeRepr *T, StringRef label) { + void visitSelfTypeRepr(SelfTypeRepr *T, Label label) { printCommon("type_self", label); auto Ty = T->getType(); @@ -3545,125 +4536,701 @@ class PrintTypeRepr : public TypeReprVisitor, printSourceLoc(T->getLoc(), &Ty->getASTContext()); } - printRec(Ty, "type"); + printRec(Ty, Label::always("type")); printFoot(); } - void visitSILBoxTypeRepr(SILBoxTypeRepr *T, StringRef label) { + void visitSILBoxTypeRepr(SILBoxTypeRepr *T, Label label) { printCommon("sil_box", label); - for (auto &Field : T->getFields()) { - printRecArbitrary([&](StringRef label) { + printList(T->getFields(), [&](auto &Field, Label label) { + printRecArbitrary([&](Label label) { printCommon("sil_box_field", label); printFlag(Field.isMutable(), "mutable"); - printRec(Field.getFieldType()); + printRec(Field.getFieldType(), Label::optional("field_type")); printFoot(); - }); - } + }, label); + }, Label::optional("fields")); - for (auto arg : T->getGenericArguments()) - printRec(arg); + printList(T->getGenericArguments(), [&](auto arg, Label label) { + printRec(arg, label); + }, Label::optional("generic_args")); printFoot(); } void visitLifetimeDependentTypeRepr(LifetimeDependentTypeRepr *T, - StringRef label) { + Label label) { printCommon("type_lifetime_dependent_return", label); + // FIXME: Improve lifetime entries in parsable output formats. printFieldRaw( [&](raw_ostream &out) { out << " " << T->getLifetimeEntry()->getString() << " "; }, - ""); - printRec(T->getBase()); + Label::optional("lifetime_entry")); + printRec(T->getBase(), Label::optional("base")); printFoot(); } - void visitIntegerTypeRepr(IntegerTypeRepr *T, StringRef label) { + void visitIntegerTypeRepr(IntegerTypeRepr *T, Label label) { printCommon("type_integer", label); if (T->getMinusLoc()) { printCommon("is_negative", label); } - printFieldQuoted(T->getValue(), "value", IdentifierColor); + printFieldQuoted(T->getValue(), Label::always("value"), IdentifierColor); + printFoot(); + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Dumping for DeclAttributes +//===----------------------------------------------------------------------===// + +namespace { +class PrintAttribute : public AttributeVisitor, + public PrintBase { + const ASTContext *Ctx; + DeclContext *DC; + +public: + PrintAttribute( + PrintWriterBase &writer, const ASTContext *ctx, DeclContext *dc, + bool parseIfNeeded = false, + llvm::function_ref getTypeOfExpr = defaultGetTypeOfExpr, + llvm::function_ref getTypeOfTypeRepr = nullptr, + llvm::function_ref + getTypeOfKeyPathComponent = defaultGetTypeOfKeyPathComponent) + : PrintBase(writer, parseIfNeeded, getTypeOfExpr, getTypeOfTypeRepr, + getTypeOfKeyPathComponent), + Ctx(ctx), DC(dc) {} + + void printCommon(DeclAttribute *Attr, StringRef name, Label label) { + printHead(name, DeclAttributeColor, label); + printFlag(Attr->isImplicit(), "implicit"); + printFlag(Attr->isInvalid(), "invalid"); + printFlag(Attr->getAddedByAccessNote(), "added_by_access_note"); + printSourceRange(Attr->Range, Ctx); + } + + /// Deleting this ensures that all attributes are covered by the + /// visitor below. + void visitDeclAttribute(DeclAttribute *A) = delete; + +#define TRIVIAL_ATTR_PRINTER(Class, Name) \ + void visit##Class##Attr(Class##Attr *Attr, Label label) { \ + printCommon(Attr, #Name "_attr", label); \ + printFoot(); \ + } + + TRIVIAL_ATTR_PRINTER(Actor, actor) + TRIVIAL_ATTR_PRINTER(AlwaysEmitConformanceMetadata, + always_emit_conformance_metadata) + TRIVIAL_ATTR_PRINTER(AlwaysEmitIntoClient, always_emit_into_client) + TRIVIAL_ATTR_PRINTER(Async, async) + TRIVIAL_ATTR_PRINTER(AtReasync, at_reasync) + TRIVIAL_ATTR_PRINTER(AtRethrows, at_rethrows) + TRIVIAL_ATTR_PRINTER(Borrowed, borrowed) + TRIVIAL_ATTR_PRINTER(Borrowing, borrowing) + TRIVIAL_ATTR_PRINTER(CompileTimeConst, compile_time_const) + TRIVIAL_ATTR_PRINTER(CompilerInitialized, compiler_initialized) + TRIVIAL_ATTR_PRINTER(Consuming, consuming) + TRIVIAL_ATTR_PRINTER(Convenience, convenience) + TRIVIAL_ATTR_PRINTER(DiscardableResult, discardable_result) + TRIVIAL_ATTR_PRINTER(DisfavoredOverload, disfavored_overload) + TRIVIAL_ATTR_PRINTER(DistributedActor, distributed_actor) + TRIVIAL_ATTR_PRINTER(Dynamic, dynamic) + TRIVIAL_ATTR_PRINTER(DynamicCallable, dynamic_callable) + TRIVIAL_ATTR_PRINTER(DynamicMemberLookup, dynamic_member_lookup) + TRIVIAL_ATTR_PRINTER(EagerMove, eager_move) + TRIVIAL_ATTR_PRINTER(EmitAssemblyVisionRemarks, emit_assembly_vision_remarks) + TRIVIAL_ATTR_PRINTER(Exported, exported) + TRIVIAL_ATTR_PRINTER(ExtractConstantsFromMembers, + extract_constants_from_members) + TRIVIAL_ATTR_PRINTER(Final, final) + TRIVIAL_ATTR_PRINTER(FixedLayout, fixed_layout) + TRIVIAL_ATTR_PRINTER(ForbidSerializingReference, forbid_serializing_reference) + TRIVIAL_ATTR_PRINTER(Frozen, frozen) + TRIVIAL_ATTR_PRINTER(GKInspectable, gk_inspectable) + TRIVIAL_ATTR_PRINTER(GlobalActor, global_actor) + TRIVIAL_ATTR_PRINTER(HasInitialValue, has_initial_value) + TRIVIAL_ATTR_PRINTER(HasMissingDesignatedInitializers, + has_missing_designated_initializers) + TRIVIAL_ATTR_PRINTER(HasStorage, has_storage) + TRIVIAL_ATTR_PRINTER(IBAction, ib_action) + TRIVIAL_ATTR_PRINTER(IBDesignable, ib_designable) + TRIVIAL_ATTR_PRINTER(IBInspectable, ib_inspectable) + TRIVIAL_ATTR_PRINTER(IBOutlet, ib_outlet) + TRIVIAL_ATTR_PRINTER(IBSegueAction, ib_segue_action) + TRIVIAL_ATTR_PRINTER(ImplementationOnly, implementation_only) + TRIVIAL_ATTR_PRINTER(ImplicitSelfCapture, implicit_self_capture) + TRIVIAL_ATTR_PRINTER(Indirect, indirect) + TRIVIAL_ATTR_PRINTER(Infix, infix) + TRIVIAL_ATTR_PRINTER(InheritActorContext, inherit_actor_context) + TRIVIAL_ATTR_PRINTER(InheritsConvenienceInitializers, + inherits_convenience_initializers) + TRIVIAL_ATTR_PRINTER(Inlinable, inlinable) + TRIVIAL_ATTR_PRINTER(Isolated, isolated) + TRIVIAL_ATTR_PRINTER(KnownToBeLocal, known_to_be_local) + TRIVIAL_ATTR_PRINTER(LLDBDebuggerFunction, lldb_debugger_function) + TRIVIAL_ATTR_PRINTER(Lazy, lazy) + TRIVIAL_ATTR_PRINTER(LegacyConsuming, legacy_consuming) + TRIVIAL_ATTR_PRINTER(LexicalLifetimes, lexical_lifetimes) + TRIVIAL_ATTR_PRINTER(MainType, main_type) + TRIVIAL_ATTR_PRINTER(Marker, marker) + TRIVIAL_ATTR_PRINTER(MoveOnly, move_only) + TRIVIAL_ATTR_PRINTER(Mutating, mutating) + TRIVIAL_ATTR_PRINTER(NSApplicationMain, ns_application_main) + TRIVIAL_ATTR_PRINTER(NSCopying, ns_copying) + TRIVIAL_ATTR_PRINTER(NSManaged, ns_managed) + TRIVIAL_ATTR_PRINTER(NoAllocation, no_allocation) + TRIVIAL_ATTR_PRINTER(NoDerivative, no_derivative) + TRIVIAL_ATTR_PRINTER(NoEagerMove, no_eager_move) + TRIVIAL_ATTR_PRINTER(NoExistentials, no_existentials) + TRIVIAL_ATTR_PRINTER(NoImplicitCopy, no_implicit_copy) + TRIVIAL_ATTR_PRINTER(NoLocks, no_locks) + TRIVIAL_ATTR_PRINTER(NoMetadata, no_metadata) + TRIVIAL_ATTR_PRINTER(NoObjCBridging, no_objc_bridging) + TRIVIAL_ATTR_PRINTER(NoRuntime, no_runtime) + TRIVIAL_ATTR_PRINTER(NonEphemeral, non_ephemeral) + TRIVIAL_ATTR_PRINTER(NonEscapable, non_escapable) + TRIVIAL_ATTR_PRINTER(NonMutating, non_mutating) + TRIVIAL_ATTR_PRINTER(NonObjC, non_objc) + TRIVIAL_ATTR_PRINTER(NonOverride, non_override) + TRIVIAL_ATTR_PRINTER(ObjCMembers, objc_members) + TRIVIAL_ATTR_PRINTER(ObjCNonLazyRealization, objc_non_lazy_realization) + TRIVIAL_ATTR_PRINTER(Optional, optional) + TRIVIAL_ATTR_PRINTER(Override, override) + TRIVIAL_ATTR_PRINTER(Postfix, postfix) + TRIVIAL_ATTR_PRINTER(PreInverseGenerics, pre_inverse_generics) + TRIVIAL_ATTR_PRINTER(Preconcurrency, preconcurrency) + TRIVIAL_ATTR_PRINTER(Prefix, prefix) + TRIVIAL_ATTR_PRINTER(PropertyWrapper, property_wrapper) + TRIVIAL_ATTR_PRINTER(Reasync, reasync) + TRIVIAL_ATTR_PRINTER(Required, required) + TRIVIAL_ATTR_PRINTER(RequiresStoredPropertyInits, + requires_stored_property_inits) + TRIVIAL_ATTR_PRINTER(ResultBuilder, result_builder) + TRIVIAL_ATTR_PRINTER(Rethrows, rethrows) + TRIVIAL_ATTR_PRINTER(SPIOnly, spi_only) + TRIVIAL_ATTR_PRINTER(Sendable, sendable) + TRIVIAL_ATTR_PRINTER(Sensitive, sensitive) + TRIVIAL_ATTR_PRINTER(ShowInInterface, show_in_interface) + TRIVIAL_ATTR_PRINTER(SpecializeExtension, specialize_extension) + TRIVIAL_ATTR_PRINTER(StaticExclusiveOnly, static_exclusive_only) + TRIVIAL_ATTR_PRINTER(StaticInitializeObjCMetadata, + static_initialize_objc_metadata) + TRIVIAL_ATTR_PRINTER(Testable, testable) + TRIVIAL_ATTR_PRINTER(Transparent, transparent) + TRIVIAL_ATTR_PRINTER(UIApplicationMain, ui_application_main) + TRIVIAL_ATTR_PRINTER(Unsafe, unsafe) + TRIVIAL_ATTR_PRINTER(UnsafeInheritExecutor, unsafe_inherit_executor) + TRIVIAL_ATTR_PRINTER(UnsafeNoObjCTaggedPointer, unsafe_no_objc_tagged_pointer) + TRIVIAL_ATTR_PRINTER(UnsafeNonEscapableResult, unsafe_non_escapable_result) + TRIVIAL_ATTR_PRINTER(UsableFromInline, usable_from_inline) + TRIVIAL_ATTR_PRINTER(Used, used) + TRIVIAL_ATTR_PRINTER(WarnUnqualifiedAccess, warn_unqualified_access) + TRIVIAL_ATTR_PRINTER(WeakLinked, weak_linked) + +#undef TRIVIAL_ATTR_PRINTER + + void visitAccessControlAttr(AccessControlAttr *Attr, Label label) { + printCommon(Attr, "access_control_attr", label); + printField(Attr->getAccess(), Label::always("access_level")); + printFoot(); + } + void visitAlignmentAttr(AlignmentAttr *Attr, Label label) { + printCommon(Attr, "alignment_attr", label); + printField(Attr->getValue(), Label::always("value")); + printFoot(); + } + void visitAllowFeatureSuppressionAttr(AllowFeatureSuppressionAttr *Attr, + Label label) { + printCommon(Attr, "allow_feature_suppression_attr", label); + printStringListField(Attr->getSuppressedFeatures(), + [&](auto name) { return name.str(); }, + Label::always("features")); + printFoot(); + } + void visitAvailableAttr(AvailableAttr *Attr, Label label) { + printCommon(Attr, "available_attr", label); + printField(Attr->Platform, Label::always("platform")); + + if (Attr->isUnconditionallyDeprecated()) { + printFlag("deprecated"); + } + if (Attr->isUnconditionallyUnavailable()) { + printFlag("unavailable"); + } + if (Attr->isNoAsync()) { + printFlag("noasync"); + } + if (auto introduced = Attr->Introduced) + printFieldRaw([&](auto &out) { out << introduced.value().getAsString(); }, + Label::always("introduced")); + if (auto deprecated = Attr->Deprecated) + printFieldRaw([&](auto &out) { out << deprecated.value().getAsString(); }, + Label::always("deprecated")); + if (auto obsoleted = Attr->Obsoleted) + printFieldRaw([&](auto &out) { out << obsoleted.value().getAsString(); }, + Label::always("obsoleted")); + if (!Attr->Message.empty()) + printFieldQuoted(Attr->Message, Label::always("message")); + if (!Attr->Rename.empty()) + printFieldQuoted(Attr->Rename, Label::always("rename")); + printFoot(); + } + void visitBackDeployedAttr(BackDeployedAttr *Attr, Label label) { + printCommon(Attr, "back_deployed_attr", label); + printField(Attr->Platform, Label::always("platform")); + printFieldRaw([&](auto &out) { out << Attr->Version.getAsString(); }, + Label::always("version")); + printFoot(); + } + void visitCDeclAttr(CDeclAttr *Attr, Label label) { + printCommon(Attr, "cdecl_attr", label); + printFieldQuoted(Attr->Name, Label::always("name")); + printFoot(); + } + void + visitClangImporterSynthesizedTypeAttr(ClangImporterSynthesizedTypeAttr *Attr, + Label label) { + printCommon(Attr, "clang_importer_synthesized_type_attr", label); + printField(Attr->getKind(), Label::always("kind")); + printField(Attr->originalTypeName, Label::always("original_type_name")); + printFoot(); + } + void visitCustomAttr(CustomAttr *Attr, Label label) { + printCommon(Attr, "custom_attr", label); + printTypeField(Attr->getType(), Label::always("type")); + if (!Writer.isParsable()) { + // The type has the semantic information we want for parsable outputs, so + // omit the `TypeRepr` there. + printRec(Attr->getTypeRepr(), Label::optional("type_repr")); + } + if (Attr->getArgs()) + printRec(Attr->getArgs(), Label::optional("args")); + printFoot(); + } + void visitDerivativeAttr(DerivativeAttr *Attr, Label label) { + printCommon(Attr, "derivative_attr", label); + printRec(Attr->getBaseTypeRepr(), Label::optional("base_type_repr")); + printName(Attr->getOriginalFunctionName().Name.getFullName(), + Label::always("original_function_name")); + // TODO: Print parameters. + printFoot(); + } + void visitDifferentiableAttr(DifferentiableAttr *Attr, Label label) { + printCommon(Attr, "differentiable_attr", label); + // TODO: Implement. + printFoot(); + } + void visitDocumentationAttr(DocumentationAttr *Attr, Label label) { + printCommon(Attr, "documentation_attr", label); + printFieldQuoted(Attr->Metadata, Label::always("metadata")); + if (Attr->Visibility.has_value()) + printField(Attr->Visibility.value(), Label::always("visibility")); + printFoot(); + } + void visitDynamicReplacementAttr(DynamicReplacementAttr *Attr, + Label label) { + printCommon(Attr, "dynamic_replacement_attr", label); + printName(Attr->getReplacedFunctionName().getFullName(), + Label::always("replaced_function_name")); + printFoot(); + } + void visitEffectsAttr(EffectsAttr *Attr, Label label) { + printCommon(Attr, "effects_attr", label); + printField(Attr->getKind(), Label::always("kind")); + if (Attr->getKind() == EffectsKind::Custom) { + printFieldQuoted(Attr->getCustomString(), Label::always("custom")); + } + printFoot(); + } + void visitExclusivityAttr(ExclusivityAttr *Attr, Label label) { + printCommon(Attr, "exclusivity_attr", label); + printField(Attr->getMode(), Label::always("mode")); + printFoot(); + } + void visitExposeAttr(ExposeAttr *Attr, Label label) { + printCommon(Attr, "expose_attr", label); + printFieldQuoted(Attr->Name, Label::always("name")); + printFoot(); + } + void visitExternAttr(ExternAttr *Attr, Label label) { + printCommon(Attr, "extern_attr", label); + printField(Attr->getExternKind(), Label::always("kind")); + if (Attr->ModuleName.has_value()) + printField(Attr->ModuleName.value(), Label::always("module")); + printFieldQuoted(Attr->Name, Label::always("name")); + printFoot(); + } + void visitImplementsAttr(ImplementsAttr *Attr, Label label) { + printCommon(Attr, "implements_attr", label); + if (Writer.isParsable()) { + // Print the resolved protocol's USR in parsable outputs, not the + // TypeRepr. + if (auto PD = Attr->getCachedProtocol(DC); PD && *PD != nullptr) { + printFieldQuoted(declUSR(*PD), Label::always("protocol")); + } + } else { + printRec(Attr->getProtocolTypeRepr(), Label::always("protocol")); + } + printName(Attr->getMemberName(), Label::always("member")); + printFoot(); + } + void visitInlineAttr(InlineAttr *Attr, Label label) { + printCommon(Attr, "inline_attr", label); + printField(Attr->getKind(), Label::always("kind")); + printFoot(); + } + void visitLifetimeAttr(LifetimeAttr *Attr, Label label) { + printCommon(Attr, "lifetime_attr", label); + // TODO: Implement. + printFoot(); + } + void visitMacroRoleAttr(MacroRoleAttr *Attr, Label label) { + printCommon(Attr, "macro_role_attr", label); + switch (Attr->getMacroSyntax()) { + case MacroSyntax::Attached: + printFlag("attached"); + break; + case MacroSyntax::Freestanding: + printFlag("freestanding"); + break; + } + printField(Attr->getMacroRole(), Label::always("role")); + if (Writer.isParsable()) { + printList(Attr->getNames(), + [&](const MacroIntroducedDeclName &name, Label label) { + printRecArbitrary([&](Label label) { + printHead("name", FieldLabelColor, label); + printField(getMacroIntroducedDeclNameString(name.getKind()), + Label::always("kind")); + if (macroIntroducedNameRequiresArgument(name.getKind())) { + printName(name.getName(), Label::always("argument")); + } + printFoot(); + }, Label::always("macro_introduced_decl_name")); + }, Label::always("names")); + } else { + printFieldQuotedRaw( + [&](auto &out) { + llvm::interleave( + Attr->getNames(), out, + [&](const MacroIntroducedDeclName &name) { + out << getMacroIntroducedDeclNameString(name.getKind()); + if (macroIntroducedNameRequiresArgument(name.getKind())) { + out << "("; + name.getName().print(out); + out << ")"; + } + }, + ","); + }, + Label::always("names")); + } + printRecRange(Attr->getConformances(), Label::always("conformances")); + + printFoot(); + } + void visitNonSendableAttr(NonSendableAttr *Attr, Label label) { + printCommon(Attr, "non_sendable_attr", label); + printField(Attr->Specificity, Label::always("specificity")); + printFoot(); + } + void visitNonisolatedAttr(NonisolatedAttr *Attr, Label label) { + printCommon(Attr, "nonisolated_attr", label); + printFlag(Attr->isUnsafe(), "unsafe"); + printFoot(); + } + void visitObjCAttr(ObjCAttr *Attr, Label label) { + printCommon(Attr, "objc_attr", label); + if (Attr->hasName()) + printFieldQuoted(Attr->getName(), Label::always("name")); + printFlag(Attr->isNameImplicit(), "is_name_implicit"); + printFoot(); + } + void visitObjCBridgedAttr(ObjCBridgedAttr *Attr, Label label) { + printCommon(Attr, "objc_bridged_attr", label); + printDeclRefField(Attr->getObjCClass(), Label::always("objc_class")); + printFoot(); + } + void visitObjCImplementationAttr(ObjCImplementationAttr *Attr, + Label label) { + printCommon(Attr, "objc_implementation_attr", label); + if (!Attr->CategoryName.empty()) + printField(Attr->CategoryName, Label::always("category")); + printFlag(Attr->isEarlyAdopter(), "is_early_adopter"); + printFlag(Attr->isCategoryNameInvalid(), "is_category_name_invalid"); + printFlag(Attr->hasInvalidImplicitLangAttrs(), + "has_invalid_implicit_lang_attrs"); + printFoot(); + } + void visitObjCRuntimeNameAttr(ObjCRuntimeNameAttr *Attr, Label label) { + printCommon(Attr, "objc_runtime_name_attr", label); + printField(Attr->Name, Label::always("name")); + printFoot(); + } + void visitOptimizeAttr(OptimizeAttr *Attr, Label label) { + printCommon(Attr, "optimize_attr", label); + printField(Attr->getMode(), Label::always("mode")); + printFoot(); + } + void visitOriginallyDefinedInAttr(OriginallyDefinedInAttr *Attr, + Label label) { + printCommon(Attr, "originally_defined_in_attr", label); + printField(Attr->OriginalModuleName, Label::always("original_module")); + printField(Attr->Platform, Label::always("platform")); + printFieldRaw([&](auto &out) { out << Attr->MovedVersion.getAsString(); }, + Label::always("moved_version")); + printFoot(); + } + void visitPrivateImportAttr(PrivateImportAttr *Attr, Label label) { + printCommon(Attr, "prinvate_import_attr", label); + printFieldQuoted(Attr->getSourceFile(), Label::always("source_file")); + printFoot(); + } + void visitProjectedValuePropertyAttr(ProjectedValuePropertyAttr *Attr, + Label label) { + printCommon(Attr, "projected_value_property_attr", label); + printField(Attr->ProjectionPropertyName, Label::always("name")); + printFoot(); + } + void visitRawDocCommentAttr(RawDocCommentAttr *Attr, Label label) { + printCommon(Attr, "raw_doc_comment_attr", label); + printFieldRaw( + [&](auto &out) { Attr->getCommentRange().print(out, Ctx->SourceMgr); }, + Label::always("comment_range")); + printFoot(); + } + void visitRawLayoutAttr(RawLayoutAttr *Attr, Label label) { + printCommon(Attr, "raw_layout_attr", label); + if (auto *tyR = Attr->getScalarLikeType()) { + printFlag("scalar_like"); + printRec(tyR, Label::optional("type_repr")); + } else if (auto typeAndCount = Attr->getArrayLikeTypeAndCount()) { + printFlag("array_like"); + printRec(typeAndCount->first, Label::optional("type_repr")); + printRec(typeAndCount->second, Label::optional("count")); + } else if (auto sizeAndAlignment = Attr->getSizeAndAlignment()) { + printField(sizeAndAlignment->first, Label::always("size")); + printField(sizeAndAlignment->second, Label::always("alignment")); + } + printFoot(); + } + void visitReferenceOwnershipAttr(ReferenceOwnershipAttr *Attr, + Label label) { + printCommon(Attr, "reference_ownership_attr", label); + printFlag(keywordOf(Attr->get())); + printFoot(); + } + void visitRestatedObjCConformanceAttr(RestatedObjCConformanceAttr *Attr, + Label label) { + printCommon(Attr, "restated_objc_conformance_attr", label); + if (Attr->Proto) { + if (Writer.isParsable()) { + printFieldQuoted(declUSR(Attr->Proto), Label::optional("proto")); + } else { + printFieldRaw([&](auto &out) { Attr->Proto->dumpRef(out); }, + Label::optional("proto")); + } + } + printFoot(); + } + void visitSILGenNameAttr(SILGenNameAttr *Attr, Label label) { + printCommon(Attr, "silgen_name_attr", label); + printFlag(Attr->Raw, "raw"); + printFieldQuoted(Attr->Name, Label::optional("name")); + printFoot(); + } + void visitSPIAccessControlAttr(SPIAccessControlAttr *Attr, Label label) { + printCommon(Attr, "spi_access_control_attr", label); + printStringListField(Attr->getSPIGroups(), + [&](auto name) { return name.str(); }, + Label::always("groups")); + printFoot(); + } + void visitSectionAttr(SectionAttr *Attr, Label label) { + printCommon(Attr, "section_attr", label); + printFieldQuoted(Attr->Name, Label::always("name")); + printFoot(); + } + void visitSemanticsAttr(SemanticsAttr *Attr, Label label) { + printCommon(Attr, "semantics_attr", label); + printFieldQuoted(Attr->Value, Label::always("value")); + printFoot(); + } + void visitSetterAccessAttr(SetterAccessAttr *Attr, Label label) { + printCommon(Attr, "setter_access_attr", label); + printField(Attr->getAccess(), Label::always("access")); + printFoot(); + } + void visitSpecializeAttr(SpecializeAttr *Attr, Label label) { + printCommon(Attr, "specialize_attr", label); + printFlag(Attr->isExported(), "exported"); + printFlag(Attr->isFullSpecialization(), "full"); + printFlag(Attr->isPartialSpecialization(), "partial"); + if (Attr->getTargetFunctionName()) { + printName(Attr->getTargetFunctionName().getFullName(), + Label::always("target")); + } + if (!Attr->getSPIGroups().empty()) { + printStringListField(Attr->getSPIGroups(), + [&](auto name) { return name.str(); }, + Label::always("spi")); + } + if (Attr->getTrailingWhereClause()) { + printWhereClause(Attr->getTrailingWhereClause(), + Label::always("requirements")); + } + printList(Attr->getAvailableAttrs(), [&](auto *availableAttr, Label label) { + printRec(availableAttr, Ctx, DC, label); + }, Label::optional("available_attrs")); + printFoot(); + } + void visitStorageRestrictionsAttr(StorageRestrictionsAttr *Attr, + Label label) { + printCommon(Attr, "storage_restrictions_attr", label); + printStringListField(Attr->getInitializesNames(), + [&](auto name) { return name.str(); }, + Label::always("initializes")); + printStringListField(Attr->getAccessesNames(), + [&](auto name) { return name.str(); }, + Label::always("accesses")); + printFoot(); + } + void visitSwiftNativeObjCRuntimeBaseAttr(SwiftNativeObjCRuntimeBaseAttr *Attr, + Label label) { + printCommon(Attr, "swift_native_objc_runtime_base", label); + printFieldQuoted(Attr->BaseClassName, Label::always("base_class_name")); + printFoot(); + } + void visitSynthesizedProtocolAttr(SynthesizedProtocolAttr *Attr, + Label label) { + printCommon(Attr, "synthesized_protocol_attr", label); + printFlag(Attr->isUnchecked(), "unchecked"); + if (Writer.isParsable()) { + printFieldQuoted(declUSR(Attr->getProtocol()), + Label::optional("protocol")); + } else { + printFieldQuotedRaw([&](auto &out) { Attr->getProtocol()->dumpRef(out); }, + Label::always("protocol")); + } + printFoot(); + } + void visitTransposeAttr(TransposeAttr *Attr, Label label) { + printCommon(Attr, "transpose_attr", label); + // TODO: Implement. + printFoot(); + } + void visitTypeEraserAttr(TypeEraserAttr *Attr, Label label) { + printCommon(Attr, "type_eraser_attr", label); + printTypeField(Attr->getTypeWithoutResolving(), Label::always("type")); + printRec(Attr->getParsedTypeEraserTypeRepr(), + Label::always("parsed_type_repr")); + printFoot(); + } + void visitUnavailableFromAsyncAttr(UnavailableFromAsyncAttr *Attr, + Label label) { + printCommon(Attr, "unavailable_from_async_attr", label); + if (Attr->hasMessage()) { + printFieldQuoted(Attr->Message, Label::always("message")); + } printFoot(); } }; -void PrintBase::printRec(Decl *D, StringRef label) { - printRecArbitrary([&](StringRef label) { +} // end anonymous namespace + +void PrintBase::printRec(Decl *D, Label label) { + printRecArbitrary([&](Label label) { if (!D) { printHead("", DeclColor, label); printFoot(); } else { - PrintDecl(OS, Indent, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, + PrintDecl(Writer, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) .visit(D, label); } }, label); } -void PrintBase::printRec(Expr *E, StringRef label) { - printRecArbitrary([&](StringRef label) { +void PrintBase::printRec(Expr *E, Label label) { + printRecArbitrary([&](Label label) { if (!E) { printHead("", ExprColor, label); printFoot(); } else { - PrintExpr(OS, Indent, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, + PrintExpr(Writer, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) .visit(E, label); } }, label); } -void PrintBase::printRec(Stmt *S, const ASTContext *Ctx, StringRef label) { - printRecArbitrary([&](StringRef label) { +void PrintBase::printRec(Stmt *S, const ASTContext *Ctx, Label label) { + printRecArbitrary([&](Label label) { if (!S) { printHead("", ExprColor, label); printFoot(); } else { - PrintStmt(OS, Ctx, Indent, ParseIfNeeded, GetTypeOfExpr, + PrintStmt(Writer, Ctx, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) .visit(S, label); } }, label); } -void PrintBase::printRec(TypeRepr *T, StringRef label) { - printRecArbitrary([&](StringRef label) { +void PrintBase::printRec(TypeRepr *T, Label label) { + printRecArbitrary([&](Label label) { if (!T) { printHead("", TypeReprColor, label); printFoot(); } else { - PrintTypeRepr(OS, Indent, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, + PrintTypeRepr(Writer, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) .visit(T, label); } }, label); } -void PrintBase::printRec(const Pattern *P, StringRef label) { - printRecArbitrary([&](StringRef label) { +void PrintBase::printRec(const Pattern *P, Label label) { + printRecArbitrary([&](Label label) { if (!P) { printHead("", PatternColor, label); printFoot(); } else { - PrintPattern(OS, Indent, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, + PrintPattern(Writer, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) .visit(const_cast(P), label); } }, label); } - - -} // end anonymous namespace +void PrintBase::printRec(const DeclAttribute *Attr, const ASTContext *Ctx, + DeclContext *DC, Label label) { + printRecArbitrary( + [&](Label label) { + if (!Attr) { + printHead("", DeclAttributeColor, label); + printFoot(); + } else { + PrintAttribute(Writer, Ctx, DC, ParseIfNeeded, GetTypeOfExpr, + GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) + .visit(const_cast(Attr), label); + } + }, + label); +} void TypeRepr::dump() const { dump(llvm::errs()); llvm::errs() << '\n'; } void TypeRepr::dump(raw_ostream &os, unsigned indent) const { - PrintTypeRepr(os, indent).visit(const_cast(this), ""); + DefaultWriter writer(os, indent); + PrintTypeRepr(writer).visit(const_cast(this), Label::optional("")); } namespace { @@ -3674,7 +5241,7 @@ class PrintConformance : public PrintBase { void visitProtocolConformanceRef(const ProtocolConformanceRef conformance, VisitedConformances &visited, - StringRef label) { + Label label) { if (conformance.isInvalid()) { printHead("invalid_conformance", ASTNodeColor, label); printFoot(); @@ -3686,23 +5253,29 @@ class PrintConformance : public PrintBase { assert(conformance.isAbstract()); printHead("abstract_conformance", ASTNodeColor, label); - printFieldQuoted(conformance.getAbstract()->getName(), "protocol"); + printReferencedDeclField(conformance.getAbstract(), + Label::always("protocol")); printFoot(); } } void visitProtocolConformance(const ProtocolConformance *conformance, - VisitedConformances &visited, StringRef label) { + VisitedConformances &visited, Label label) { // A recursive conformance shouldn't have its contents printed, or there's // infinite recursion. (This also avoids printing things that occur multiple - // times in a conformance hierarchy.) - auto shouldPrintDetails = visited.insert(conformance).second; + // times in a conformance hierarchy.) We also don't print the details in the + // parsable (JSON) output because it is far too much information when it is + // rendered as part of every declref that contains such a conformance. + auto shouldPrintDetails = + visited.insert(conformance).second && !Writer.isParsable(); auto printCommon = [&](StringRef kind) { printHead(kind, ASTNodeColor, label); - printFieldQuoted(conformance->getType(), "type"); - printFieldQuoted(conformance->getProtocol()->getName(), "protocol"); - printFlag(!shouldPrintDetails, "
"); + printTypeField(conformance->getType(), Label::always("type")); + printReferencedDeclField(conformance->getProtocol(), + Label::always("protocol")); + if (!Writer.isParsable()) + printFlag(!shouldPrintDetails, "
"); }; switch (conformance->getKind()) { @@ -3717,57 +5290,67 @@ class PrintConformance : public PrintBase { if (normal->isLazilyLoaded()) { printFlag("lazy"); } else { - normal->forEachTypeWitness([&](const AssociatedTypeDecl *req, Type ty, - const TypeDecl *) -> bool { - printRecArbitrary([&](StringRef label) { - printHead("assoc_type", ASTNodeColor, label); - printFieldQuoted(req->getName(), "req"); - printFieldQuoted(Type(ty->getDesugaredType()), "type", TypeColor); - printFoot(); + printListArbitrary([&]{ + normal->forEachTypeWitness([&](const AssociatedTypeDecl *req, + Type ty, const TypeDecl *) -> bool { + printRecArbitrary([&](Label label) { + printHead("assoc_type", ASTNodeColor, label); + printReferencedDeclField(req, Label::always("req")); + printTypeField(ty->getDesugaredType(), Label::always("type")); + printFoot(); + }, Label::optional("type_witness")); + return false; }); - return false; - }); - - normal->forEachValueWitness([&](const ValueDecl *req, - Witness witness) { - printRecArbitrary([&](StringRef label) { - printHead("value", ASTNodeColor, label); - printFieldQuoted(req->getName(), "req"); - if (!witness) - printFlag("no_witness"); - else if (witness.getDecl() == req) - printFlag("dynamic_witness"); - else - printFieldQuotedRaw([&](raw_ostream &out) { - witness.getDecl()->dumpRef(out); - }, "witness"); - printFoot(); + }, Label::always("type_witnesses")); + printListArbitrary([&]{ + normal->forEachValueWitness([&](const ValueDecl *req, + Witness witness) { + printRecArbitrary([&](Label label) { + printHead("value", ASTNodeColor, label); + printFieldQuoted(req->getName(), Label::always("req")); + if (!witness) + printFlag("no_witness"); + else if (witness.getDecl() == req) + printFlag("dynamic_witness"); + else if (Writer.isParsable()) { + printFieldQuoted(declUSR(witness.getDecl()), + Label::always("witness")); + } else { + printFieldQuotedRaw([&](raw_ostream &out) { + witness.getDecl()->dumpRef(out); + }, Label::always("witness")); + } + printFoot(); + }, Label::optional("value_witness")); }); - }); - - normal->forEachAssociatedConformance( - [&](Type t, ProtocolDecl *proto, unsigned index) { - printRecArbitrary([&](StringRef label) { - printHead("assoc_conformance", ASTNodeColor, label); - printFieldQuoted(t, "type", TypeColor); - printFieldQuoted(proto->getName(), "proto"); - printRec(normal->getAssociatedConformance(t, proto), visited); - printFoot(); + }, Label::always("value_witnesses")); + + printListArbitrary([&]{ + normal->forEachAssociatedConformance( + [&](Type t, ProtocolDecl *proto, unsigned index) { + printRecArbitrary([&](Label label) { + printHead("assoc_conformance", ASTNodeColor, label); + printTypeField(t, Label::always("type")); + printReferencedDeclField(proto, Label::always("proto")); + printRec(normal->getAssociatedConformance(t, proto), visited, + Label::optional("conformance")); + printFoot(); + }, Label::optional("associated_conformance")); + return false; }); - return false; - }); + }, Label::always("associated_conformances")); } if (auto condReqs = normal->getConditionalRequirementsIfAvailable()) { - for (auto requirement : *condReqs) { - printRec(requirement); - } + printList(*condReqs, [&](auto requirement, Label label) { + printRec(requirement, label); + }, Label::optional("conditional_reqs")); } else { - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("", - ASTNodeColor); + ASTNodeColor, label); printFoot(); - }); + }, Label::optional("conditional_reqs")); } break; } @@ -3783,7 +5366,8 @@ class PrintConformance : public PrintBase { if (!shouldPrintDetails) break; - printRec(conf->getInheritedConformance(), visited); + printRec(conf->getInheritedConformance(), visited, + Label::optional("inherited_conformance")); break; } @@ -3793,19 +5377,21 @@ class PrintConformance : public PrintBase { if (!shouldPrintDetails) break; - printRec(conf->getSubstitutionMap(), visited); + printRec(conf->getSubstitutionMap(), visited, + Label::optional("substitutions")); if (auto condReqs = conf->getConditionalRequirementsIfAvailableOrCached(/*computeIfPossible=*/false)) { - for (auto subReq : *condReqs) { - printRec(subReq); - } + printList(*condReqs, [&](auto subReq, Label label) { + printRec(subReq, label); + }, Label::optional("conditional_reqs")); } else { - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("", - ASTNodeColor); + ASTNodeColor, label); printFoot(); - }); + }, Label::optional("conditional_reqs")); } - printRec(conf->getGenericConformance(), visited); + printRec(conf->getGenericConformance(), visited, + Label::optional("generic_conformance")); break; } @@ -3818,27 +5404,29 @@ class PrintConformance : public PrintBase { } void visitPackConformance(const PackConformance *conformance, - VisitedConformances &visited, StringRef label) { + VisitedConformances &visited, Label label) { printHead("pack_conformance", ASTNodeColor, label); - printFieldQuoted(Type(conformance->getType()), "type"); - printFieldQuoted(conformance->getProtocol()->getName(), "protocol"); + printTypeField(conformance->getType(), Label::always("type")); + printReferencedDeclField(conformance->getProtocol(), + Label::always("protocol")); - for (auto conformanceRef : conformance->getPatternConformances()) { - printRec(conformanceRef, visited); - } + printList(conformance->getPatternConformances(), [&](auto conformanceRef, Label label) { + printRec(conformanceRef, visited, label); + }, Label::optional("pattern_conformances")); printFoot(); } void visitSubstitutionMap(SubstitutionMap map, SubstitutionMap::DumpStyle style, - VisitedConformances &visited, StringRef label) { + VisitedConformances &visited, Label label) { // In Minimal style, use single quote so this dump can appear in // double-quoted fields without escaping. - std::optional> restoreQuote; + char quote = Writer.quote(); if (style == SubstitutionMap::DumpStyle::Minimal) - restoreQuote.emplace(quote, '\''); + Writer.setQuote('\''); + SWIFT_DEFER { Writer.setQuote(quote); }; auto genericSig = map.getGenericSignature(); printHead("substitution_map", ASTNodeColor, label); @@ -3849,31 +5437,42 @@ class PrintConformance : public PrintBase { return; } - printFieldRaw([&](raw_ostream &out) { genericSig->print(out); }, - "generic_signature"); + // We don't need to print the human-readable signature for parsable outputs + // because the parameters and requirements printed below contain all the + // same information. + if (!Writer.isParsable()) { + printFieldRaw([&](raw_ostream &out) { genericSig->print(out); }, + Label::always("generic_signature")); + } auto genericParams = genericSig.getGenericParams(); auto replacementTypes = static_cast(map).getReplacementTypes(); - for (unsigned i : indices(genericParams)) { + printList(indices(genericParams), [&](unsigned i, Label label) { if (style == SubstitutionMap::DumpStyle::Minimal) { printFieldRaw([&](raw_ostream &out) { genericParams[i]->print(out); out << " -> "; out << replacementTypes[i]; - }, ""); + }, label); } else { - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("substitution", ASTNodeColor, label); - printFieldRaw([&](raw_ostream &out) { - genericParams[i]->print(out); - out << " -> "; - }, ""); - printRec(replacementTypes[i]); + if (Writer.isParsable()) { + printTypeField(genericParams[i], Label::always("generic_param")); + printTypeField(replacementTypes[i], + Label::always("replacement_type")); + } else { + printFieldRaw([&](raw_ostream &out) { + genericParams[i]->print(out); + out << " -> "; + }, Label::optional("")); + printRec(replacementTypes[i], label); + } printFoot(); - }); + }, Label::optional("replacement")); } - } + }, Label::always("substitutions")); // A minimal dump doesn't need the details about the conformances, a lot of // that info can be inferred from the signature. @@ -3881,42 +5480,42 @@ class PrintConformance : public PrintBase { return; auto conformances = map.getConformances(); - for (const auto &req : genericSig.getRequirements()) { + printList(genericSig.getRequirements(), [&](const auto &req, Label label) { if (req.getKind() != RequirementKind::Conformance) - continue; + return; - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("conformance", ASTNodeColor, label); - printFieldQuoted(req.getFirstType(), "type"); - printRec(conformances.front(), visited); + printTypeField(req.getFirstType(), Label::always("type")); + printRec(conformances.front(), visited, Label::optional("conformance")); printFoot(); - }); + }, label); conformances = conformances.slice(1); - } + }, Label::optional("reqs")); } }; void PrintBase::printRec(SubstitutionMap map, VisitedConformances &visited, - StringRef label) { - printRecArbitrary([&](StringRef label) { - PrintConformance(OS, Indent) + Label label) { + printRecArbitrary([&](Label label) { + PrintConformance(Writer) .visitSubstitutionMap(map, SubstitutionMap::DumpStyle::Full, visited, label); }, label); } void PrintBase::printRec(const ProtocolConformanceRef &ref, - VisitedConformances &visited, StringRef label) { - printRecArbitrary([&](StringRef label) { - PrintConformance(OS, Indent) + VisitedConformances &visited, Label label) { + printRecArbitrary([&](Label label) { + PrintConformance(Writer) .visitProtocolConformanceRef(ref, visited, label); }, label); } void PrintBase::printRec(const ProtocolConformance *conformance, - VisitedConformances &visited, StringRef label) { - printRecArbitrary([&](StringRef label) { - PrintConformance(OS, Indent) + VisitedConformances &visited, Label label) { + printRecArbitrary([&](Label label) { + PrintConformance(Writer) .visitProtocolConformance(conformance, visited, label); }, label); } @@ -3934,12 +5533,16 @@ void ProtocolConformanceRef::dump(llvm::raw_ostream &out, unsigned indent, if (!details && isConcrete()) visited.insert(getConcrete()); - PrintConformance(out, indent).visitProtocolConformanceRef(*this, visited, ""); + DefaultWriter writer(out, indent); + PrintConformance(writer).visitProtocolConformanceRef(*this, visited, + Label::optional("")); } void ProtocolConformanceRef::print(llvm::raw_ostream &out) const { llvm::SmallPtrSet visited; - PrintConformance(out, 0).visitProtocolConformanceRef(*this, visited, ""); + DefaultWriter writer(out, /*indent=*/ 0); + PrintConformance(writer).visitProtocolConformanceRef(*this, visited, + Label::optional("")); } void ProtocolConformance::dump() const { @@ -3950,18 +5553,24 @@ void ProtocolConformance::dump() const { void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const { llvm::SmallPtrSet visited; - PrintConformance(out, indent).visitProtocolConformance(this, visited, ""); + DefaultWriter writer(out, indent); + PrintConformance(writer).visitProtocolConformance(this, visited, + Label::optional("")); } void PackConformance::dump(llvm::raw_ostream &out, unsigned indent) const { llvm::SmallPtrSet visited; - PrintConformance(out, indent).visitPackConformance(this, visited, ""); + DefaultWriter writer(out, indent); + PrintConformance(writer).visitPackConformance(this, visited, + Label::optional("")); } void SubstitutionMap::dump(llvm::raw_ostream &out, DumpStyle style, unsigned indent) const { llvm::SmallPtrSet visited; - PrintConformance(out, indent).visitSubstitutionMap(*this, style, visited, ""); + DefaultWriter writer(out, indent); + PrintConformance(writer).visitSubstitutionMap(*this, style, visited, + Label::optional("")); } void SubstitutionMap::dump() const { @@ -3974,9 +5583,9 @@ void SubstitutionMap::dump() const { //===----------------------------------------------------------------------===// namespace { - class PrintType : public TypeVisitor, + class PrintType : public TypeVisitor, public PrintBase { - void printCommon(StringRef name, StringRef label) { + void printCommon(StringRef name, Label label) { printHead(name, TypeColor, label); } @@ -3992,31 +5601,31 @@ namespace { using PrintBase::PrintBase; #define TRIVIAL_TYPE_PRINTER(Class,Name) \ - void visit##Class##Type(Class##Type *T, StringRef label) { \ + void visit##Class##Type(Class##Type *T, Label label) { \ printCommon(#Name "_type", label); printFoot(); \ } - void visitErrorType(ErrorType *T, StringRef label) { + void visitErrorType(ErrorType *T, Label label) { printCommon("error_type", label); if (auto originalType = T->getOriginalType()) - printRec(originalType, "original_type"); + printRec(originalType, Label::always("original_type")); printFoot(); } TRIVIAL_TYPE_PRINTER(Unresolved, unresolved) - void visitPlaceholderType(PlaceholderType *T, StringRef label) { + void visitPlaceholderType(PlaceholderType *T, Label label) { printCommon("placeholder_type", label); auto originator = T->getOriginator(); if (auto *typeVar = originator.dyn_cast()) { - printRec(typeVar, "type_variable"); + printRec(typeVar, Label::always("type_variable")); } else if (auto *VD = originator.dyn_cast()) { - printFieldQuotedRaw([&](raw_ostream &OS) { VD->dumpRef(OS); }, "", - DeclColor); + printFieldQuotedRaw([&](raw_ostream &OS) { VD->dumpRef(OS); }, + Label::optional("originating_var"), DeclColor); } else if (auto *EE = originator.dyn_cast()) { printFlag("error_expr"); } else if (auto *DMT = originator.dyn_cast()) { - printRec(DMT, "dependent_member_type"); + printRec(DMT, Label::always("dependent_member_type")); } else if (originator.is()) { printFlag("type_repr"); } else { @@ -4025,18 +5634,18 @@ namespace { printFoot(); } - void visitBuiltinIntegerType(BuiltinIntegerType *T, StringRef label) { + void visitBuiltinIntegerType(BuiltinIntegerType *T, Label label) { printCommon("builtin_integer_type", label); if (T->isFixedWidth()) - printField(T->getFixedWidth(), "bit_width"); + printField(T->getFixedWidth(), Label::always("bit_width")); else printFlag("word_sized"); printFoot(); } - void visitBuiltinFloatType(BuiltinFloatType *T, StringRef label) { + void visitBuiltinFloatType(BuiltinFloatType *T, Label label) { printCommon("builtin_float_type", label); - printField(T->getBitWidth(), "bit_width"); + printField(T->getBitWidth(), Label::always("bit_width")); printFoot(); } @@ -4053,320 +5662,342 @@ namespace { TRIVIAL_TYPE_PRINTER(BuiltinUnsafeValueBuffer, builtin_unsafe_value_buffer) TRIVIAL_TYPE_PRINTER(SILToken, sil_token) - void visitBuiltinVectorType(BuiltinVectorType *T, StringRef label) { + void visitBuiltinVectorType(BuiltinVectorType *T, Label label) { printCommon("builtin_vector_type", label); - printField(T->getNumElements(), "num_elements"); - printRec(T->getElementType()); + printField(T->getNumElements(), Label::always("num_elements")); + printRec(T->getElementType(), Label::optional("element_type")); printFoot(); } void visitBuiltinUnboundGenericType(BuiltinUnboundGenericType *T, - StringRef label) { + Label label) { printCommon("builtin_unbound_generic_type", label); - printField(T->getBuiltinTypeNameString(), "name"); + printField(T->getBuiltinTypeNameString(), Label::always("name")); printFoot(); } void visitBuiltinFixedArrayType(BuiltinFixedArrayType *T, - StringRef label) { + Label label) { printCommon("builtin_fixed_array_type", label); - printRec(T->getSize()); - printRec(T->getElementType()); + printRec(T->getSize(), Label::optional("size")); + printRec(T->getElementType(), Label::optional("element_type")); printFoot(); } - void visitTypeAliasType(TypeAliasType *T, StringRef label) { + void visitTypeAliasType(TypeAliasType *T, Label label) { printCommon("type_alias_type", label); - printFieldQuoted(T->getDecl()->printRef(), "decl"); + printFieldQuoted(T->getDecl()->printRef(), Label::always("decl")); if (auto underlying = T->getSinglyDesugaredType()) { - printRec(underlying, "underlying"); + printRec(underlying, Label::always("underlying")); } else { // This can't actually happen printFlag("unresolved_underlying"); } if (T->getParent()) - printRec(T->getParent(), "parent"); - for (auto arg : T->getDirectGenericArgs()) - printRec(arg); + printRec(T->getParent(), Label::always("parent")); + printList(T->getDirectGenericArgs(), [&](auto arg, Label label) { + printRec(arg, label); + }, Label::optional("direct_generic_args")); printFoot(); } - void visitPackType(PackType *T, StringRef label) { + void visitPackType(PackType *T, Label label) { printCommon("pack_type", label); - printField(T->getNumElements(), "num_elements"); + printField(T->getNumElements(), Label::always("num_elements")); - for (Type elt : T->getElementTypes()) { - printRec(elt); - } + printList(T->getElementTypes(), [&](Type elt, Label label) { + printRec(elt, label); + }, Label::optional("element_types")); printFoot(); } - void visitSILPackType(SILPackType *T, StringRef label) { + void visitSILPackType(SILPackType *T, Label label) { printCommon("sil_pack_type", label); - printField(T->isElementAddress(), "element_is_address"); - printField(T->getNumElements(), "num_elements"); + printField(T->isElementAddress(), Label::always("element_is_address")); + printField(T->getNumElements(), Label::always("num_elements")); - for (Type elt : T->getElementTypes()) { - printRec(elt); - } + printList(T->getElementTypes(), [&](Type elt, Label label) { + printRec(elt, label); + }, Label::optional("element_types")); printFoot(); } - void visitPackExpansionType(PackExpansionType *T, StringRef label) { + void visitPackExpansionType(PackExpansionType *T, Label label) { printCommon("pack_expansion_type", label); - printRec(T->getPatternType(), "pattern"); - printRec(T->getCountType(), "count"); + printRec(T->getPatternType(), Label::always("pattern")); + printRec(T->getCountType(), Label::always("count")); printFoot(); } - void visitPackElementType(PackElementType *T, StringRef label) { + void visitPackElementType(PackElementType *T, Label label) { printCommon("element_type", label); - printField(T->getLevel(), "level"); + printField(T->getLevel(), Label::always("level")); - printRec(T->getPackType(), "pack"); + printRec(T->getPackType(), Label::always("pack")); printFoot(); } - void visitTupleType(TupleType *T, StringRef label) { + void visitTupleType(TupleType *T, Label label) { printCommon("tuple_type", label); - printField(T->getNumElements(), "num_elements"); + printField(T->getNumElements(), Label::always("num_elements")); - for (const auto &elt : T->getElements()) { - printRecArbitrary([&](StringRef label) { + printList(T->getElements(), [&](const auto &elt, Label label) { + printRecArbitrary([&](Label label) { printHead("tuple_type_elt", FieldLabelColor, label); if (elt.hasName()) - printFieldQuoted(elt.getName().str(), "name"); - printRec(elt.getType()); + printFieldQuoted(elt.getName().str(), Label::always("name")); + printRec(elt.getType(), Label::optional("type")); printFoot(); - }); - } + }, label); + }, Label::optional("elements")); printFoot(); } #define REF_STORAGE(Name, name, ...) \ - void visit##Name##StorageType(Name##StorageType *T, StringRef label) { \ + void visit##Name##StorageType(Name##StorageType *T, Label label) { \ printCommon(#name "_storage_type", label); \ - printRec(T->getReferentType()); \ + printRec(T->getReferentType(), Label::optional("referent_type")); \ printFoot(); \ } #include "swift/AST/ReferenceStorage.def" #define VISIT_NOMINAL_TYPE(TypeClass, Name) \ - void visit##TypeClass(TypeClass *T, StringRef label) { \ + void visit##TypeClass(TypeClass *T, Label label) { \ printCommon(#Name, label); \ \ - printFieldQuoted(T->getDecl()->printRef(), "decl"); \ + printFieldQuoted(T->getDecl()->printRef(), Label::always("decl")); \ \ if (T->getParent()) \ - printRec(T->getParent(), "parent"); \ + printRec(T->getParent(), Label::always("parent")); \ \ printFoot(); \ } - VISIT_NOMINAL_TYPE(EnumType, enum_type) - VISIT_NOMINAL_TYPE(StructType, struct_type) - VISIT_NOMINAL_TYPE(ClassType, class_type) +#define VISIT_BINDABLE_NOMINAL_TYPE(TypeClass, Name) \ + VISIT_NOMINAL_TYPE(TypeClass, Name) \ + void visitBoundGeneric##TypeClass( \ + BoundGeneric##TypeClass *T, Label label) { \ + printCommon("bound_generic_" #Name, label); \ + printFieldQuoted(T->getDecl()->printRef(), Label::always("decl")); \ + if (T->getParent()) \ + printRec(T->getParent(), Label::always("parent")); \ + printList(T->getGenericArgs(), [&](auto arg, Label label) { \ + printRec(arg, label); \ + }, Label::optional("generic_args")); \ + printFoot(); \ + } + + VISIT_BINDABLE_NOMINAL_TYPE(EnumType, enum_type) + VISIT_BINDABLE_NOMINAL_TYPE(StructType, struct_type) + VISIT_BINDABLE_NOMINAL_TYPE(ClassType, class_type) VISIT_NOMINAL_TYPE(ProtocolType, protocol_type) +#undef VISIT_BINDABLE_NOMINAL_TYPE #undef VISIT_NOMINAL_TYPE - void visitBuiltinTupleType(BuiltinTupleType *T, StringRef label) { + void visitBuiltinTupleType(BuiltinTupleType *T, Label label) { printCommon("builtin_tuple_type", label); - printFieldQuoted(T->getDecl()->printRef(), "decl"); + printFieldQuoted(T->getDecl()->printRef(), Label::always("decl")); printFoot(); } - void visitMetatypeType(MetatypeType *T, StringRef label) { + void visitMetatypeType(MetatypeType *T, Label label) { printCommon("metatype_type", label); if (T->hasRepresentation()) printFlag(getDumpString(T->getRepresentation())); - printRec(T->getInstanceType()); + printRec(T->getInstanceType(), Label::optional("instance_type")); printFoot(); } void visitExistentialMetatypeType(ExistentialMetatypeType *T, - StringRef label) { + Label label) { printCommon("existential_metatype_type", label); if (T->hasRepresentation()) printFlag(getDumpString(T->getRepresentation())); - printRec(T->getInstanceType()); + printRec(T->getInstanceType(), Label::optional("instance_type")); printFoot(); } - void visitModuleType(ModuleType *T, StringRef label) { + void visitModuleType(ModuleType *T, Label label) { printCommon("module_type", label); - printDeclNameField(T->getModule(), "module"); + printDeclName(T->getModule(), Label::always("module")); printFoot(); } - void visitDynamicSelfType(DynamicSelfType *T, StringRef label) { + void visitDynamicSelfType(DynamicSelfType *T, Label label) { printCommon("dynamic_self_type", label); - printRec(T->getSelfType()); + printRec(T->getSelfType(), Label::optional("self_type")); printFoot(); } void printArchetypeCommon(ArchetypeType *T, StringRef className, - StringRef label) { + Label label) { printCommon(className, label); - printField(static_cast(T), "address"); + printField(static_cast(T), Label::always("address")); printFlag(T->requiresClass(), "class"); if (auto layout = T->getLayoutConstraint()) { printFieldRaw([&](raw_ostream &OS) { layout->print(OS); - }, "layout"); + }, Label::always("layout")); } for (auto proto : T->getConformsTo()) - printFieldQuoted(proto->printRef(), "conforms_to"); + printFieldQuoted(proto->printRef(), Label::always("conforms_to")); } void printArchetypeCommonRec(ArchetypeType *T) { - printRec(T->getInterfaceType(), "interface_type"); + printRec(T->getInterfaceType(), Label::always("interface_type")); if (auto superclass = T->getSuperclass()) - printRec(superclass, "superclass"); + printRec(superclass, Label::always("superclass")); } - void visitPrimaryArchetypeType(PrimaryArchetypeType *T, StringRef label) { + void visitPrimaryArchetypeType(PrimaryArchetypeType *T, Label label) { printArchetypeCommon(T, "primary_archetype_type", label); - printFieldQuoted(T->getFullName(), "name"); + printFieldQuoted(T->getFullName(), Label::always("name")); printArchetypeCommonRec(T); printFoot(); } - void visitOpenedArchetypeType(OpenedArchetypeType *T, StringRef label) { + void visitOpenedArchetypeType(OpenedArchetypeType *T, Label label) { printArchetypeCommon(T, "opened_archetype_type", label); auto *env = T->getGenericEnvironment(); - printFieldQuoted(env->getOpenedExistentialUUID(), "opened_existential_id"); + printFieldQuoted(env->getOpenedExistentialUUID(), + Label::always("opened_existential_id")); printArchetypeCommonRec(T); - printRec(env->getOpenedExistentialType(), "opened_existential"); + printRec(env->getOpenedExistentialType(), + Label::always("opened_existential")); if (auto subMap = env->getOuterSubstitutions()) - printRec(subMap, "substitutions"); + printRec(subMap, Label::always("substitutions")); printFoot(); } void visitOpaqueTypeArchetypeType(OpaqueTypeArchetypeType *T, - StringRef label) { + Label label) { printArchetypeCommon(T, "opaque_type", label); - printFieldQuoted(T->getDecl()->getNamingDecl()->printRef(), "decl"); + printFieldQuoted(T->getDecl()->getNamingDecl()->printRef(), + Label::always("decl")); printArchetypeCommonRec(T); if (!T->getSubstitutions().empty()) { - printRec(T->getSubstitutions()); + printRec(T->getSubstitutions(), Label::optional("substitutions")); } printFoot(); } - void visitPackArchetypeType(PackArchetypeType *T, StringRef label) { + void visitPackArchetypeType(PackArchetypeType *T, Label label) { printArchetypeCommon(T, "pack_archetype_type", label); - printFieldQuoted(T->getFullName(), "name"); + printFieldQuoted(T->getFullName(), Label::always("name")); printArchetypeCommonRec(T); printFoot(); } - void visitElementArchetypeType(ElementArchetypeType *T, StringRef label) { + void visitElementArchetypeType(ElementArchetypeType *T, Label label) { printArchetypeCommon(T, "element_archetype_type", label); - printFieldQuoted(T->getOpenedElementID(), "opened_element_id"); + printFieldQuoted(T->getOpenedElementID(), + Label::always("opened_element_id")); printFoot(); } - void visitGenericTypeParamType(GenericTypeParamType *T, StringRef label) { + void visitGenericTypeParamType(GenericTypeParamType *T, Label label) { printCommon("generic_type_param_type", label); - printField(T->getDepth(), "depth"); - printField(T->getIndex(), "index"); + printField(T->getDepth(), Label::always("depth")); + printField(T->getIndex(), Label::always("index")); if (!T->isCanonical()) - printFieldQuoted(T->getName(), "name"); + printFieldQuoted(T->getName(), Label::always("name")); switch (T->getParamKind()) { case GenericTypeParamKind::Type: - printField((StringRef)"type", "param_kind"); + printField((StringRef)"type", Label::always("param_kind")); break; case GenericTypeParamKind::Pack: - printField((StringRef)"pack", "param_kind"); + printField((StringRef)"pack", Label::always("param_kind")); break; case GenericTypeParamKind::Value: - printField((StringRef)"value", "param_kind"); - printRec(T->getValueType(), "value_type"); + printField((StringRef)"value", Label::always("param_kind")); + printRec(T->getValueType(), Label::always("value_type")); } printFoot(); } - void visitDependentMemberType(DependentMemberType *T, StringRef label) { + void visitDependentMemberType(DependentMemberType *T, Label label) { printCommon("dependent_member_type", label); if (auto assocType = T->getAssocType()) { - printFieldQuoted(assocType->printRef(), "assoc_type"); + printFieldQuoted(assocType->printRef(), Label::always("assoc_type")); } else { - printFieldQuoted(T->getName(), "name"); + printFieldQuoted(T->getName(), Label::always("name")); } - printRec(T->getBase(), "base"); + printRec(T->getBase(), Label::always("base")); printFoot(); } void printAnyFunctionParamsRec(ArrayRef params, - StringRef label) { - printRecArbitrary([&](StringRef label) { - printCommon("function_params", label); + Label label) { + printRecArbitrary([&](Label label) { + printHead("function_params", FieldLabelColor, label); - printField(params.size(), "num_params"); - for (const auto ¶m : params) { - printRecArbitrary([&](StringRef label) { + printField(params.size(), Label::always("num_params")); + printList(params, [&](const auto ¶m, Label label) { + printRecArbitrary([&](Label label) { printHead("param", FieldLabelColor, label); if (param.hasLabel()) - printFieldQuoted(param.getLabel().str(), "name"); + printFieldQuoted(param.getLabel().str(), Label::always("name")); if (param.hasInternalLabel()) - printFieldQuoted(param.getInternalLabel().str(), "internal_name"); + printFieldQuoted(param.getInternalLabel().str(), + Label::always("internal_name")); dumpParameterFlags(param.getParameterFlags()); - printRec(param.getPlainType()); + printRec(param.getPlainType(), Label::optional("plain_type")); printFoot(); - }); - } + }, label); + }, Label::optional("params")); printFoot(); }, label); } - void printClangTypeRec(const ClangTypeInfo &info, const ASTContext &ctx) { + void printClangTypeRec(const ClangTypeInfo &info, const ASTContext &ctx, + Label label) { // [TODO: Improve-Clang-type-printing] if (!info.empty()) { - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("clang_type", ASTNodeColor, label); printNameRaw([&](raw_ostream &OS) { auto &clangCtx = ctx.getClangModuleLoader()->getClangASTContext(); info.dump(OS, clangCtx); - }); + }, Label::optional("clang_type_info")); printFoot(); - }); + }, label); } } - void printAnyFunctionTypeCommonRec(AnyFunctionType *T, StringRef label, + void printAnyFunctionTypeCommonRec(AnyFunctionType *T, Label label, StringRef name) { printCommon(name, label); @@ -4375,7 +6006,7 @@ namespace { T->getExtInfo().getSILRepresentation(); if (representation != SILFunctionType::Representation::Thick) { - printField(representation, "representation"); + printField(representation, Label::always("representation")); } printFlag(!T->isNoEscape(), "escaping"); printFlag(T->isSendable(), "Sendable"); @@ -4384,103 +6015,108 @@ namespace { printFlag(T->hasSendingResult(), "sending_result"); } if (Type globalActor = T->getGlobalActor()) { - printFieldQuoted(globalActor.getString(), "global_actor"); + printFieldQuoted(globalActor.getString(), Label::always("global_actor")); } - printClangTypeRec(T->getClangTypeInfo(), T->getASTContext()); - printAnyFunctionParamsRec(T->getParams(), "input"); - printRec(T->getResult(), "output"); + printClangTypeRec(T->getClangTypeInfo(), T->getASTContext(), + Label::optional("clang_type_info")); + printAnyFunctionParamsRec(T->getParams(), Label::always("input")); + printRec(T->getResult(), Label::always("output")); if (Type thrownError = T->getThrownError()) { - printRec(thrownError, "thrown_error"); + printRec(thrownError, Label::always("thrown_error")); } } - void visitFunctionType(FunctionType *T, StringRef label) { + void visitFunctionType(FunctionType *T, Label label) { printAnyFunctionTypeCommonRec(T, label, "function_type"); printFoot(); } - void visitGenericFunctionType(GenericFunctionType *T, StringRef label) { + void visitGenericFunctionType(GenericFunctionType *T, Label label) { printAnyFunctionTypeCommonRec(T, label, "generic_function_type"); // FIXME: generic signature dumping needs improvement - printRecArbitrary([&](StringRef label) { + printRecArbitrary([&](Label label) { printHead("generic_sig", TypeColor, label); - printFieldQuoted(T->getGenericSignature()->getAsString(), ""); + printFieldQuoted(T->getGenericSignature()->getAsString(), + Label::optional("")); printFoot(); - }); + }, Label::optional("generic_sig")); printFoot(); } - void visitSILFunctionType(SILFunctionType *T, StringRef label) { + void visitSILFunctionType(SILFunctionType *T, Label label) { printCommon("sil_function_type", label); - printFieldQuoted(T->getString(), "type"); - - for (auto param : T->getParameters()) { - printRec(param.getInterfaceType(), "input"); - } - for (auto yield : T->getYields()) { - printRec(yield.getInterfaceType(), "yield"); - } - for (auto result : T->getResults()) { - printRec(result.getInterfaceType(), "result"); - } + printFieldQuoted(T->getString(), Label::always("type")); + + printList(T->getParameters(), [&](auto param, Label label) { + printRec(param.getInterfaceType(), label); + }, Label::always("input")); + printList(T->getYields(), [&](auto yield, Label label) { + printRec(yield.getInterfaceType(), label); + }, Label::always("yield")); + printList(T->getResults(), [&](auto result, Label label) { + printRec(result.getInterfaceType(), label); + }, Label::always("result")); if (auto error = T->getOptionalErrorResult()) { - printRec(error->getInterfaceType(), "error"); + printRec(error->getInterfaceType(), Label::always("error")); } - printRec(T->getPatternSubstitutions()); - printRec(T->getInvocationSubstitutions()); - printClangTypeRec(T->getClangTypeInfo(), T->getASTContext()); + printRec(T->getPatternSubstitutions(), + Label::optional("pattern_substitutions")); + printRec(T->getInvocationSubstitutions(), + Label::optional("invocation_substitutions")); + printClangTypeRec(T->getClangTypeInfo(), T->getASTContext(), + Label::optional("clang_type_info")); printFoot(); } - void visitSILBlockStorageType(SILBlockStorageType *T, StringRef label) { + void visitSILBlockStorageType(SILBlockStorageType *T, Label label) { printCommon("sil_block_storage_type", label); - printRec(T->getCaptureType()); + printRec(T->getCaptureType(), Label::optional("capture_type")); printFoot(); } void visitSILMoveOnlyWrappedType(SILMoveOnlyWrappedType *T, - StringRef label) { + Label label) { printCommon("sil_move_only_type", label); - printRec(T->getInnerType()); + printRec(T->getInnerType(), Label::optional("inner_type")); printFoot(); } - void visitSILBoxType(SILBoxType *T, StringRef label) { + void visitSILBoxType(SILBoxType *T, Label label) { printCommon("sil_box_type", label); // FIXME: Print the structure of the type. - printFieldQuoted(T->getString(), "type"); + printFieldQuoted(T->getString(), Label::always("type")); printFoot(); } - void visitArraySliceType(ArraySliceType *T, StringRef label) { + void visitArraySliceType(ArraySliceType *T, Label label) { printCommon("array_slice_type", label); - printRec(T->getBaseType()); + printRec(T->getBaseType(), Label::optional("base_type")); printFoot(); } - void visitOptionalType(OptionalType *T, StringRef label) { + void visitOptionalType(OptionalType *T, Label label) { printCommon("optional_type", label); - printRec(T->getBaseType()); + printRec(T->getBaseType(), Label::optional("base_type")); printFoot(); } - void visitDictionaryType(DictionaryType *T, StringRef label) { + void visitDictionaryType(DictionaryType *T, Label label) { printCommon("dictionary_type", label); - printRec(T->getKeyType(), "key"); - printRec(T->getValueType(), "value"); + printRec(T->getKeyType(), Label::always("key")); + printRec(T->getValueType(), Label::always("value")); printFoot(); } - void visitVariadicSequenceType(VariadicSequenceType *T, StringRef label) { + void visitVariadicSequenceType(VariadicSequenceType *T, Label label) { printCommon("variadic_sequence_type", label); - printRec(T->getBaseType()); + printRec(T->getBaseType(), Label::optional("base_type")); printFoot(); } void visitProtocolCompositionType(ProtocolCompositionType *T, - StringRef label) { + Label label) { printCommon("protocol_composition_type", label); @@ -4497,111 +6133,81 @@ namespace { } } - for (auto proto : T->getMembers()) { - printRec(proto); - } + printList(T->getMembers(), [&](auto proto, Label label) { + printRec(proto, label); + }, Label::optional("members")); printFoot(); } void visitParameterizedProtocolType(ParameterizedProtocolType *T, - StringRef label) { + Label label) { printCommon("parameterized_protocol_type", label); - printRec(T->getBaseType(), "base"); - for (auto arg : T->getArgs()) { - printRec(arg); - } + printRec(T->getBaseType(), Label::always("base")); + printList(T->getArgs(), [&](auto arg, Label label) { + printRec(arg, label); + }, Label::optional("args")); printFoot(); } void visitExistentialType(ExistentialType *T, - StringRef label) { + Label label) { printCommon("existential_type", label); - printRec(T->getConstraintType()); + printRec(T->getConstraintType(), Label::optional("constraint_type")); printFoot(); } - void visitLValueType(LValueType *T, StringRef label) { + void visitLValueType(LValueType *T, Label label) { printCommon("lvalue_type", label); - printRec(T->getObjectType()); + printRec(T->getObjectType(), Label::optional("object_type")); printFoot(); } - void visitInOutType(InOutType *T, StringRef label) { + void visitInOutType(InOutType *T, Label label) { printCommon("inout_type", label); - printRec(T->getObjectType()); + printRec(T->getObjectType(), Label::optional("object_type")); printFoot(); } - void visitUnboundGenericType(UnboundGenericType *T, StringRef label) { + void visitUnboundGenericType(UnboundGenericType *T, Label label) { printCommon("unbound_generic_type", label); - printFieldQuoted(T->getDecl()->printRef(), "decl"); - if (T->getParent()) - printRec(T->getParent(), "parent"); - printFoot(); - } - - void visitBoundGenericClassType(BoundGenericClassType *T, StringRef label) { - printCommon("bound_generic_class_type", label); - printFieldQuoted(T->getDecl()->printRef(), "decl"); - if (T->getParent()) - printRec(T->getParent(), "parent"); - for (auto arg : T->getGenericArgs()) - printRec(arg); - printFoot(); - } - - void visitBoundGenericStructType(BoundGenericStructType *T, - StringRef label) { - printCommon("bound_generic_struct_type", label); - printFieldQuoted(T->getDecl()->printRef(), "decl"); - if (T->getParent()) - printRec(T->getParent(), "parent"); - for (auto arg : T->getGenericArgs()) - printRec(arg); - printFoot(); - } - - void visitBoundGenericEnumType(BoundGenericEnumType *T, StringRef label) { - printCommon("bound_generic_enum_type", label); - printFieldQuoted(T->getDecl()->printRef(), "decl"); + printFieldQuoted(T->getDecl()->printRef(), Label::always("decl")); if (T->getParent()) - printRec(T->getParent(), "parent"); - for (auto arg : T->getGenericArgs()) - printRec(arg); + printRec(T->getParent(), Label::always("parent")); printFoot(); } - void visitTypeVariableType(TypeVariableType *T, StringRef label) { + void visitTypeVariableType(TypeVariableType *T, Label label) { printCommon("type_variable_type", label); - printField(T->getID(), "id"); + printField(T->getID(), Label::always("id")); printFoot(); } - void visitErrorUnionType(ErrorUnionType *T, StringRef label) { + void visitErrorUnionType(ErrorUnionType *T, Label label) { printCommon("error_union_type", label); - for (auto term : T->getTerms()) - printRec(term); + printList(T->getTerms(), [&](auto term, Label label) { + printRec(term, label); + }, Label::optional("terms")); printFoot(); } - void visitIntegerType(IntegerType *T, StringRef label) { + void visitIntegerType(IntegerType *T, Label label) { printCommon("integer_type", label); printFlag(T->isNegative(), "is_negative"); - printFieldQuoted(T->getValue(), "value", LiteralValueColor); + printFieldQuoted(T->getValue(), Label::always("value"), LiteralValueColor); printFoot(); } #undef TRIVIAL_TYPE_PRINTER }; - void PrintBase::printRec(Type type, StringRef label) { - printRecArbitrary([&](StringRef label) { + void PrintBase::printRec(Type type, Label label) { + printRecArbitrary([&](Label label) { if (type.isNull()) { printHead("", DeclColor, label); printFoot(); } else { - PrintType(OS, Indent, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, + PrintType(Writer, ParseIfNeeded, GetTypeOfExpr, GetTypeOfTypeRepr, GetTypeOfKeyPathComponent) .visit(type, label); } @@ -4614,7 +6220,8 @@ void Type::dump() const { } void Type::dump(raw_ostream &os, unsigned indent) const { - PrintType(os, indent).visit(*this, ""); + DefaultWriter writer(os, indent); + PrintType(writer).visit(*this, Label::optional("")); os << "\n"; } @@ -4726,7 +6333,8 @@ void Requirement::dump() const { llvm::errs() << '\n'; } void Requirement::dump(raw_ostream &out) const { - PrintBase(out, 0).visitRequirement(*this); + DefaultWriter writer(out, /*indent=*/ 0); + PrintBase(writer).visitRequirement(*this, Label::optional("")); } void SILParameterInfo::dump() const { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 958f2a64d174c..8a34cbe3c669a 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -2898,6 +2898,14 @@ ProtocolDecl *ImplementsAttr::getProtocol(DeclContext *dc) const { ImplementsAttrProtocolRequest{this, dc}, nullptr); } +std::optional +ImplementsAttr::getCachedProtocol(DeclContext *dc) const { + ImplementsAttrProtocolRequest request{this, dc}; + if (dc->getASTContext().evaluator.hasCachedResult(request)) + return getProtocol(dc); + return std::nullopt; +} + CustomAttr::CustomAttr(SourceLoc atLoc, SourceRange range, TypeExpr *type, CustomAttributeInitializer *initContext, ArgumentList *argList, bool implicit) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 6419120573d67..54e09eb470a7c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1086,6 +1086,14 @@ Type AbstractFunctionDecl::getThrownInterfaceType() const { return CatchNode(mutableThis).getExplicitCaughtType(getASTContext()); } +std::optional AbstractFunctionDecl::getCachedThrownInterfaceType() const { + if (!getThrownTypeRepr()) + return ThrownType.getType(); + + auto mutableThis = const_cast(this); + return CatchNode(mutableThis).getCachedExplicitCaughtType(getASTContext()); +} + std::optional AbstractFunctionDecl::getEffectiveThrownErrorType() const { // FIXME: Only getters can have thrown error types right now, and DidSet // has a cyclic reference if we try to get its interface type here. Find a @@ -3913,6 +3921,12 @@ OpaqueTypeDecl *ValueDecl::getOpaqueResultTypeDecl() const { nullptr); } +std::optional +ValueDecl::getCachedOpaqueResultTypeDecl() const { + return OpaqueResultTypeRequest{const_cast(this)} + .getCachedResult(); +} + bool ValueDecl::isObjC() const { ASTContext &ctx = getASTContext(); return evaluateOrDefault(ctx.evaluator, @@ -10610,6 +10624,11 @@ Type FuncDecl::getResultInterfaceType() const { return ErrorType::get(ctx); } +std::optional FuncDecl::getCachedResultInterfaceType() const { + auto mutableThis = const_cast(this); + return ResultTypeRequest{mutableThis}.getCachedResult(); +} + bool FuncDecl::isUnaryOperator() const { if (!isOperator()) return false; @@ -11766,6 +11785,11 @@ Type MacroDecl::getResultInterfaceType() const { return ErrorType::get(ctx); } +std::optional MacroDecl::getCachedResultInterfaceType() const { + auto mutableThis = const_cast(this); + return ResultTypeRequest{mutableThis}.getCachedResult(); +} + SourceRange MacroDecl::getSourceRange() const { SourceLoc endLoc = getNameLoc(); if (parameterList) @@ -12196,6 +12220,11 @@ Type CatchNode::getExplicitCaughtType(ASTContext &ctx) const { ctx.evaluator, ExplicitCaughtTypeRequest{&ctx, *this}, Type()); } +std::optional +CatchNode::getCachedExplicitCaughtType(ASTContext &ctx) const { + return ExplicitCaughtTypeRequest{&ctx, *this}.getCachedResult(); +} + void swift::simple_display(llvm::raw_ostream &out, CatchNode catchNode) { out << "catch node"; } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 4851e38e6b72f..e0d9e42f536a4 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -347,6 +347,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_cxx_interoperability_mode); inputArgs.AddLastArg(arguments, options::OPT_enable_builtin_module); inputArgs.AddLastArg(arguments, options::OPT_compiler_assertions); + inputArgs.AddLastArg(arguments, options::OPT_dump_ast_format); // Pass on any build config options inputArgs.AddAllArgs(arguments, options::OPT_D); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 84a196e9ef557..5565fcd1dc0ec 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -29,6 +29,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Process.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/LineIterator.h" @@ -203,6 +204,32 @@ bool ArgsToFrontendOptionsConverter::convert( computeDumpScopeMapLocations(); + // Ensure that the compiler was built with zlib support if it was the + // requested AST format. + if (const Arg *A = Args.getLastArg(OPT_dump_ast_format)) { + auto format = + llvm::StringSwitch>(A->getValue()) + .Case("json", FrontendOptions::ASTFormat::JSON) + .Case("json-zlib", FrontendOptions::ASTFormat::JSONZlib) + .Case("default", FrontendOptions::ASTFormat::Default) + .Default(std::nullopt); + if (!format.has_value()) { + Diags.diagnose(SourceLoc(), diag::unknown_dump_ast_format, A->getValue()); + return true; + } + if (format != FrontendOptions::ASTFormat::Default && + !Args.hasArg(OPT_dump_ast)) { + Diags.diagnose(SourceLoc(), diag::ast_format_requires_dump_ast); + return true; + } + if (Opts.DumpASTFormat == FrontendOptions::ASTFormat::JSONZlib && + !llvm::compression::zlib::isAvailable()) { + Diags.diagnose(SourceLoc(), diag::json_zlib_not_supported); + return true; + } + Opts.DumpASTFormat = *format; + } + std::optional inputsAndOutputs = ArgsToFrontendInputsConverter(Diags, Args).convert(buffers); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 9dc2986f3e751..05a9eaa88ea7e 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -82,6 +82,7 @@ #include "llvm/IRReader/IRReader.h" #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" @@ -463,6 +464,28 @@ getPrimaryOrMainSourceFile(const CompilerInstance &Instance) { /// Dumps the AST of all available primary source files. If corresponding output /// files were specified, use them; otherwise, dump the AST to stdout. static bool dumpAST(CompilerInstance &Instance) { + const FrontendOptions &opts = Instance.getInvocation().getFrontendOptions(); + auto dumpAST = [&](SourceFile *SF, raw_ostream &out) { + switch (opts.DumpASTFormat) { + case FrontendOptions::ASTFormat::Default: + SF->dump(out, /*parseIfNeeded*/ true); + break; + case FrontendOptions::ASTFormat::JSON: + SF->dumpJSON(out); + break; + case FrontendOptions::ASTFormat::JSONZlib: + std::string jsonText; + llvm::raw_string_ostream jsonTextStream(jsonText); + SF->dumpJSON(jsonTextStream); + + SmallVector compressed; + llvm::compression::zlib::compress(llvm::arrayRefFromStringRef(jsonText), + compressed); + out << llvm::toStringRef(compressed); + break; + } + }; + auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { @@ -471,7 +494,7 @@ static bool dumpAST(CompilerInstance &Instance) { if (withOutputPath(Instance.getASTContext().Diags, Instance.getOutputBackend(), OutputFilename, [&](raw_ostream &out) -> bool { - sourceFile->dump(out, /*parseIfNeeded*/ true); + dumpAST(sourceFile, out); return false; })) return true; @@ -480,7 +503,7 @@ static bool dumpAST(CompilerInstance &Instance) { // Some invocations don't have primary files. In that case, we default to // looking for the main file and dumping it to `stdout`. auto &SF = getPrimaryOrMainSourceFile(Instance); - SF.dump(llvm::outs(), /*parseIfNeeded*/ true); + dumpAST(&SF, llvm::outs()); } return Instance.getASTContext().hadError(); } diff --git a/test/Frontend/ast-dump-json-macros.swift b/test/Frontend/ast-dump-json-macros.swift new file mode 100644 index 0000000000000..0765251fecc10 --- /dev/null +++ b/test/Frontend/ast-dump-json-macros.swift @@ -0,0 +1,17 @@ +// Checks that macro-expanded members are dumped and that their source ranges +// contain the appropriate buffer ID. + +// REQUIRES: swift_swift_parser + +// RUN: %target-swift-frontend -target %target-swift-5.9-abi-triple -disable-availability-checking -plugin-path %swift-plugin-dir -parse-as-library -dump-ast -dump-ast-format json %s -module-name main -o - | %FileCheck %s + +@DebugDescription +struct X { + var y: Int + + var debugDescription: String { + "y is \(y)" + } +} + +// CHECK: "buffer_id":"@__swiftmacro_ diff --git a/test/Frontend/ast-dump-json-no-crash.swift b/test/Frontend/ast-dump-json-no-crash.swift new file mode 100644 index 0000000000000..bfa739dd27256 --- /dev/null +++ b/test/Frontend/ast-dump-json-no-crash.swift @@ -0,0 +1,469 @@ +// This test is by no means exhaustive, but attempts to catch any places in the +// implementation of ASTDumper's JSON where it might do an unprotected call to +// some method on AST node that would cause an assertion due to some +// unsatisfied precondition. This is a good place to put regression tests if +// issues are discovered in the future. +// +// This file should not contain top-level code, since it contains a `@main` +// type. It should also not contain code that requires Obj-C; put that in +// ast-dump-json-objc-no-crash.swift instead. + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -target %target-swift-5.9-abi-triple -swift-version 6 -I %S/Inputs/dependencies -parse-as-library -dump-ast -dump-ast-format json %s -module-name main -o - >/dev/null + +struct E: Error {} + +@main +struct MainThing { + static func main() {} +} + +// Statements + +func f1() { + return () +} +func f2() { + defer { print() } + if true { print() } else { print() } + guard true else { fatalError() } + while true { print() } + do { print() } + do { throw E() } catch let e1 as E { _ = e1 } catch { _ = error } + repeat { print() } while true + for _ in [] where true { } + for case let .some(x) in [1, nil, 3] where x % 2 == 0 { } + + switch Int.random(in: 0...10) { + case 0: fallthrough + case 1...6: break + case let x where x % 2 == 0: break + default: break + } + + label1: for _ in [] { continue label1 } + label2: for _ in [] { break label2 } + + struct S1: ~Copyable { + deinit {} + consuming func f() { discard self } + } + struct S2 { + init?() { return nil } + } +} + +// Declarations + +protocol P1 { + var v1: Int { get set } + var v2: Int { get set } + func f() + + associatedtype A1 + associatedtype A2 = Int + associatedtype A3: BinaryInteger where A3: Codable +} +protocol P2: AnyObject where Self: C1 {} + +public final class C1: P1 { + public typealias A1 = String + public typealias A3 = Int32 + + public var v1: Int { + willSet { print() } + didSet { print() } + } + public var v2: Int { + get { 10 } + set { print() } + } + public init() { + self.v1 = 0 + self._v3 = 0 + } + public func f() {} + + public var _v3: Int + public var v3: Int { + @storageRestrictions(accesses: v1, initializes: _v3) + init { _v3 = 10 } + get { 0 } + set { print() } + } +} +open class C2 { + public required init() {} + func f1() {} + class func f2() {} + static func f3() {} + + class var v2: Int { 0 } + nonisolated(unsafe) static var v3: Int = 0 + static var v4: Int { 0 } +} +class C3: C2 { + required init() { super.init() } + override func f1() {} + override class func f2() { super.f2() } +} +actor AC1 { + nonisolated func f1() {} +} +enum E1 { + indirect case a(E1) + case b, c(Int), d(x: Int, y: String = "") +} +indirect enum E2 { case e1(E2), e2 } +struct S1 where A: StringProtocol { + func f1() where A == String, B == Int {} +} +typealias TA1 = S1 where B: FixedWidthInteger + +@MainActor public struct MAS {} + +@MainActor protocol MAP { func f() } +struct MASInferred: MAP { + func f() {} +} + +@dynamicMemberLookup +struct DynLook { + subscript(dynamicMember member: String) -> String { return member } + + func f() { + _ = self.hello + } +} +func z1() { + _ = DynLook().hello +} + +@dynamicCallable +struct DynCall { + func dynamicallyCall(withKeywordArguments args: KeyValuePairs) {} + + func f() { + self(label: "value") + } +} +func z2() { + let dc = DynCall() + dc(label: "value") +} + +func z3() { + var s1: S1 + var ta1: TA1 +} +func f1() -> some BinaryInteger { 0 } +func f2(_ t: T) {} +func f3(_ t: some BinaryInteger) {} +func f4(_ values: Int...) {} +func z4() { + f4(1, 2, 3) +} + +func f5( _ value: @autoclosure () -> Int) {} +func z5() { + f5(10) +} + +func f6(_ x: Int = 10) {} +func f7(_ x: inout Int) { x = 50 } +func z7() { + var f7x = 10; f7(&f7x) +} + +func f8() async throws -> Int { 0 } +func f9() async throws { + _ = try await f8() + async let x = f8() + print(try await x) +} + +func f10(isolation: any Actor = #isolation) {} + +struct LazyHolder { + lazy var v: Int = 10 +} + +struct Accessors { + private var _x: Int + var x: Int { + _read { yield _x } + _modify { yield &_x } + } +} + +struct SynthEq: Equatable { + var x: Int + var y: String +} +struct SynthHash: Hashable { + var x: Int + var y: String +} +struct SynthCode: Codable { + var x: Int + var y: String +} +enum SynthComp: Comparable { + case a + case b(Int) + case c(Int, String) +} + +import Swift +import struct Swift.Int +@preconcurrency import Swift +public import Swift +import UserClangModule + +@freestanding(expression) +macro Macro1(t: T) -> String = #externalMacro(module: "DummyModule", type: "DummyType") + +@attached(member) +macro Macro2(t: T) = #externalMacro(module: "DummyModule", type: "DummyType") + +struct ToExtend {} +extension ToExtend { + func f1() {} +} +extension ToExtend where T == Int { + func f2() {} +} +extension [Int] { + func f3() {} +} + +precedencegroup MooglePrecedence { + higherThan: AdditionPrecedence + associativity: left + assignment: false +} +infix operator ^*^: MooglePrecedence + +@resultBuilder +struct StringBuilder { + static func buildBlock(_ parts: String...) -> String { + parts.joined() + } +} +@StringBuilder func sb1() -> String { + "hello" + " " + "world" +} +func sb2(@StringBuilder _ body: () -> String) { print(body()) } +func sb3() { + sb2 { + "hello" + " " + "world" + } +} + +// Expressions + +func zz1() throws { + let _: Unicode.Scalar = "a" + let _: Character = "a\u{0301}" + _ = "abc" + _ = true + _ = 10 + _ = 0x10 + _ = 10.0 + _ = 0x10p4 + _ = "a\(10)b" + _ = #file + _ = #line + _ = /hello(?\s+)world/ + _ = Array(repeating: 0, count: 2) + + let array1 = [1, 2, 3] + _ = array1[0] + + let dict1 = ["a": 1, "b": 2, "c": 3] + _ = dict1["a"] + + let tup1 = (1, b: 2, c: 3) + _ = tup1.0 + _ = tup1.b + + let j: Int = 4 + _ = (1 + 9) * -3 / j << 2 + + struct CAF { + func callAsFunction(x: Int) {} + } + let caf = CAF() + caf(x: 10) + + struct KP { + var x: KP2? + } + struct KP2 { + var y: Int + } + let kp = KP() + _ = kp[keyPath: \.x?.y] + _ = kp[keyPath: \KP.x?.y] + + let cv = 5 + _ = copy cv + + struct NonCop: ~Copyable {} + let noncop = NonCop() + _ = consume noncop + + func thrower() throws(E) -> Int { throw E() } + _ = try thrower() + _ = try? thrower() + _ = try! thrower() + + _ = { [kp, cv] in + print(kp, cv) + } + _ = { @MainActor @Sendable (x: Int) -> String in + return "\(x)" + } + _ = { (x: Int) -> Int in + try thrower() + } + _ = { (x: Int) -> String in + try await f9() + return "ok" + } + let _: (Int, Int) -> Int = { $0 + $1 } +} + +struct Pack { + func f(_ t: repeat each T) { + repeat g(each t) + } +} +// FIXME: USR generation crashes if this is a member of Pack above! +func g(_ t: U) {} + +func tuplify(_ value: repeat each T) -> (repeat each T) { + return (repeat each value) +} +func example(_ value: repeat each T) { + let abstractTuple = tuplify(repeat each value) + repeat print(each abstractTuple) +} + +let x = 10 +func zz1b() { + _ = type(of: x) +} + +struct RebindInit { + var x: Int + init() { self = .init(x: 0)} + init(x: Int) { self.x = x } +} + +@propertyWrapper +struct PropWrap { + var wrappedValue: T + var projectedValue: PropWrap { self } + + init(wrappedValue: T) { self.wrappedValue = wrappedValue } +} +struct WrapperHolder { + @PropWrap var w: Int = 0 + + mutating func f() { + self.w = 0 + self._w = PropWrap(wrappedValue: 1) + _ = self.$w + } +} + +func zz2() { + struct S3 { + var x: Int + } + let s3: S3? = nil + _ = s3?.x.bigEndian + _ = s3!.x.bigEndian + + _ = 5 as Int + _ = 5 as? String + _ = 5 as! Double + + let te1: Int + let te2: String + (te1, te2) = (1, "hello") + + let an1: Any + an1 = 5 + + let trn = Bool.random() ? 10 : 20 +} + +protocol P9 { + func f() -> Self +} +func doSomething(_ p: any P9) { + p.f() // implicit opening +} + +func zz3() { + let ah: AnyHashable = 10 +} + +// Patterns + +enum ForPattern { + case a + case b(value: Int, name: String) + case c(value: Int, name: String) + case d(Int) + case e(Bool) +} +func ffp(_ value: ForPattern) { + switch value { + case .a: break + case .b(let v, let n): print(v, n); break + case let .c(v, n): print(v, n); break + case .d(let x) where x > 10: break + case .d(20): break + case .d: break + case .e(true): break + case .e(false): break + } + switch 5 { + case is Int: break + case _: break; + } +} +func ffpo(_ value: ForPattern?) { + switch value { + case .a?: break + case .d(x as Int): break + default: break + } +} + +// Various other constructs + +@available(iOS, deprecated: 14.0, renamed: "newThing()", message: "Use the new thing") +func oldThing() {} + +@available(iOS, introduced: 14.0) +func newThing() {} + +func newThingClient() { + if #available(iOS 14.0, *) { + newThing() + } else { + oldThing() + } +} + +dynamic func toBeReplaced(arg: Int) {} + +@_dynamicReplacement(for: toBeReplaced(arg:)) +func toReplaceWith(arg: Int) {} diff --git a/test/Frontend/ast-dump-json-objc-no-crash.swift b/test/Frontend/ast-dump-json-objc-no-crash.swift new file mode 100644 index 0000000000000..26057b43cf1bb --- /dev/null +++ b/test/Frontend/ast-dump-json-objc-no-crash.swift @@ -0,0 +1,30 @@ +// This test is by no means exhaustive, but attempts to catch any places in the +// implementation of ASTDumper's JSON where it might do an unprotected call to +// some method on AST node that would cause an assertion due to some +// unsatisfied precondition. This is a good place to put regression tests if +// issues are discovered in the future. +// +// It is separate from ast-dump-json-no-crash.swift since we don't want that +// other test to require Obj-C interop. + +// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -target %target-swift-5.9-abi-triple -enable-objc-interop -swift-version 6 -parse-as-library -dump-ast -dump-ast-format json %s -module-name main -o - + +import Foundation + +@objc public class NotRenamed: NSObject { + @objc public var property: Int = 0 +} + +@objc(PFXRenamed) @objcMembers public class Renamed: NSObject { + @objc(f1) public func f1() {} + @objc(f2WithInteger:) public func f2(_ x: Int) {} + @objc(f3withInteger:error:) public func f3(_ x: Int) throws {} + + @objc(objcnested) public var nested: NotRenamed = .init() +} + +func f() { + _ = #selector(Renamed.f1) + _ = #keyPath(Renamed.nested.property) +} diff --git a/test/Frontend/ast-dump-json-output-map.swift b/test/Frontend/ast-dump-json-output-map.swift new file mode 100644 index 0000000000000..9cae9bd15b046 --- /dev/null +++ b/test/Frontend/ast-dump-json-output-map.swift @@ -0,0 +1,33 @@ +// RUN: %empty-directory(%t) +// RUN: echo 'public func a() { }' > %t/a.swift +// RUN: echo 'public func b() { }' > %t/b.swift +// RUN: echo 'public func main() {a(); b()}' > %t/main.swift +// RUN: echo "{\"%/t/a.swift\": {\"ast-dump\": \"%/t/a.ast\"}, \"%/t/b.swift\": {\"ast-dump\": \"%/t/b.ast\"}, \"%/t/main.swift\": {\"ast-dump\": \"%/t/main.ast\"}}" > %t/outputs.json + +// RUN: %target-build-swift -dump-ast -dump-ast-format json -output-file-map %t/outputs.json %/t/a.swift %/t/b.swift %/t/main.swift -module-name main +// RUN: %FileCheck -check-prefix A-AST %s < %t/a.ast +// RUN: %FileCheck -check-prefix B-AST %s < %t/b.ast +// RUN: %FileCheck -check-prefix MAIN-AST %s < %t/main.ast + + +// Check a.swift's AST +// A-AST: "filename":"{{[^"]+}}a.swift" +// A-AST-SAME: "_kind":"func_decl" +// A-AST-SAME: "usr":"s:4main1ayyF" + + +// Check b.swift's AST +// B-AST: "filename":"{{[^"]+}}b.swift" +// B-AST-SAME: "_kind":"func_decl" +// B-AST-SAME: "usr":"s:4main1byyF" + + +// Check main.swift's AST +// MAIN-AST: "filename":"{{[^"]+}}main.swift" +// MAIN-AST-SAME: "_kind":"func_decl" +// MAIN-AST-SAME: "usr":"s:4mainAAyyF" + +// MAIN-AST-SAME: "_kind":"call_expr" +// MAIN-AST-SAME: "decl_usr":"s:4main1ayyF" +// MAIN-AST-SAME: "_kind":"call_expr" +// MAIN-AST-SAME: "decl_usr":"s:4main1byyF" diff --git a/test/Frontend/ast-dump-json-zlib.swift b/test/Frontend/ast-dump-json-zlib.swift new file mode 100644 index 0000000000000..7a7be3e99c0f4 --- /dev/null +++ b/test/Frontend/ast-dump-json-zlib.swift @@ -0,0 +1,13 @@ +// This test verifies that the zlib-compressed JSON option returns something +// that looks like correct JSON once it's been decompressed. Note that the +// output is a simple zlib-compressed stream and *not* a gzip archive. + +// Windows does not currently build with zlib support. +// UNSUPPORTED: OS=windows-msvc + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -target %target-swift-5.9-abi-triple -swift-version 6 -I %S/Inputs/dependencies -parse-as-library -dump-ast -dump-ast-format json-zlib %S/ast-dump-json-no-crash.swift -module-name main -o - > %t/main.jsonz +// RUN: %{python} -c 'import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.buffer.read()).decode())' < %t/main.jsonz | %FileCheck %s + +// CHECK: {"_kind":"source_file", +// CHECK-SAME: "items":[{ diff --git a/test/Frontend/ast-dump-json.swift b/test/Frontend/ast-dump-json.swift new file mode 100644 index 0000000000000..abaeb946cf3bf --- /dev/null +++ b/test/Frontend/ast-dump-json.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: echo 'public func a() { }' >%t/a.swift +// RUN: echo 'public func b() { }' >%t/b.swift +// RUN: echo 'public func main() {a(); b()}' >%t/main.swift + +// RUN: %target-swift-frontend -dump-ast -dump-ast-format json -primary-file %t/a.swift %t/b.swift %t/main.swift -module-name main -o - 2>%t/a.swift.stderr | %FileCheck -check-prefix A-AST %s +// RUN: %target-swift-frontend -dump-ast -dump-ast-format json %t/a.swift -primary-file %t/b.swift %t/main.swift -module-name main -o - 2>%t/b.swift.stderr | %FileCheck -check-prefix B-AST %s +// RUN: %target-swift-frontend -dump-ast -dump-ast-format json %t/a.swift %t/b.swift -primary-file %t/main.swift -module-name main -o - 2>%t/main.swift.stderr | %FileCheck -check-prefix MAIN-AST %s + +// Check a.swift's AST +// A-AST: "filename":"{{[^"]+}}/a.swift" +// A-AST-SAME: "_kind":"func_decl" +// A-AST-SAME: "usr":"s:4main1ayyF" + + +// Check b.swift's AST +// B-AST: "filename":"{{[^"]+}}/b.swift" +// B-AST-SAME: "_kind":"func_decl" +// B-AST-SAME: "usr":"s:4main1byyF" + + +// Check main.swift's AST +// MAIN-AST: "filename":"{{[^"]+}}/main.swift" +// MAIN-AST-SAME: "_kind":"func_decl" +// MAIN-AST-SAME: "usr":"s:4mainAAyyF" + +// MAIN-AST-SAME: "_kind":"call_expr" +// MAIN-AST-SAME: "decl_usr":"s:4main1ayyF" +// MAIN-AST-SAME: "_kind":"call_expr" +// MAIN-AST-SAME: "decl_usr":"s:4main1byyF" diff --git a/test/Frontend/dump-parse.swift b/test/Frontend/dump-parse.swift index a6bde971d56e0..42e641e331ace 100644 --- a/test/Frontend/dump-parse.swift +++ b/test/Frontend/dump-parse.swift @@ -110,11 +110,11 @@ _ = { (v: MyEnum) in } // CHECK-LABEL: (struct_decl range=[{{.+}}] "SelfParam" struct SelfParam { - // CHECK-LABEL: (func_decl range=[{{.+}}] "createOptional()" type + // CHECK-LABEL: (func_decl range=[{{.+}}] "createOptional()" static // CHECK-NEXT: (parameter "self") - // CHECK-NEXT: (parameter_list range=[{{.+}}]) // CHECK-NEXT: (result=type_optional // CHECK-NEXT: (type_unqualified_ident id="SelfParam" unbound)) + // CHECK-NEXT: (parameter_list range=[{{.+}}]) static func createOptional() -> SelfParam? { // CHECK-LABEL: (call_expr type="" @@ -125,8 +125,8 @@ struct SelfParam { } // CHECK-LABEL: (func_decl range=[{{.+}}] "dumpMemberTypeRepr()" -// CHECK-NEXT: (parameter_list range=[{{.+}}]) // CHECK-NEXT: (result=type_qualified_ident id="Element" unbound // CHECK-NEXT: (type_unqualified_ident id="Array" unbound // CHECK-NEXT: (type_unqualified_ident id="Bool" unbound)) +// CHECK-NEXT: (parameter_list range=[{{.+}}]) func dumpMemberTypeRepr() -> Array.Element { true } diff --git a/test/Frontend/module-alias-dump-ast.swift b/test/Frontend/module-alias-dump-ast.swift index 520258374979b..d07fb433d5031 100644 --- a/test/Frontend/module-alias-dump-ast.swift +++ b/test/Frontend/module-alias-dump-ast.swift @@ -13,10 +13,8 @@ // RUN: %target-swift-frontend -dump-ast %t/FileLib.swift -module-alias XLogging=AppleLogging -I %t > %t/result-ast.output // RUN: %FileCheck %s -input-file %t/result-ast.output -check-prefix CHECK-AST -// CHECK-AST-NOT: bind="XLogging" // CHECK-AST-NOT: module // CHECK-AST-NOT: decl="XLogging" -// CHECK-AST: type_unqualified_ident id="XLogging" bind="AppleLogging" // CHECK-AST: module // CHECK-AST: decl="AppleLogging" diff --git a/test/attr/ApplicationMain/attr_main_throws.swift b/test/attr/ApplicationMain/attr_main_throws.swift index 6c3f53cc108c3..803a9d3c16e13 100644 --- a/test/attr/ApplicationMain/attr_main_throws.swift +++ b/test/attr/ApplicationMain/attr_main_throws.swift @@ -7,7 +7,7 @@ struct MyBase { } } -// CHECK-AST: (func_decl implicit "$main()" interface type="(MyBase.Type) -> () throws -> ()" access=internal type +// CHECK-AST: (func_decl implicit "$main()" interface type="(MyBase.Type) -> () throws -> ()" access=internal static // CHECK-AST-NEXT: (parameter "self") // CHECK-AST-NEXT: (parameter_list) // CHECK-AST-NEXT: (brace_stmt implicit From 6e81f45e5752d9535989a019d4d4d27db5066fec Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 27 Jan 2025 15:28:25 -0500 Subject: [PATCH 2/3] Additional fixes from #77937 needed for attribute dumping. --- lib/ASTGen/Sources/ASTGen/DeclAttrs.swift | 49 +++++++++++++---------- test/ASTGen/attrs.swift | 6 ++- test/ASTGen/diagnostics.swift | 9 ----- test/decl/protocol/override.swift | 4 +- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 4b303fa3c6260..0e116324ac1f0 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -338,7 +338,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), value: value ) } @@ -375,7 +375,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), inverted: inverted, features: features) } @@ -397,7 +397,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name ) } @@ -462,14 +462,14 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), effectKind: effectKind ) } else { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), customString: self.extractRawText(arguments).bridged, customStringLoc: self.generateSourceLoc(arguments) ) @@ -493,7 +493,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), mode: mode ) } @@ -521,7 +521,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name, kind: kind ) @@ -568,7 +568,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), lParenLoc: self.generateSourceLoc(node.leftParen), rParenLoc: self.generateSourceLoc(node.rightParen), kind: kind, @@ -587,7 +587,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name ) } @@ -608,7 +608,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), protocolType: type, memberName: member.name, memberNameLoc: member.loc @@ -632,7 +632,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), kind: kind ) } @@ -799,7 +799,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), syntax: syntax, lParenLoc: self.generateSourceLoc(node.leftParen), role: role, @@ -827,7 +827,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), kind: kind ) } @@ -889,7 +889,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name, isEarlyAdopter: isEarlyAdopter ) @@ -907,7 +907,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name ) } @@ -930,7 +930,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), mode: mode ) } @@ -983,7 +983,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name ) } @@ -1014,7 +1014,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), kind: kind ) } @@ -1069,7 +1069,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name, isRaw: isRaw ) @@ -1086,7 +1086,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), spiGroupName: spiName ) } @@ -1101,7 +1101,7 @@ extension ASTGenVisitor { return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), - range: self.generateSourceRange(node), + range: self.generateAttrSourceRange(node), name: name ) } @@ -1156,6 +1156,13 @@ extension ASTGenVisitor { ) } + func generateAttrSourceRange(_ node: AttributeSyntax) -> BridgedSourceRange { + guard let firstNameTok = node.attributeName.firstToken(viewMode: .sourceAccurate) else { + return BridgedSourceRange() + } + return self.generateSourceRange(start: firstNameTok, end: node.lastToken(viewMode: .sourceAccurate)!) + } + func generateStringLiteralTextIfNotInterpolated(expr node: some ExprSyntaxProtocol) -> BridgedStringRef? { if let segments = node.as(SimpleStringLiteralExprSyntax.self)?.segments { return extractRawText(segments).bridged diff --git a/test/ASTGen/attrs.swift b/test/ASTGen/attrs.swift index 07cca346f2281..a3729476c0e24 100644 --- a/test/ASTGen/attrs.swift +++ b/test/ASTGen/attrs.swift @@ -59,5 +59,7 @@ struct S4 {} @implementation extension ObjCClass1 {} // expected-error {{cannot find type 'ObjCClass1' in scope}} @implementation(Category) extension ObjCClass1 {} // expected-error {{cannot find type 'ObjCClass1' in scope}} -@_objcImplementation extension ObjCClass2 {} // expected-error {{cannot find type 'ObjCClass2' in scope}} -@_objcImplementation(Category) extension ObjCClass2 {} // expected-error {{cannot find type 'ObjCClass2' in scope}} + +// FIXME: @_objcImplementation inserts implicit @objc attribute in C++ parser. +//@_objcImplementation extension ObjCClass2 {} // xpected-error {{cannot find type 'ObjCClass2' in scope}} +//@_objcImplementation(Category) extension ObjCClass2 {} // xpected-error {{cannot find type 'ObjCClass2' in scope}} diff --git a/test/ASTGen/diagnostics.swift b/test/ASTGen/diagnostics.swift index 810cadaf322a9..73ed88770a65d 100644 --- a/test/ASTGen/diagnostics.swift +++ b/test/ASTGen/diagnostics.swift @@ -1,14 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: not %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-bare-slash-regex -enable-experimental-feature ParserASTGen > %t/astgen.ast.raw -// RUN: not %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-bare-slash-regex > %t/cpp-parser.ast.raw - -// Filter out any addresses in the dump, since they can differ. -// RUN: sed -E 's#0x[0-9a-fA-F]+##g' %t/cpp-parser.ast.raw > %t/cpp-parser.ast -// RUN: sed -E 's#0x[0-9a-fA-F]+##g' %t/astgen.ast.raw > %t/astgen.ast - -// RUN: %diff -u %t/astgen.ast %t/cpp-parser.ast - // RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-bare-slash-regex -enable-experimental-feature ParserASTGen // REQUIRES: swift_swift_parser diff --git a/test/decl/protocol/override.swift b/test/decl/protocol/override.swift index aa5f00d9f11d8..fc0a48b81b042 100644 --- a/test/decl/protocol/override.swift +++ b/test/decl/protocol/override.swift @@ -110,13 +110,13 @@ protocol P8: P0 { // CHECK: associated_type_decl // CHECK-SAME: "A" // CHECK-NOT: override - // CHECK-SAME: ) + // CHECK-SAME: {{$}} @_nonoverride associatedtype A // CHECK: func_decl{{.*}}foo(){{.*}}Self : P8 // CHECK-NOT: override= - // CHECK-SAME: ) + // CHECK-SAME: {{$}} @_nonoverride func foo() From 5b53cd85858e1783bcb65fc5bf4455476331205b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 28 Jan 2025 10:59:00 -0500 Subject: [PATCH 3/3] Fix the macro test to use its own macro instead of relying on `DebugDescription`. --- .../Inputs/json_ast_macro_definitions.swift | 19 +++++++++++++++++++ .../Inputs/json_ast_macro_library.swift | 2 ++ test/Frontend/ast-dump-json-macros.swift | 17 ++++++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 test/Frontend/Inputs/json_ast_macro_definitions.swift create mode 100644 test/Frontend/Inputs/json_ast_macro_library.swift diff --git a/test/Frontend/Inputs/json_ast_macro_definitions.swift b/test/Frontend/Inputs/json_ast_macro_definitions.swift new file mode 100644 index 0000000000000..9d67386e8da54 --- /dev/null +++ b/test/Frontend/Inputs/json_ast_macro_definitions.swift @@ -0,0 +1,19 @@ +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +struct MemberInjectingMacro: MemberMacro { + public static func expansion( + of node: AttributeSyntax, + providingMembersOf decl: some DeclGroupSyntax, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + let member: DeclSyntax = + """ + private var _macroInjectedMember: String = "" + """ + + return [member] + } +} diff --git a/test/Frontend/Inputs/json_ast_macro_library.swift b/test/Frontend/Inputs/json_ast_macro_library.swift new file mode 100644 index 0000000000000..a51010aefe883 --- /dev/null +++ b/test/Frontend/Inputs/json_ast_macro_library.swift @@ -0,0 +1,2 @@ +@attached(member, names: named(_macroInjectedMember)) +public macro InjectMember() = #externalMacro(module: "MacroDefinition", type: "MemberInjectingMacro") diff --git a/test/Frontend/ast-dump-json-macros.swift b/test/Frontend/ast-dump-json-macros.swift index 0765251fecc10..5e483720abbe0 100644 --- a/test/Frontend/ast-dump-json-macros.swift +++ b/test/Frontend/ast-dump-json-macros.swift @@ -3,15 +3,18 @@ // REQUIRES: swift_swift_parser -// RUN: %target-swift-frontend -target %target-swift-5.9-abi-triple -disable-availability-checking -plugin-path %swift-plugin-dir -parse-as-library -dump-ast -dump-ast-format json %s -module-name main -o - | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/json_ast_macro_definitions.swift -g -no-toolchain-stdlib-rpath +// RUN: %target-swift-frontend -swift-version 5 -emit-module -o %t/json_ast_macro_library.swiftmodule %S/Inputs/json_ast_macro_library.swift -module-name json_ast_macro_library -load-plugin-library %t/%target-library-name(MacroDefinition) +// RUN: %target-swift-frontend -target %target-swift-5.9-abi-triple -I %t -load-plugin-library %t/%target-library-name(MacroDefinition) -parse-as-library -dump-ast -dump-ast-format json %s -module-name main -o - | %FileCheck %s -@DebugDescription +import json_ast_macro_library + +@InjectMember struct X { var y: Int - - var debugDescription: String { - "y is \(y)" - } } -// CHECK: "buffer_id":"@__swiftmacro_ +// CHECK: "_kind":"pattern_binding_decl" +// CHECK-SAME: "buffer_id":"@__swiftmacro_4main1X12InjectMemberfMm_.swift" +// CHECK-SAME: "name":"_macroInjectedMember"