//===--- ParseTentative.cpp - Ambiguity Resolution Parsing ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the tentative parsing portions of the Parser // interfaces, for ambiguity resolution. // //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Sema/ParsedTemplate.h" usingnamespaceclang; /// isCXXDeclarationStatement - C++-specialized function that disambiguates /// between a declaration or an expression statement, when parsing function /// bodies. Returns true for declaration, false for expression. /// /// declaration-statement: /// block-declaration /// /// block-declaration: /// simple-declaration /// asm-definition /// namespace-alias-definition /// using-declaration /// using-directive /// [C++0x] static_assert-declaration /// /// asm-definition: /// 'asm' '(' string-literal ')' ';' /// /// namespace-alias-definition: /// 'namespace' identifier = qualified-namespace-specifier ';' /// /// using-declaration: /// 'using' typename[opt] '::'[opt] nested-name-specifier /// unqualified-id ';' /// 'using' '::' unqualified-id ; /// /// using-directive: /// 'using' 'namespace' '::'[opt] nested-name-specifier[opt] /// namespace-name ';' /// bool Parser::isCXXDeclarationStatement( bool DisambiguatingWithExpression /*=false*/) { … } /// isCXXSimpleDeclaration - C++-specialized function that disambiguates /// between a simple-declaration or an expression-statement. /// If during the disambiguation process a parsing error is encountered, /// the function returns true to let the declaration parsing code handle it. /// Returns false if the statement is disambiguated as expression. /// /// simple-declaration: /// decl-specifier-seq init-declarator-list[opt] ';' /// decl-specifier-seq ref-qualifier[opt] '[' identifier-list ']' /// brace-or-equal-initializer ';' [C++17] /// /// (if AllowForRangeDecl specified) /// for ( for-range-declaration : for-range-initializer ) statement /// /// for-range-declaration: /// decl-specifier-seq declarator /// decl-specifier-seq ref-qualifier[opt] '[' identifier-list ']' /// /// In any of the above cases there can be a preceding attribute-specifier-seq, /// but the caller is expected to handle that. bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) { … } /// Try to consume a token sequence that we've already identified as /// (potentially) starting a decl-specifier. Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { … } /// simple-declaration: /// decl-specifier-seq init-declarator-list[opt] ';' /// /// (if AllowForRangeDecl specified) /// for ( for-range-declaration : for-range-initializer ) statement /// for-range-declaration: /// attribute-specifier-seqopt type-specifier-seq declarator /// Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { … } /// Tentatively parse an init-declarator-list in order to disambiguate it from /// an expression. /// /// init-declarator-list: /// init-declarator /// init-declarator-list ',' init-declarator /// /// init-declarator: /// declarator initializer[opt] /// [GNU] declarator simple-asm-expr[opt] attributes[opt] initializer[opt] /// /// initializer: /// brace-or-equal-initializer /// '(' expression-list ')' /// /// brace-or-equal-initializer: /// '=' initializer-clause /// [C++11] braced-init-list /// /// initializer-clause: /// assignment-expression /// braced-init-list /// /// braced-init-list: /// '{' initializer-list ','[opt] '}' /// '{' '}' /// Parser::TPResult Parser::TryParseInitDeclaratorList(bool MayHaveTrailingReturnType) { … } struct Parser::ConditionDeclarationOrInitStatementState { … }; bool Parser::isEnumBase(bool AllowSemi) { … } /// Disambiguates between a declaration in a condition, a /// simple-declaration in an init-statement, and an expression for /// a condition of a if/switch statement. /// /// condition: /// expression /// type-specifier-seq declarator '=' assignment-expression /// [C++11] type-specifier-seq declarator '=' initializer-clause /// [C++11] type-specifier-seq declarator braced-init-list /// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt] /// '=' assignment-expression /// simple-declaration: /// decl-specifier-seq init-declarator-list[opt] ';' /// /// Note that, unlike isCXXSimpleDeclaration, we must disambiguate all the way /// to the ';' to disambiguate cases like 'int(x))' (an expression) from /// 'int(x);' (a simple-declaration in an init-statement). Parser::ConditionOrInitStatement Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement, bool CanBeForRangeDecl) { … } /// Determine whether the next set of tokens contains a type-id. /// /// The context parameter states what context we're parsing right /// now, which affects how this routine copes with the token /// following the type-id. If the context is TypeIdInParens, we have /// already parsed the '(' and we will cease lookahead when we hit /// the corresponding ')'. If the context is /// TypeIdAsTemplateArgument, we've already parsed the '<' or ',' /// before this template argument, and will cease lookahead when we /// hit a '>', '>>' (in C++0x), or ','; or, in C++0x, an ellipsis immediately /// preceding such. Returns true for a type-id and false for an expression. /// If during the disambiguation process a parsing error is encountered, /// the function returns true to let the declaration parsing code handle it. /// /// type-id: /// type-specifier-seq abstract-declarator[opt] /// bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { … } /// Returns true if this is a C++11 attribute-specifier. Per /// C++11 [dcl.attr.grammar]p6, two consecutive left square bracket tokens /// always introduce an attribute. In Objective-C++11, this rule does not /// apply if either '[' begins a message-send. /// /// If Disambiguate is true, we try harder to determine whether a '[[' starts /// an attribute-specifier, and return CAK_InvalidAttributeSpecifier if not. /// /// If OuterMightBeMessageSend is true, we assume the outer '[' is either an /// Obj-C message send or the start of an attribute. Otherwise, we assume it /// is not an Obj-C message send. /// /// C++11 [dcl.attr.grammar]: /// /// attribute-specifier: /// '[' '[' attribute-list ']' ']' /// alignment-specifier /// /// attribute-list: /// attribute[opt] /// attribute-list ',' attribute[opt] /// attribute '...' /// attribute-list ',' attribute '...' /// /// attribute: /// attribute-token attribute-argument-clause[opt] /// /// attribute-token: /// identifier /// identifier '::' identifier /// /// attribute-argument-clause: /// '(' balanced-token-seq ')' Parser::CXX11AttributeKind Parser::isCXX11AttributeSpecifier(bool Disambiguate, bool OuterMightBeMessageSend) { … } bool Parser::TrySkipAttributes() { … } Parser::TPResult Parser::TryParsePtrOperatorSeq() { … } /// operator-function-id: /// 'operator' operator /// /// operator: one of /// new delete new[] delete[] + - * / % ^ [...] /// /// conversion-function-id: /// 'operator' conversion-type-id /// /// conversion-type-id: /// type-specifier-seq conversion-declarator[opt] /// /// conversion-declarator: /// ptr-operator conversion-declarator[opt] /// /// literal-operator-id: /// 'operator' string-literal identifier /// 'operator' user-defined-string-literal Parser::TPResult Parser::TryParseOperatorId() { … } /// declarator: /// direct-declarator /// ptr-operator declarator /// /// direct-declarator: /// declarator-id /// direct-declarator '(' parameter-declaration-clause ')' /// cv-qualifier-seq[opt] exception-specification[opt] /// direct-declarator '[' constant-expression[opt] ']' /// '(' declarator ')' /// [GNU] '(' attributes declarator ')' /// /// abstract-declarator: /// ptr-operator abstract-declarator[opt] /// direct-abstract-declarator /// /// direct-abstract-declarator: /// direct-abstract-declarator[opt] /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// direct-abstract-declarator[opt] '[' constant-expression[opt] ']' /// '(' abstract-declarator ')' /// [C++0x] ... /// /// ptr-operator: /// '*' cv-qualifier-seq[opt] /// '&' /// [C++0x] '&&' [TODO] /// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] /// /// cv-qualifier-seq: /// cv-qualifier cv-qualifier-seq[opt] /// /// cv-qualifier: /// 'const' /// 'volatile' /// /// declarator-id: /// '...'[opt] id-expression /// /// id-expression: /// unqualified-id /// qualified-id [TODO] /// /// unqualified-id: /// identifier /// operator-function-id /// conversion-function-id /// literal-operator-id /// '~' class-name [TODO] /// '~' decltype-specifier [TODO] /// template-id [TODO] /// Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier, bool mayHaveDirectInit, bool mayHaveTrailingReturnType) { … } bool Parser::isTentativelyDeclared(IdentifierInfo *II) { … } namespace { class TentativeParseCCC final : public CorrectionCandidateCallback { … }; } /// isCXXDeclarationSpecifier - Returns TPResult::True if it is a declaration /// specifier, TPResult::False if it is not, TPResult::Ambiguous if it could /// be either a decl-specifier or a function-style cast, and TPResult::Error /// if a parsing error was found and reported. /// /// If InvalidAsDeclSpec is not null, some cases that would be ill-formed as /// declaration specifiers but possibly valid as some other kind of construct /// return TPResult::Ambiguous instead of TPResult::False. When this happens, /// the intent is to keep trying to disambiguate, on the basis that we might /// find a better reason to treat this construct as a declaration later on. /// When this happens and the name could possibly be valid in some other /// syntactic context, *InvalidAsDeclSpec is set to 'true'. The current cases /// that trigger this are: /// /// * When parsing X::Y (with no 'typename') where X is dependent /// * When parsing X<Y> where X is undeclared /// /// decl-specifier: /// storage-class-specifier /// type-specifier /// function-specifier /// 'friend' /// 'typedef' /// [C++11] 'constexpr' /// [C++20] 'consteval' /// [GNU] attributes declaration-specifiers[opt] /// /// storage-class-specifier: /// 'register' /// 'static' /// 'extern' /// 'mutable' /// 'auto' /// [GNU] '__thread' /// [C++11] 'thread_local' /// [C11] '_Thread_local' /// /// function-specifier: /// 'inline' /// 'virtual' /// 'explicit' /// /// typedef-name: /// identifier /// /// type-specifier: /// simple-type-specifier /// class-specifier /// enum-specifier /// elaborated-type-specifier /// typename-specifier /// cv-qualifier /// /// simple-type-specifier: /// '::'[opt] nested-name-specifier[opt] type-name /// '::'[opt] nested-name-specifier 'template' /// simple-template-id [TODO] /// 'char' /// 'wchar_t' /// 'bool' /// 'short' /// 'int' /// 'long' /// 'signed' /// 'unsigned' /// 'float' /// 'double' /// 'void' /// [GNU] typeof-specifier /// [GNU] '_Complex' /// [C++11] 'auto' /// [GNU] '__auto_type' /// [C++11] 'decltype' ( expression ) /// [C++1y] 'decltype' ( 'auto' ) /// /// type-name: /// class-name /// enum-name /// typedef-name /// /// elaborated-type-specifier: /// class-key '::'[opt] nested-name-specifier[opt] identifier /// class-key '::'[opt] nested-name-specifier[opt] 'template'[opt] /// simple-template-id /// 'enum' '::'[opt] nested-name-specifier[opt] identifier /// /// enum-name: /// identifier /// /// enum-specifier: /// 'enum' identifier[opt] '{' enumerator-list[opt] '}' /// 'enum' identifier[opt] '{' enumerator-list ',' '}' /// /// class-specifier: /// class-head '{' member-specification[opt] '}' /// /// class-head: /// class-key identifier[opt] base-clause[opt] /// class-key nested-name-specifier identifier base-clause[opt] /// class-key nested-name-specifier[opt] simple-template-id /// base-clause[opt] /// /// class-key: /// 'class' /// 'struct' /// 'union' /// /// cv-qualifier: /// 'const' /// 'volatile' /// [GNU] restrict /// Parser::TPResult Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, Parser::TPResult BracedCastResult, bool *InvalidAsDeclSpec) { … } bool Parser::isCXXDeclarationSpecifierAType() { … } /// [GNU] typeof-specifier: /// 'typeof' '(' expressions ')' /// 'typeof' '(' type-name ')' /// Parser::TPResult Parser::TryParseTypeofSpecifier() { … } /// [ObjC] protocol-qualifiers: //// '<' identifier-list '>' Parser::TPResult Parser::TryParseProtocolQualifiers() { … } /// isCXXFunctionDeclarator - Disambiguates between a function declarator or /// a constructor-style initializer, when parsing declaration statements. /// Returns true for function declarator and false for constructor-style /// initializer. /// If during the disambiguation process a parsing error is encountered, /// the function returns true to let the declaration parsing code handle it. /// /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// bool Parser::isCXXFunctionDeclarator( bool *IsAmbiguous, ImplicitTypenameContext AllowImplicitTypename) { … } /// parameter-declaration-clause: /// parameter-declaration-list[opt] '...'[opt] /// parameter-declaration-list ',' '...' /// /// parameter-declaration-list: /// parameter-declaration /// parameter-declaration-list ',' parameter-declaration /// /// parameter-declaration: /// attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt] /// attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt] /// '=' assignment-expression /// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] /// attributes[opt] /// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] /// attributes[opt] '=' assignment-expression /// Parser::TPResult Parser::TryParseParameterDeclarationClause( bool *InvalidAsDeclaration, bool VersusTemplateArgument, ImplicitTypenameContext AllowImplicitTypename) { … } /// TryParseFunctionDeclarator - We parsed a '(' and we want to try to continue /// parsing as a function declarator. /// If TryParseFunctionDeclarator fully parsed the function declarator, it will /// return TPResult::Ambiguous, otherwise it will return either False() or /// Error(). /// /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// /// exception-specification: /// 'throw' '(' type-id-list[opt] ')' /// Parser::TPResult Parser::TryParseFunctionDeclarator(bool MayHaveTrailingReturnType) { … } // When parsing an identifier after an arrow it may be a member expression, // in which case we should not annotate it as an independant expression // so we just lookup that name, if it's not a type the construct is not // a function declaration. bool Parser::NameAfterArrowIsNonType() { … } /// '[' constant-expression[opt] ']' /// Parser::TPResult Parser::TryParseBracketDeclarator() { … } /// Determine whether we might be looking at the '<' template-argument-list '>' /// of a template-id or simple-template-id, rather than a less-than comparison. /// This will often fail and produce an ambiguity, but should never be wrong /// if it returns True or False. Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { … } /// Determine whether we might be looking at the '(' of a C++20 explicit(bool) /// in an earlier language mode. Parser::TPResult Parser::isExplicitBool() { … }