//===--- DiagnosticEngine.h - Diagnostic Display Engine ---------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file declares the DiagnosticEngine class, which manages any diagnostics // emitted by Swift. // //===----------------------------------------------------------------------===// #ifndef SWIFT_BASIC_DIAGNOSTICENGINE_H #define SWIFT_BASIC_DIAGNOSTICENGINE_H #include "swift/AST/Attr.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/DiagnosticConsumer.h" namespace swift { class Decl; class DiagnosticEngine; class SourceManager; class ValueDecl; enum class PatternKind : uint8_t; enum class ReferenceOwnership : uint8_t; enum class StaticSpellingKind : uint8_t; enum class DescriptiveDeclKind : uint8_t; enum DeclAttrKind : unsigned; /// \brief Enumeration describing all of possible diagnostics. /// /// Each of the diagnostics described in Diagnostics.def has an entry in /// this enumeration type that uniquely identifies it. enum class DiagID : uint32_t; /// \brief Describes a diagnostic along with its argument types. /// /// The diagnostics header introduces instances of this type for each /// diagnostic, which provide both the set of argument types (used to /// check/convert the arguments at each call site) and the diagnostic ID /// (for other information about the diagnostic). template struct Diag { /// \brief The diagnostic ID corresponding to this diagnostic. DiagID ID; }; namespace detail { /// \brief Describes how to pass a diagnostic argument of the given type. /// /// By default, diagnostic arguments are passed by value, because they /// tend to be small. Larger diagnostic arguments /// need to specialize this class template to pass by reference. template struct PassArgument { typedef T type; }; } /// \brief Describes the kind of diagnostic argument we're storing. /// enum class DiagnosticArgumentKind { String, Integer, Unsigned, Identifier, ObjCSelector, ValueDecl, Type, TypeRepr, PatternKind, ReferenceOwnership, StaticSpellingKind, DescriptiveDeclKind, DeclAttribute, VersionTuple, LayoutConstraint, }; namespace diag { enum class RequirementKind : uint8_t; } /// \brief Variant type that holds a single diagnostic argument of a known /// type. /// /// All diagnostic arguments are converted to an instance of this class. class DiagnosticArgument { DiagnosticArgumentKind Kind; union { int IntegerVal; unsigned UnsignedVal; StringRef StringVal; DeclName IdentifierVal; ObjCSelector ObjCSelectorVal; ValueDecl *TheValueDecl; Type TypeVal; TypeRepr *TyR; PatternKind PatternKindVal; ReferenceOwnership ReferenceOwnershipVal; StaticSpellingKind StaticSpellingKindVal; DescriptiveDeclKind DescriptiveDeclKindVal; const DeclAttribute *DeclAttributeVal; llvm::VersionTuple VersionVal; LayoutConstraint LayoutConstraintVal; }; public: DiagnosticArgument(StringRef S) : Kind(DiagnosticArgumentKind::String), StringVal(S) { } DiagnosticArgument(int I) : Kind(DiagnosticArgumentKind::Integer), IntegerVal(I) { } DiagnosticArgument(unsigned I) : Kind(DiagnosticArgumentKind::Unsigned), UnsignedVal(I) { } DiagnosticArgument(DeclName D) : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(D) {} DiagnosticArgument(DeclBaseName D) : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(D) {} DiagnosticArgument(Identifier I) : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(I) { } DiagnosticArgument(ObjCSelector S) : Kind(DiagnosticArgumentKind::ObjCSelector), ObjCSelectorVal(S) { } DiagnosticArgument(ValueDecl *VD) : Kind(DiagnosticArgumentKind::ValueDecl), TheValueDecl(VD) { } DiagnosticArgument(Type T) : Kind(DiagnosticArgumentKind::Type), TypeVal(T) { } DiagnosticArgument(TypeRepr *T) : Kind(DiagnosticArgumentKind::TypeRepr), TyR(T) { } DiagnosticArgument(const TypeLoc &TL) { if (TypeRepr *tyR = TL.getTypeRepr()) { Kind = DiagnosticArgumentKind::TypeRepr; TyR = tyR; } else { Kind = DiagnosticArgumentKind::Type; TypeVal = TL.getType(); } } DiagnosticArgument(PatternKind K) : Kind(DiagnosticArgumentKind::PatternKind), PatternKindVal(K) {} DiagnosticArgument(ReferenceOwnership RO) : Kind(DiagnosticArgumentKind::ReferenceOwnership), ReferenceOwnershipVal(RO) {} DiagnosticArgument(StaticSpellingKind SSK) : Kind(DiagnosticArgumentKind::StaticSpellingKind), StaticSpellingKindVal(SSK) {} DiagnosticArgument(DescriptiveDeclKind DDK) : Kind(DiagnosticArgumentKind::DescriptiveDeclKind), DescriptiveDeclKindVal(DDK) {} DiagnosticArgument(const DeclAttribute *attr) : Kind(DiagnosticArgumentKind::DeclAttribute), DeclAttributeVal(attr) {} DiagnosticArgument(llvm::VersionTuple version) : Kind(DiagnosticArgumentKind::VersionTuple), VersionVal(version) { } DiagnosticArgument(LayoutConstraint L) : Kind(DiagnosticArgumentKind::LayoutConstraint), LayoutConstraintVal(L) { } /// Initializes a diagnostic argument using the underlying type of the /// given enum. template< typename EnumType, typename std::enable_if::value>::type* = nullptr> DiagnosticArgument(EnumType value) : DiagnosticArgument( static_cast::type>(value)) {} DiagnosticArgumentKind getKind() const { return Kind; } StringRef getAsString() const { assert(Kind == DiagnosticArgumentKind::String); return StringVal; } int getAsInteger() const { assert(Kind == DiagnosticArgumentKind::Integer); return IntegerVal; } unsigned getAsUnsigned() const { assert(Kind == DiagnosticArgumentKind::Unsigned); return UnsignedVal; } DeclName getAsIdentifier() const { assert(Kind == DiagnosticArgumentKind::Identifier); return IdentifierVal; } ObjCSelector getAsObjCSelector() const { assert(Kind == DiagnosticArgumentKind::ObjCSelector); return ObjCSelectorVal; } ValueDecl *getAsValueDecl() const { assert(Kind == DiagnosticArgumentKind::ValueDecl); return TheValueDecl; } Type getAsType() const { assert(Kind == DiagnosticArgumentKind::Type); return TypeVal; } TypeRepr *getAsTypeRepr() const { assert(Kind == DiagnosticArgumentKind::TypeRepr); return TyR; } PatternKind getAsPatternKind() const { assert(Kind == DiagnosticArgumentKind::PatternKind); return PatternKindVal; } ReferenceOwnership getAsReferenceOwnership() const { assert(Kind == DiagnosticArgumentKind::ReferenceOwnership); return ReferenceOwnershipVal; } StaticSpellingKind getAsStaticSpellingKind() const { assert(Kind == DiagnosticArgumentKind::StaticSpellingKind); return StaticSpellingKindVal; } DescriptiveDeclKind getAsDescriptiveDeclKind() const { assert(Kind == DiagnosticArgumentKind::DescriptiveDeclKind); return DescriptiveDeclKindVal; } const DeclAttribute *getAsDeclAttribute() const { assert(Kind == DiagnosticArgumentKind::DeclAttribute); return DeclAttributeVal; } llvm::VersionTuple getAsVersionTuple() const { assert(Kind == DiagnosticArgumentKind::VersionTuple); return VersionVal; } LayoutConstraint getAsLayoutConstraint() const { assert(Kind == DiagnosticArgumentKind::LayoutConstraint); return LayoutConstraintVal; } }; struct DiagnosticFormatOptions { const std::string OpeningQuotationMark; const std::string ClosingQuotationMark; const std::string AKAFormatString; DiagnosticFormatOptions(std::string OpeningQuotationMark, std::string ClosingQuotationMark, std::string AKAFormatString) : OpeningQuotationMark(OpeningQuotationMark), ClosingQuotationMark(ClosingQuotationMark), AKAFormatString(AKAFormatString) {} DiagnosticFormatOptions() : OpeningQuotationMark("'"), ClosingQuotationMark("'"), AKAFormatString("'%1$s' (aka '%2$s')") {} }; /// Diagnostic - This is a specific instance of a diagnostic along with all of /// the DiagnosticArguments that it requires. class Diagnostic { public: typedef DiagnosticInfo::FixIt FixIt; private: DiagID ID; SmallVector Args; SmallVector Ranges; SmallVector FixIts; SourceLoc Loc; const Decl *Decl = nullptr; public: // All constructors are intentionally implicit. template Diagnostic(Diag ID, typename detail::PassArgument::type... VArgs) : ID(ID.ID) { DiagnosticArgument DiagArgs[] = { DiagnosticArgument(0), std::move(VArgs)... }; Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs)); } /*implicit*/Diagnostic(DiagID ID, ArrayRef Args) : ID(ID), Args(Args.begin(), Args.end()) {} // Accessors. DiagID getID() const { return ID; } ArrayRef getArgs() const { return Args; } ArrayRef getRanges() const { return Ranges; } ArrayRef getFixIts() const { return FixIts; } SourceLoc getLoc() const { return Loc; } const class Decl *getDecl() const { return Decl; } void setLoc(SourceLoc loc) { Loc = loc; } void setDecl(const class Decl *decl) { Decl = decl; } /// Returns true if this object represents a particular diagnostic. /// /// \code /// someDiag.is(diag::invalid_diagnostic) /// \endcode template bool is(Diag Other) const { return ID == Other.ID; } void addRange(CharSourceRange R) { Ranges.push_back(R); } // Avoid copying the fix-it text more than necessary. void addFixIt(FixIt &&F) { FixIts.push_back(std::move(F)); } }; /// \brief Describes an in-flight diagnostic, which is currently active /// within the diagnostic engine and can be augmented within additional /// information (source ranges, Fix-Its, etc.). /// /// Only a single in-flight diagnostic can be active at one time, and all /// additional information must be emitted through the active in-flight /// diagnostic. class InFlightDiagnostic { friend class DiagnosticEngine; DiagnosticEngine *Engine; bool IsActive; /// \brief Create a new in-flight diagnostic. /// /// This constructor is only available to the DiagnosticEngine. InFlightDiagnostic(DiagnosticEngine &Engine) : Engine(&Engine), IsActive(true) { } InFlightDiagnostic(const InFlightDiagnostic &) = delete; InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete; InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete; public: /// \brief Create an active but unattached in-flight diagnostic. /// /// The resulting diagnostic can be used as a dummy, accepting the /// syntax to add additional information to a diagnostic without /// actually emitting a diagnostic. InFlightDiagnostic() : Engine(0), IsActive(true) { } /// \brief Transfer an in-flight diagnostic to a new object, which is /// typically used when returning in-flight diagnostics. InFlightDiagnostic(InFlightDiagnostic &&Other) : Engine(Other.Engine), IsActive(Other.IsActive) { Other.IsActive = false; } ~InFlightDiagnostic() { if (IsActive) flush(); } /// \brief Flush the active diagnostic to the diagnostic output engine. void flush(); /// \brief Add a token-based range to the currently-active diagnostic. InFlightDiagnostic &highlight(SourceRange R); /// \brief Add a character-based range to the currently-active diagnostic. InFlightDiagnostic &highlightChars(SourceLoc Start, SourceLoc End); /// \brief Add a token-based replacement fix-it to the currently-active /// diagnostic. InFlightDiagnostic &fixItReplace(SourceRange R, StringRef Str); /// \brief Add a character-based replacement fix-it to the currently-active /// diagnostic. InFlightDiagnostic &fixItReplaceChars(SourceLoc Start, SourceLoc End, StringRef Str); /// \brief Add an insertion fix-it to the currently-active diagnostic. InFlightDiagnostic &fixItInsert(SourceLoc L, StringRef Str) { return fixItReplaceChars(L, L, Str); } /// \brief Add an insertion fix-it to the currently-active diagnostic. The /// text is inserted immediately *after* the token specified. /// InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef); /// \brief Add a token-based removal fix-it to the currently-active /// diagnostic. InFlightDiagnostic &fixItRemove(SourceRange R); /// \brief Add a character-based removal fix-it to the currently-active /// diagnostic. InFlightDiagnostic &fixItRemoveChars(SourceLoc Start, SourceLoc End) { return fixItReplaceChars(Start, End, {}); } /// \brief Add two replacement fix-it exchanging source ranges to the /// currently-active diagnostic. InFlightDiagnostic &fixItExchange(SourceRange R1, SourceRange R2); }; /// \brief Class to track, map, and remap diagnostic severity and fatality /// class DiagnosticState { public: /// \brief Describes the current behavior to take with a diagnostic enum class Behavior : uint8_t { Unspecified, Ignore, Note, Remark, Warning, Error, Fatal, }; private: /// \brief Whether we should continue to emit diagnostics, even after a /// fatal error bool showDiagnosticsAfterFatalError = false; /// \brief Don't emit any warnings bool suppressWarnings = false; /// \brief Emit all warnings as errors bool warningsAsErrors = false; /// \brief Whether a fatal error has occurred bool fatalErrorOccurred = false; /// \brief Whether any error diagnostics have been emitted. bool anyErrorOccurred = false; /// \brief Track the previous emitted Behavior, useful for notes Behavior previousBehavior = Behavior::Unspecified; /// \brief Track settable, per-diagnostic state that we store std::vector perDiagnosticBehavior; public: DiagnosticState(); /// \brief Figure out the Behavior for the given diagnostic, taking current /// state such as fatality into account. Behavior determineBehavior(DiagID id); bool hadAnyError() const { return anyErrorOccurred; } bool hasFatalErrorOccurred() const { return fatalErrorOccurred; } void setShowDiagnosticsAfterFatalError(bool val = true) { showDiagnosticsAfterFatalError = val; } bool getShowDiagnosticsAfterFatalError() { return showDiagnosticsAfterFatalError; } /// \brief Whether to skip emitting warnings void setSuppressWarnings(bool val) { suppressWarnings = val; } bool getSuppressWarnings() const { return suppressWarnings; } /// \brief Whether to treat warnings as errors void setWarningsAsErrors(bool val) { warningsAsErrors = val; } bool getWarningsAsErrors() const { return warningsAsErrors; } void resetHadAnyError() { anyErrorOccurred = false; fatalErrorOccurred = false; } /// Set per-diagnostic behavior void setDiagnosticBehavior(DiagID id, Behavior behavior) { perDiagnosticBehavior[(unsigned)id] = behavior; } private: // Make the state movable only DiagnosticState(const DiagnosticState &) = delete; const DiagnosticState &operator=(const DiagnosticState &) = delete; DiagnosticState(DiagnosticState &&) = default; DiagnosticState &operator=(DiagnosticState &&) = default; }; /// \brief Class responsible for formatting diagnostics and presenting them /// to the user. class DiagnosticEngine { /// \brief The source manager used to interpret source locations and /// display diagnostics. SourceManager &SourceMgr; /// \brief The diagnostic consumer(s) that will be responsible for actually /// emitting diagnostics. SmallVector Consumers; /// \brief Tracks diagnostic behaviors and state DiagnosticState state; /// \brief The currently active diagnostic, if there is one. Optional ActiveDiagnostic; /// \brief All diagnostics that have are no longer active but have not yet /// been emitted due to an open transaction. SmallVector TentativeDiagnostics; /// \brief The set of declarations for which we have pretty-printed /// results that we can point to on the command line. llvm::DenseMap PrettyPrintedDeclarations; /// \brief The number of open diagnostic transactions. Diagnostics are only /// emitted once all transactions have closed. unsigned TransactionCount = 0; friend class InFlightDiagnostic; friend class DiagnosticTransaction; public: explicit DiagnosticEngine(SourceManager &SourceMgr) : SourceMgr(SourceMgr), ActiveDiagnostic() { } /// hadAnyError - return true if any *error* diagnostics have been emitted. bool hadAnyError() const { return state.hadAnyError(); } bool hasFatalErrorOccurred() const { return state.hasFatalErrorOccurred(); } void setShowDiagnosticsAfterFatalError(bool val = true) { state.setShowDiagnosticsAfterFatalError(val); } bool getShowDiagnosticsAfterFatalError() { return state.getShowDiagnosticsAfterFatalError(); } /// \brief Whether to skip emitting warnings void setSuppressWarnings(bool val) { state.setSuppressWarnings(val); } bool getSuppressWarnings() const { return state.getSuppressWarnings(); } /// \brief Whether to treat warnings as errors void setWarningsAsErrors(bool val) { state.setWarningsAsErrors(val); } bool getWarningsAsErrors() const { return state.getWarningsAsErrors(); } void ignoreDiagnostic(DiagID id) { state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore); } void resetHadAnyError() { state.resetHadAnyError(); } /// \brief Add an additional DiagnosticConsumer to receive diagnostics. void addConsumer(DiagnosticConsumer &Consumer) { Consumers.push_back(&Consumer); } /// \brief Remove and return all \c DiagnosticConsumers. std::vector takeConsumers() { auto Result = std::vector(Consumers.begin(), Consumers.end()); Consumers.clear(); return Result; } /// \brief Emit a diagnostic using a preformatted array of diagnostic /// arguments. /// /// \param Loc The location to which the diagnostic refers in the source /// code. /// /// \param ID The diagnostic ID. /// /// \param Args The preformatted set of diagnostic arguments. The caller /// must ensure that the diagnostic arguments have the appropriate type. /// /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(SourceLoc Loc, DiagID ID, ArrayRef Args) { assert(!ActiveDiagnostic && "Already have an active diagnostic"); ActiveDiagnostic = Diagnostic(ID, Args); ActiveDiagnostic->setLoc(Loc); return InFlightDiagnostic(*this); } /// \brief Emit a diagnostic using a preformatted array of diagnostic /// arguments. /// /// \param Loc The declaration name location to which the /// diagnostic refers in the source code. /// /// \param ID The diagnostic ID. /// /// \param Args The preformatted set of diagnostic arguments. The caller /// must ensure that the diagnostic arguments have the appropriate type. /// /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(DeclNameLoc Loc, DiagID ID, ArrayRef Args) { return diagnose(Loc.getBaseNameLoc(), ID, Args); } /// \brief Emit an already-constructed diagnostic at the given location. /// /// \param Loc The location to which the diagnostic refers in the source /// code. /// /// \param D The diagnostic. /// /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(SourceLoc Loc, const Diagnostic &D) { assert(!ActiveDiagnostic && "Already have an active diagnostic"); ActiveDiagnostic = D; ActiveDiagnostic->setLoc(Loc); return InFlightDiagnostic(*this); } /// \brief Emit a diagnostic with the given set of diagnostic arguments. /// /// \param Loc The location to which the diagnostic refers in the source /// code. /// /// \param ID The diagnostic to be emitted. /// /// \param Args The diagnostic arguments, which will be converted to /// the types expected by the diagnostic \p ID. template InFlightDiagnostic diagnose(SourceLoc Loc, Diag ID, typename detail::PassArgument::type... Args) { assert(!ActiveDiagnostic && "Already have an active diagnostic"); ActiveDiagnostic = Diagnostic(ID, std::move(Args)...); ActiveDiagnostic->setLoc(Loc); return InFlightDiagnostic(*this); } /// \brief Emit a diagnostic with the given set of diagnostic arguments. /// /// \param Loc The declaration name location to which the /// diagnostic refers in the source code. /// /// \param ID The diagnostic to be emitted. /// /// \param Args The diagnostic arguments, which will be converted to /// the types expected by the diagnostic \p ID. template InFlightDiagnostic diagnose(DeclNameLoc Loc, Diag ID, typename detail::PassArgument::type... Args) { assert(!ActiveDiagnostic && "Already have an active diagnostic"); ActiveDiagnostic = Diagnostic(ID, std::move(Args)...); ActiveDiagnostic->setLoc(Loc.getBaseNameLoc()); return InFlightDiagnostic(*this); } /// \brief Emit a diagnostic using a preformatted array of diagnostic /// arguments. /// /// \param decl The declaration to which this diagnostic refers, which /// may or may not have associated source-location information. /// /// \param id The diagnostic ID. /// /// \param args The preformatted set of diagnostic arguments. The caller /// must ensure that the diagnostic arguments have the appropriate type. /// /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(const Decl *decl, DiagID id, ArrayRef args) { assert(!ActiveDiagnostic && "Already have an active diagnostic"); ActiveDiagnostic = Diagnostic(id, args); ActiveDiagnostic->setDecl(decl); return InFlightDiagnostic(*this); } /// \brief Emit an already-constructed diagnostic referencing the given /// declaration. /// /// \param decl The declaration to which this diagnostic refers, which /// may or may not have associated source-location information. /// /// \param diag The diagnostic. /// /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(const Decl *decl, const Diagnostic &diag) { assert(!ActiveDiagnostic && "Already have an active diagnostic"); ActiveDiagnostic = diag; ActiveDiagnostic->setDecl(decl); return InFlightDiagnostic(*this); } /// \brief Emit a diagnostic with the given set of diagnostic arguments. /// /// \param decl The declaration to which this diagnostic refers, which /// may or may not have associated source-location information. /// /// \param id The diagnostic to be emitted. /// /// \param args The diagnostic arguments, which will be converted to /// the types expected by the diagnostic \p ID. template InFlightDiagnostic diagnose(const Decl *decl, Diag id, typename detail::PassArgument::type... args) { ActiveDiagnostic = Diagnostic(id, std::move(args)...); ActiveDiagnostic->setDecl(decl); return InFlightDiagnostic(*this); } /// \returns true if diagnostic is marked with PointsToFirstBadToken /// option. bool isDiagnosticPointsToFirstBadToken(DiagID id) const; /// \returns true if any diagnostic consumer gave an error while invoking //// \c finishProcessing. bool finishProcessing(); /// \brief Format the given diagnostic text and place the result in the given /// buffer. static void formatDiagnosticText( llvm::raw_ostream &Out, StringRef InText, ArrayRef FormatArgs, DiagnosticFormatOptions FormatOpts = DiagnosticFormatOptions()); private: /// \brief Flush the active diagnostic. void flushActiveDiagnostic(); /// \brief Retrieve the active diagnostic. Diagnostic &getActiveDiagnostic() { return *ActiveDiagnostic; } /// \brief Send \c diag to all diagnostic consumers. void emitDiagnostic(const Diagnostic &diag); /// \brief Send all tentative diagnostics to all diagnostic consumers and /// delete them. void emitTentativeDiagnostics(); public: static const char *diagnosticStringFor(const DiagID id); }; /// \brief Represents a diagnostic transaction. While a transaction is /// open, all recorded diagnostics are saved until the transaction commits, /// at which point they are emitted. If the transaction is instead aborted, /// the diagnostics are erased. Transactions may be nested but must be closed /// in LIFO order. An open transaction is implicitly committed upon /// destruction. class DiagnosticTransaction { DiagnosticEngine &Engine; /// \brief How many tentative diagnostics there were when the transaction /// was opened. unsigned PrevDiagnostics; /// \brief How many other transactions were open when this transaction was /// opened. unsigned Depth; /// \brief Whether this transaction is currently open. bool IsOpen = true; public: explicit DiagnosticTransaction(DiagnosticEngine &engine) : Engine(engine), PrevDiagnostics(Engine.TentativeDiagnostics.size()), Depth(Engine.TransactionCount), IsOpen(true) { assert(!Engine.ActiveDiagnostic); Engine.TransactionCount++; } ~DiagnosticTransaction() { if (IsOpen) { commit(); } } /// \brief Abort and close this transaction and erase all diagnostics /// record while it was open. void abort() { close(); Engine.TentativeDiagnostics.erase( Engine.TentativeDiagnostics.begin() + PrevDiagnostics, Engine.TentativeDiagnostics.end()); } /// \brief Commit and close this transaction. If this is the top-level /// transaction, emit any diagnostics that were recorded while it was open. void commit() { close(); if (Depth == 0) { assert(PrevDiagnostics == 0); Engine.emitTentativeDiagnostics(); } } private: void close() { assert(IsOpen && "only open transactions may be closed"); IsOpen = false; Engine.TransactionCount--; assert(Depth == Engine.TransactionCount && "transactions must be closed LIFO"); } }; } // end namespace swift #endif