| //===--- RAIIObjectsForParser.h - RAII helpers for the parser ---*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines and implements the some simple RAII objects that are used |
| // by the parser to manage bits in recursion. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_PARSE_RAII_OBJECTS_FOR_PARSER_H |
| #define LLVM_CLANG_PARSE_RAII_OBJECTS_FOR_PARSER_H |
| |
| #include "clang/Parse/ParseDiagnostic.h" |
| #include "clang/Parse/Parser.h" |
| #include "clang/Sema/DelayedDiagnostic.h" |
| #include "clang/Sema/Sema.h" |
| |
| namespace clang { |
| // TODO: move ParsingClassDefinition here. |
| // TODO: move TentativeParsingAction here. |
| |
| /// \brief A RAII object used to temporarily suppress access-like |
| /// checking. Access-like checks are those associated with |
| /// controlling the use of a declaration, like C++ access control |
| /// errors and deprecation warnings. They are contextually |
| /// dependent, in that they can only be resolved with full |
| /// information about what's being declared. They are also |
| /// suppressed in certain contexts, like the template arguments of |
| /// an explicit instantiation. However, those suppression contexts |
| /// cannot necessarily be fully determined in advance; for |
| /// example, something starting like this: |
| /// template <> class std::vector<A::PrivateType> |
| /// might be the entirety of an explicit instantiation: |
| /// template <> class std::vector<A::PrivateType>; |
| /// or just an elaborated type specifier: |
| /// template <> class std::vector<A::PrivateType> make_vector<>(); |
| /// Therefore this class collects all the diagnostics and permits |
| /// them to be re-delayed in a new context. |
| class SuppressAccessChecks { |
| Sema &S; |
| sema::DelayedDiagnosticPool DiagnosticPool; |
| Sema::ParsingDeclState State; |
| bool Active; |
| |
| public: |
| /// Begin suppressing access-like checks |
| SuppressAccessChecks(Parser &P, bool activate = true) |
| : S(P.getActions()), DiagnosticPool(nullptr) { |
| if (activate) { |
| State = S.PushParsingDeclaration(DiagnosticPool); |
| Active = true; |
| } else { |
| Active = false; |
| } |
| } |
| |
| void done() { |
| assert(Active && "trying to end an inactive suppression"); |
| S.PopParsingDeclaration(State, nullptr); |
| Active = false; |
| } |
| |
| void redelay() { |
| assert(!Active && "redelaying without having ended first"); |
| if (!DiagnosticPool.pool_empty()) |
| S.redelayDiagnostics(DiagnosticPool); |
| assert(DiagnosticPool.pool_empty()); |
| } |
| |
| ~SuppressAccessChecks() { |
| if (Active) done(); |
| } |
| }; |
| |
| /// \brief RAII object used to inform the actions that we're |
| /// currently parsing a declaration. This is active when parsing a |
| /// variable's initializer, but not when parsing the body of a |
| /// class or function definition. |
| class ParsingDeclRAIIObject { |
| Sema &Actions; |
| sema::DelayedDiagnosticPool DiagnosticPool; |
| Sema::ParsingDeclState State; |
| bool Popped; |
| |
| ParsingDeclRAIIObject(const ParsingDeclRAIIObject &) LLVM_DELETED_FUNCTION; |
| void operator=(const ParsingDeclRAIIObject &) LLVM_DELETED_FUNCTION; |
| |
| public: |
| enum NoParent_t { NoParent }; |
| ParsingDeclRAIIObject(Parser &P, NoParent_t _) |
| : Actions(P.getActions()), DiagnosticPool(nullptr) { |
| push(); |
| } |
| |
| /// Creates a RAII object whose pool is optionally parented by another. |
| ParsingDeclRAIIObject(Parser &P, |
| const sema::DelayedDiagnosticPool *parentPool) |
| : Actions(P.getActions()), DiagnosticPool(parentPool) { |
| push(); |
| } |
| |
| /// Creates a RAII object and, optionally, initialize its |
| /// diagnostics pool by stealing the diagnostics from another |
| /// RAII object (which is assumed to be the current top pool). |
| ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *other) |
| : Actions(P.getActions()), |
| DiagnosticPool(other ? other->DiagnosticPool.getParent() : nullptr) { |
| if (other) { |
| DiagnosticPool.steal(other->DiagnosticPool); |
| other->abort(); |
| } |
| push(); |
| } |
| |
| ~ParsingDeclRAIIObject() { |
| abort(); |
| } |
| |
| sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() { |
| return DiagnosticPool; |
| } |
| const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const { |
| return DiagnosticPool; |
| } |
| |
| /// Resets the RAII object for a new declaration. |
| void reset() { |
| abort(); |
| push(); |
| } |
| |
| /// Signals that the context was completed without an appropriate |
| /// declaration being parsed. |
| void abort() { |
| pop(nullptr); |
| } |
| |
| void complete(Decl *D) { |
| assert(!Popped && "ParsingDeclaration has already been popped!"); |
| pop(D); |
| } |
| |
| /// Unregister this object from Sema, but remember all the |
| /// diagnostics that were emitted into it. |
| void abortAndRemember() { |
| pop(nullptr); |
| } |
| |
| private: |
| void push() { |
| State = Actions.PushParsingDeclaration(DiagnosticPool); |
| Popped = false; |
| } |
| |
| void pop(Decl *D) { |
| if (!Popped) { |
| Actions.PopParsingDeclaration(State, D); |
| Popped = true; |
| } |
| } |
| }; |
| |
| /// A class for parsing a DeclSpec. |
| class ParsingDeclSpec : public DeclSpec { |
| ParsingDeclRAIIObject ParsingRAII; |
| |
| public: |
| ParsingDeclSpec(Parser &P) |
| : DeclSpec(P.getAttrFactory()), |
| ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {} |
| ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII) |
| : DeclSpec(P.getAttrFactory()), |
| ParsingRAII(P, RAII) {} |
| |
| const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const { |
| return ParsingRAII.getDelayedDiagnosticPool(); |
| } |
| |
| void complete(Decl *D) { |
| ParsingRAII.complete(D); |
| } |
| |
| void abort() { |
| ParsingRAII.abort(); |
| } |
| }; |
| |
| /// A class for parsing a declarator. |
| class ParsingDeclarator : public Declarator { |
| ParsingDeclRAIIObject ParsingRAII; |
| |
| public: |
| ParsingDeclarator(Parser &P, const ParsingDeclSpec &DS, TheContext C) |
| : Declarator(DS, C), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) { |
| } |
| |
| const ParsingDeclSpec &getDeclSpec() const { |
| return static_cast<const ParsingDeclSpec&>(Declarator::getDeclSpec()); |
| } |
| |
| ParsingDeclSpec &getMutableDeclSpec() const { |
| return const_cast<ParsingDeclSpec&>(getDeclSpec()); |
| } |
| |
| void clear() { |
| Declarator::clear(); |
| ParsingRAII.reset(); |
| } |
| |
| void complete(Decl *D) { |
| ParsingRAII.complete(D); |
| } |
| }; |
| |
| /// A class for parsing a field declarator. |
| class ParsingFieldDeclarator : public FieldDeclarator { |
| ParsingDeclRAIIObject ParsingRAII; |
| |
| public: |
| ParsingFieldDeclarator(Parser &P, const ParsingDeclSpec &DS) |
| : FieldDeclarator(DS), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) { |
| } |
| |
| const ParsingDeclSpec &getDeclSpec() const { |
| return static_cast<const ParsingDeclSpec&>(D.getDeclSpec()); |
| } |
| |
| ParsingDeclSpec &getMutableDeclSpec() const { |
| return const_cast<ParsingDeclSpec&>(getDeclSpec()); |
| } |
| |
| void complete(Decl *D) { |
| ParsingRAII.complete(D); |
| } |
| }; |
| |
| /// ExtensionRAIIObject - This saves the state of extension warnings when |
| /// constructed and disables them. When destructed, it restores them back to |
| /// the way they used to be. This is used to handle __extension__ in the |
| /// parser. |
| class ExtensionRAIIObject { |
| ExtensionRAIIObject(const ExtensionRAIIObject &) LLVM_DELETED_FUNCTION; |
| void operator=(const ExtensionRAIIObject &) LLVM_DELETED_FUNCTION; |
| |
| DiagnosticsEngine &Diags; |
| public: |
| ExtensionRAIIObject(DiagnosticsEngine &diags) : Diags(diags) { |
| Diags.IncrementAllExtensionsSilenced(); |
| } |
| |
| ~ExtensionRAIIObject() { |
| Diags.DecrementAllExtensionsSilenced(); |
| } |
| }; |
| |
| /// ColonProtectionRAIIObject - This sets the Parser::ColonIsSacred bool and |
| /// restores it when destroyed. This says that "foo:" should not be |
| /// considered a possible typo for "foo::" for error recovery purposes. |
| class ColonProtectionRAIIObject { |
| Parser &P; |
| bool OldVal; |
| public: |
| ColonProtectionRAIIObject(Parser &p, bool Value = true) |
| : P(p), OldVal(P.ColonIsSacred) { |
| P.ColonIsSacred = Value; |
| } |
| |
| /// restore - This can be used to restore the state early, before the dtor |
| /// is run. |
| void restore() { |
| P.ColonIsSacred = OldVal; |
| } |
| |
| ~ColonProtectionRAIIObject() { |
| restore(); |
| } |
| }; |
| |
| /// \brief RAII object that makes '>' behave either as an operator |
| /// or as the closing angle bracket for a template argument list. |
| class GreaterThanIsOperatorScope { |
| bool &GreaterThanIsOperator; |
| bool OldGreaterThanIsOperator; |
| public: |
| GreaterThanIsOperatorScope(bool >IO, bool Val) |
| : GreaterThanIsOperator(GTIO), OldGreaterThanIsOperator(GTIO) { |
| GreaterThanIsOperator = Val; |
| } |
| |
| ~GreaterThanIsOperatorScope() { |
| GreaterThanIsOperator = OldGreaterThanIsOperator; |
| } |
| }; |
| |
| class InMessageExpressionRAIIObject { |
| bool &InMessageExpression; |
| bool OldValue; |
| |
| public: |
| InMessageExpressionRAIIObject(Parser &P, bool Value) |
| : InMessageExpression(P.InMessageExpression), |
| OldValue(P.InMessageExpression) { |
| InMessageExpression = Value; |
| } |
| |
| ~InMessageExpressionRAIIObject() { |
| InMessageExpression = OldValue; |
| } |
| }; |
| |
| /// \brief RAII object that makes sure paren/bracket/brace count is correct |
| /// after declaration/statement parsing, even when there's a parsing error. |
| class ParenBraceBracketBalancer { |
| Parser &P; |
| unsigned short ParenCount, BracketCount, BraceCount; |
| public: |
| ParenBraceBracketBalancer(Parser &p) |
| : P(p), ParenCount(p.ParenCount), BracketCount(p.BracketCount), |
| BraceCount(p.BraceCount) { } |
| |
| ~ParenBraceBracketBalancer() { |
| P.ParenCount = ParenCount; |
| P.BracketCount = BracketCount; |
| P.BraceCount = BraceCount; |
| } |
| }; |
| |
| class PoisonSEHIdentifiersRAIIObject { |
| PoisonIdentifierRAIIObject Ident_AbnormalTermination; |
| PoisonIdentifierRAIIObject Ident_GetExceptionCode; |
| PoisonIdentifierRAIIObject Ident_GetExceptionInfo; |
| PoisonIdentifierRAIIObject Ident__abnormal_termination; |
| PoisonIdentifierRAIIObject Ident__exception_code; |
| PoisonIdentifierRAIIObject Ident__exception_info; |
| PoisonIdentifierRAIIObject Ident___abnormal_termination; |
| PoisonIdentifierRAIIObject Ident___exception_code; |
| PoisonIdentifierRAIIObject Ident___exception_info; |
| public: |
| PoisonSEHIdentifiersRAIIObject(Parser &Self, bool NewValue) |
| : Ident_AbnormalTermination(Self.Ident_AbnormalTermination, NewValue), |
| Ident_GetExceptionCode(Self.Ident_GetExceptionCode, NewValue), |
| Ident_GetExceptionInfo(Self.Ident_GetExceptionInfo, NewValue), |
| Ident__abnormal_termination(Self.Ident__abnormal_termination, NewValue), |
| Ident__exception_code(Self.Ident__exception_code, NewValue), |
| Ident__exception_info(Self.Ident__exception_info, NewValue), |
| Ident___abnormal_termination(Self.Ident___abnormal_termination, NewValue), |
| Ident___exception_code(Self.Ident___exception_code, NewValue), |
| Ident___exception_info(Self.Ident___exception_info, NewValue) { |
| } |
| }; |
| |
| /// \brief RAII class that helps handle the parsing of an open/close delimiter |
| /// pair, such as braces { ... } or parentheses ( ... ). |
| class BalancedDelimiterTracker : public GreaterThanIsOperatorScope { |
| Parser& P; |
| tok::TokenKind Kind, Close, FinalToken; |
| SourceLocation (Parser::*Consumer)(); |
| SourceLocation LOpen, LClose; |
| |
| unsigned short &getDepth() { |
| switch (Kind) { |
| case tok::l_brace: return P.BraceCount; |
| case tok::l_square: return P.BracketCount; |
| case tok::l_paren: return P.ParenCount; |
| default: llvm_unreachable("Wrong token kind"); |
| } |
| } |
| |
| enum { MaxDepth = 256 }; |
| |
| bool diagnoseOverflow(); |
| bool diagnoseMissingClose(); |
| |
| public: |
| BalancedDelimiterTracker(Parser& p, tok::TokenKind k, |
| tok::TokenKind FinalToken = tok::semi) |
| : GreaterThanIsOperatorScope(p.GreaterThanIsOperator, true), |
| P(p), Kind(k), FinalToken(FinalToken) |
| { |
| switch (Kind) { |
| default: llvm_unreachable("Unexpected balanced token"); |
| case tok::l_brace: |
| Close = tok::r_brace; |
| Consumer = &Parser::ConsumeBrace; |
| break; |
| case tok::l_paren: |
| Close = tok::r_paren; |
| Consumer = &Parser::ConsumeParen; |
| break; |
| |
| case tok::l_square: |
| Close = tok::r_square; |
| Consumer = &Parser::ConsumeBracket; |
| break; |
| } |
| } |
| |
| SourceLocation getOpenLocation() const { return LOpen; } |
| SourceLocation getCloseLocation() const { return LClose; } |
| SourceRange getRange() const { return SourceRange(LOpen, LClose); } |
| |
| bool consumeOpen() { |
| if (!P.Tok.is(Kind)) |
| return true; |
| |
| if (getDepth() < P.getLangOpts().BracketDepth) { |
| LOpen = (P.*Consumer)(); |
| return false; |
| } |
| |
| return diagnoseOverflow(); |
| } |
| |
| bool expectAndConsume(unsigned DiagID = diag::err_expected, |
| const char *Msg = "", |
| tok::TokenKind SkipToTok = tok::unknown); |
| bool consumeClose() { |
| if (P.Tok.is(Close)) { |
| LClose = (P.*Consumer)(); |
| return false; |
| } |
| |
| return diagnoseMissingClose(); |
| } |
| void skipToEnd(); |
| }; |
| |
| } // end namespace clang |
| |
| #endif |