//===--- ParseExpr.cpp - Expression 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 // //===----------------------------------------------------------------------===// /// /// \file /// Provides the Expression parsing implementation. /// /// Expressions in C99 basically consist of a bunch of binary operators with /// unary operators and other random stuff at the leaves. /// /// In the C99 grammar, these unary operators bind tightest and are represented /// as the 'cast-expression' production. Everything else is either a binary /// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are /// handled by ParseCastExpression, the higher level pieces are handled by /// ParseBinaryExpression. /// //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" #include "clang/AST/Availability.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/LocInfoType.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaCodeCompletion.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenACC.h" #include "clang/Sema/SemaOpenMP.h" #include "clang/Sema/SemaSYCL.h" #include "clang/Sema/TypoCorrection.h" #include "llvm/ADT/SmallVector.h" #include using namespace clang; ExprResult Parser::ParseExpression(TypoCorrectionTypeBehavior CorrectionBehavior) { ExprResult LHS(ParseAssignmentExpression(CorrectionBehavior)); return ParseRHSOfBinaryExpression(LHS, prec::Comma); } ExprResult Parser::ParseExpressionWithLeadingAt(SourceLocation AtLoc) { ExprResult LHS(ParseObjCAtExpression(AtLoc)); return ParseRHSOfBinaryExpression(LHS, prec::Comma); } ExprResult Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) { ExprResult LHS(true); { // Silence extension warnings in the sub-expression ExtensionRAIIObject O(Diags); LHS = ParseCastExpression(CastParseKind::AnyCastExpr); } if (!LHS.isInvalid()) LHS = Actions.ActOnUnaryOp(getCurScope(), ExtLoc, tok::kw___extension__, LHS.get()); return ParseRHSOfBinaryExpression(LHS, prec::Comma); } ExprResult Parser::ParseAssignmentExpression( TypoCorrectionTypeBehavior CorrectionBehavior) { if (Tok.is(tok::code_completion)) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteExpression( getCurScope(), PreferredType.get(Tok.getLocation())); return ExprError(); } if (Tok.is(tok::kw_throw)) return ParseThrowExpression(); if (Tok.is(tok::kw_co_yield)) return ParseCoyieldExpression(); ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/false, CorrectionBehavior); return ParseRHSOfBinaryExpression(LHS, prec::Assignment); } ExprResult Parser::ParseConditionalExpression() { if (Tok.is(tok::code_completion)) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteExpression( getCurScope(), PreferredType.get(Tok.getLocation())); return ExprError(); } ExprResult LHS = ParseCastExpression( CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/false, TypoCorrectionTypeBehavior::AllowNonTypes); return ParseRHSOfBinaryExpression(LHS, prec::Conditional); } ExprResult Parser::ParseAssignmentExprWithObjCMessageExprStart(SourceLocation LBracLoc, SourceLocation SuperLoc, ParsedType ReceiverType, Expr *ReceiverExpr) { ExprResult R = ParseObjCMessageExpressionBody(LBracLoc, SuperLoc, ReceiverType, ReceiverExpr); R = ParsePostfixExpressionSuffix(R); return ParseRHSOfBinaryExpression(R, prec::Assignment); } ExprResult Parser::ParseConstantExpressionInExprEvalContext( TypoCorrectionTypeBehavior CorrectionBehavior) { assert(Actions.ExprEvalContexts.back().Context == Sema::ExpressionEvaluationContext::ConstantEvaluated && "Call this function only if your ExpressionEvaluationContext is " "already ConstantEvaluated"); ExprResult LHS(ParseCastExpression(CastParseKind::AnyCastExpr, false, CorrectionBehavior)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnConstantExpression(Res); } ExprResult Parser::ParseConstantExpression() { // C++03 [basic.def.odr]p2: // An expression is potentially evaluated unless it appears where an // integral constant expression is required (see 5.19) [...]. // C++98 and C++11 have no such rule, but this is only a defect in C++98. EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); return ParseConstantExpressionInExprEvalContext( TypoCorrectionTypeBehavior::AllowNonTypes); } ExprResult Parser::ParseArrayBoundExpression() { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); // If we parse the bound of a VLA... we parse a non-constant // constant-expression! Actions.ExprEvalContexts.back().InConditionallyConstantEvaluateContext = true; // For a VLA type inside an unevaluated operator like: // // sizeof(typeof(*(int (*)[N])array)) // // N and array are supposed to be ODR-used. // Initially when encountering `array`, it is deemed unevaluated and non-ODR // used because that occurs before parsing the type cast. Therefore we use // Sema::TransformToPotentiallyEvaluated() to rebuild the expression to ensure // it's actually ODR-used. // // However, in other unevaluated contexts as in constraint substitution, it // would end up rebuilding the type twice which is unnecessary. So we push up // a flag to help distinguish these cases. for (auto Iter = Actions.ExprEvalContexts.rbegin() + 1; Iter != Actions.ExprEvalContexts.rend(); ++Iter) { if (!Iter->isUnevaluated()) break; Iter->InConditionallyConstantEvaluateContext = true; } return ParseConstantExpressionInExprEvalContext( TypoCorrectionTypeBehavior::AllowNonTypes); } ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); Actions.currentEvaluationContext().IsCaseExpr = true; ExprResult LHS( ParseCastExpression(CastParseKind::AnyCastExpr, false, TypoCorrectionTypeBehavior::AllowNonTypes)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnCaseExpr(CaseLoc, Res); } ExprResult Parser::ParseConstraintExpression() { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult LHS(ParseCastExpression(CastParseKind::AnyCastExpr)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) { return ExprError(); } return Res; } ExprResult Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated); bool NotPrimaryExpression = false; auto ParsePrimary = [&]() { ExprResult E = ParseCastExpression( CastParseKind::PrimaryExprOnly, /*isAddressOfOperand=*/false, TypoCorrectionTypeBehavior::AllowNonTypes, /*isVectorLiteral=*/false, &NotPrimaryExpression); if (E.isInvalid()) return ExprError(); auto RecoverFromNonPrimary = [&] (ExprResult E, bool Note) { E = ParsePostfixExpressionSuffix(E); // Use InclusiveOr, the precedence just after '&&' to not parse the // next arguments to the logical and. E = ParseRHSOfBinaryExpression(E, prec::InclusiveOr); if (!E.isInvalid()) Diag(E.get()->getExprLoc(), Note ? diag::note_unparenthesized_non_primary_expr_in_requires_clause : diag::err_unparenthesized_non_primary_expr_in_requires_clause) << FixItHint::CreateInsertion(E.get()->getBeginLoc(), "(") << FixItHint::CreateInsertion( PP.getLocForEndOfToken(E.get()->getEndLoc()), ")") << E.get()->getSourceRange(); return E; }; if (NotPrimaryExpression || // Check if the following tokens must be a part of a non-primary // expression getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, /*CPlusPlus11=*/true) > prec::LogicalAnd || // Postfix operators other than '(' (which will be checked for in // CheckConstraintExpression). Tok.isOneOf(tok::period, tok::plusplus, tok::minusminus) || (Tok.is(tok::l_square) && !NextToken().is(tok::l_square))) { E = RecoverFromNonPrimary(E, /*Note=*/false); if (E.isInvalid()) return ExprError(); NotPrimaryExpression = false; } bool PossibleNonPrimary; bool IsConstraintExpr = Actions.CheckConstraintExpression(E.get(), Tok, &PossibleNonPrimary, IsTrailingRequiresClause); if (!IsConstraintExpr || PossibleNonPrimary) { // Atomic constraint might be an unparenthesized non-primary expression // (such as a binary operator), in which case we might get here (e.g. in // 'requires 0 + 1 && true' we would now be at '+', and parse and ignore // the rest of the addition expression). Try to parse the rest of it here. if (PossibleNonPrimary) E = RecoverFromNonPrimary(E, /*Note=*/!IsConstraintExpr); return ExprError(); } return E; }; ExprResult LHS = ParsePrimary(); if (LHS.isInvalid()) return ExprError(); while (Tok.is(tok::ampamp)) { SourceLocation LogicalAndLoc = ConsumeToken(); ExprResult RHS = ParsePrimary(); if (RHS.isInvalid()) { return ExprError(); } ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalAndLoc, tok::ampamp, LHS.get(), RHS.get()); if (!Op.isUsable()) { return ExprError(); } LHS = Op; } return LHS; } ExprResult Parser::ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause) { ExprResult LHS(ParseConstraintLogicalAndExpression(IsTrailingRequiresClause)); if (!LHS.isUsable()) return ExprError(); while (Tok.is(tok::pipepipe)) { SourceLocation LogicalOrLoc = ConsumeToken(); ExprResult RHS = ParseConstraintLogicalAndExpression(IsTrailingRequiresClause); if (!RHS.isUsable()) { return ExprError(); } ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalOrLoc, tok::pipepipe, LHS.get(), RHS.get()); if (!Op.isUsable()) { return ExprError(); } LHS = Op; } return LHS; } bool Parser::isNotExpressionStart() { tok::TokenKind K = Tok.getKind(); if (K == tok::l_brace || K == tok::r_brace || K == tok::kw_for || K == tok::kw_while || K == tok::kw_if || K == tok::kw_else || K == tok::kw_goto || K == tok::kw_try) return true; // If this is a decl-specifier, we can't be at the start of an expression. return isKnownToBeDeclarationSpecifier(); } bool Parser::isFoldOperator(prec::Level Level) const { return Level > prec::Unknown && Level != prec::Conditional && Level != prec::Spaceship; } bool Parser::isFoldOperator(tok::TokenKind Kind) const { return isFoldOperator(getBinOpPrecedence(Kind, GreaterThanIsOperator, true)); } ExprResult Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { prec::Level NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, getLangOpts().CPlusPlus11); SourceLocation ColonLoc; auto SavedType = PreferredType; while (true) { // Every iteration may rely on a preferred type for the whole expression. PreferredType = SavedType; // If this token has a lower precedence than we are allowed to parse (e.g. // because we are called recursively, or because the token is not a binop), // then we are done! if (NextTokPrec < MinPrec) return LHS; // Consume the operator, saving the operator token for error reporting. Token OpToken = Tok; ConsumeToken(); // If we're potentially in a template-id, we may now be able to determine // whether we're actually in one or not. if (OpToken.isOneOf(tok::comma, tok::greater, tok::greatergreater, tok::greatergreatergreater) && checkPotentialAngleBracketDelimiter(OpToken)) return ExprError(); // Bail out when encountering a comma followed by a token which can't // possibly be the start of an expression. For instance: // int f() { return 1, } // We can't do this before consuming the comma, because // isNotExpressionStart() looks at the token stream. if (OpToken.is(tok::comma) && isNotExpressionStart()) { PP.EnterToken(Tok, /*IsReinject*/true); Tok = OpToken; return LHS; } // If the next token is an ellipsis, then this is a fold-expression. Leave // it alone so we can handle it in the paren expression. if (isFoldOperator(NextTokPrec) && Tok.is(tok::ellipsis)) { // FIXME: We can't check this via lookahead before we consume the token // because that tickles a lexer bug. PP.EnterToken(Tok, /*IsReinject*/true); Tok = OpToken; return LHS; } // In Objective-C++, alternative operator tokens can be used as keyword args // in message expressions. Unconsume the token so that it can reinterpreted // as an identifier in ParseObjCMessageExpressionBody. i.e., we support: // [foo meth:0 and:0]; // [foo not_eq]; if (getLangOpts().ObjC && getLangOpts().CPlusPlus && Tok.isOneOf(tok::colon, tok::r_square) && OpToken.getIdentifierInfo() != nullptr) { PP.EnterToken(Tok, /*IsReinject*/true); Tok = OpToken; return LHS; } // Special case handling for the ternary operator. ExprResult TernaryMiddle(true); if (NextTokPrec == prec::Conditional) { if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { // Parse a braced-init-list here for error recovery purposes. SourceLocation BraceLoc = Tok.getLocation(); TernaryMiddle = ParseBraceInitializer(); if (!TernaryMiddle.isInvalid()) { Diag(BraceLoc, diag::err_init_list_bin_op) << /*RHS*/ 1 << PP.getSpelling(OpToken) << Actions.getExprRange(TernaryMiddle.get()); TernaryMiddle = ExprError(); } } else if (Tok.isNot(tok::colon)) { // Don't parse FOO:BAR as if it were a typo for FOO::BAR. ColonProtectionRAIIObject X(*this); // Handle this production specially: // logical-OR-expression '?' expression ':' conditional-expression // In particular, the RHS of the '?' is 'expression', not // 'logical-OR-expression' as we might expect. TernaryMiddle = ParseExpression(); } else { // Special case handling of "X ? Y : Z" where Y is empty: // logical-OR-expression '?' ':' conditional-expression [GNU] TernaryMiddle = nullptr; Diag(Tok, diag::ext_gnu_conditional_expr); } if (TernaryMiddle.isInvalid()) { LHS = ExprError(); TernaryMiddle = nullptr; } if (!TryConsumeToken(tok::colon, ColonLoc)) { // Otherwise, we're missing a ':'. Assume that this was a typo that // the user forgot. If we're not in a macro expansion, we can suggest // a fixit hint. If there were two spaces before the current token, // suggest inserting the colon in between them, otherwise insert ": ". SourceLocation FILoc = Tok.getLocation(); const char *FIText = ": "; const SourceManager &SM = PP.getSourceManager(); if (FILoc.isFileID() || PP.isAtStartOfMacroExpansion(FILoc, &FILoc)) { assert(FILoc.isFileID()); bool IsInvalid = false; const char *SourcePtr = SM.getCharacterData(FILoc.getLocWithOffset(-1), &IsInvalid); if (!IsInvalid && *SourcePtr == ' ') { SourcePtr = SM.getCharacterData(FILoc.getLocWithOffset(-2), &IsInvalid); if (!IsInvalid && *SourcePtr == ' ') { FILoc = FILoc.getLocWithOffset(-1); FIText = ":"; } } } Diag(Tok, diag::err_expected) << tok::colon << FixItHint::CreateInsertion(FILoc, FIText); Diag(OpToken, diag::note_matching) << tok::question; ColonLoc = Tok.getLocation(); } } PreferredType.enterBinary(Actions, Tok.getLocation(), LHS.get(), OpToken.getKind()); // Parse another leaf here for the RHS of the operator. // ParseCastExpression works here because all RHS expressions in C have it // as a prefix, at least. However, in C++, an assignment-expression could // be a throw-expression, which is not a valid cast-expression. // Therefore we need some special-casing here. // Also note that the third operand of the conditional operator is // an assignment-expression in C++, and in C++11, we can have a // braced-init-list on the RHS of an assignment. For better diagnostics, // parse as if we were allowed braced-init-lists everywhere, and check that // they only appear on the RHS of assignments later. ExprResult RHS; bool RHSIsInitList = false; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { RHS = ParseBraceInitializer(); RHSIsInitList = true; } else if (getLangOpts().CPlusPlus && NextTokPrec <= prec::Conditional) RHS = ParseAssignmentExpression(); else RHS = ParseCastExpression(CastParseKind::AnyCastExpr); if (RHS.isInvalid()) { LHS = ExprError(); } // Remember the precedence of this operator and get the precedence of the // operator immediately to the right of the RHS. prec::Level ThisPrec = NextTokPrec; NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, getLangOpts().CPlusPlus11); // Assignment and conditional expressions are right-associative. bool isRightAssoc = ThisPrec == prec::Conditional || ThisPrec == prec::Assignment; // Get the precedence of the operator to the right of the RHS. If it binds // more tightly with RHS than we do, evaluate it completely first. if (ThisPrec < NextTokPrec || (ThisPrec == NextTokPrec && isRightAssoc)) { if (!RHS.isInvalid() && RHSIsInitList) { Diag(Tok, diag::err_init_list_bin_op) << /*LHS*/0 << PP.getSpelling(Tok) << Actions.getExprRange(RHS.get()); RHS = ExprError(); } // If this is left-associative, only parse things on the RHS that bind // more tightly than the current operator. If it is right-associative, it // is okay, to bind exactly as tightly. For example, compile A=B=C=D as // A=(B=(C=D)), where each paren is a level of recursion here. // The function takes ownership of the RHS. RHS = ParseRHSOfBinaryExpression(RHS, static_cast(ThisPrec + !isRightAssoc)); RHSIsInitList = false; if (RHS.isInvalid()) { LHS = ExprError(); } NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, getLangOpts().CPlusPlus11); } if (!RHS.isInvalid() && RHSIsInitList) { if (ThisPrec == prec::Assignment) { Diag(OpToken, diag::warn_cxx98_compat_generalized_initializer_lists) << Actions.getExprRange(RHS.get()); } else if (ColonLoc.isValid()) { Diag(ColonLoc, diag::err_init_list_bin_op) << /*RHS*/1 << ":" << Actions.getExprRange(RHS.get()); LHS = ExprError(); } else { Diag(OpToken, diag::err_init_list_bin_op) << /*RHS*/1 << PP.getSpelling(OpToken) << Actions.getExprRange(RHS.get()); LHS = ExprError(); } } if (!LHS.isInvalid()) { // Combine the LHS and RHS into the LHS (e.g. build AST). if (TernaryMiddle.isInvalid()) { // If we're using '>>' as an operator within a template // argument list (in C++98), suggest the addition of // parentheses so that the code remains well-formed in C++0x. if (!GreaterThanIsOperator && OpToken.is(tok::greatergreater)) SuggestParentheses(OpToken.getLocation(), diag::warn_cxx11_right_shift_in_template_arg, SourceRange(Actions.getExprRange(LHS.get()).getBegin(), Actions.getExprRange(RHS.get()).getEnd())); ExprResult BinOp = Actions.ActOnBinOp(getCurScope(), OpToken.getLocation(), OpToken.getKind(), LHS.get(), RHS.get()); if (BinOp.isInvalid()) BinOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(), RHS.get()->getEndLoc(), {LHS.get(), RHS.get()}); LHS = BinOp; } else { ExprResult CondOp = Actions.ActOnConditionalOp( OpToken.getLocation(), ColonLoc, LHS.get(), TernaryMiddle.get(), RHS.get()); if (CondOp.isInvalid()) { std::vector Args; // TernaryMiddle can be null for the GNU conditional expr extension. if (TernaryMiddle.get()) Args = {LHS.get(), TernaryMiddle.get(), RHS.get()}; else Args = {LHS.get(), RHS.get()}; CondOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(), RHS.get()->getEndLoc(), Args); } LHS = CondOp; } } } } ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, TypoCorrectionTypeBehavior CorrectionBehavior, bool isVectorLiteral, bool *NotPrimaryExpression) { bool NotCastExpr; ExprResult Res = ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); if (NotCastExpr) Diag(Tok, diag::err_expected_expression); return Res; } namespace { class CastExpressionIdValidator final : public CorrectionCandidateCallback { public: CastExpressionIdValidator(Token Next, TypoCorrectionTypeBehavior CorrectionBehavior) : NextToken(Next) { WantTypeSpecifiers = WantFunctionLikeCasts = (CorrectionBehavior != TypoCorrectionTypeBehavior::AllowNonTypes); AllowNonTypes = (CorrectionBehavior != TypoCorrectionTypeBehavior::AllowTypes); } bool ValidateCandidate(const TypoCorrection &candidate) override { NamedDecl *ND = candidate.getCorrectionDecl(); if (!ND) return candidate.isKeyword(); if (isa(ND)) return WantTypeSpecifiers; if (!AllowNonTypes || !CorrectionCandidateCallback::ValidateCandidate(candidate)) return false; if (!NextToken.isOneOf(tok::equal, tok::arrow, tok::period)) return true; for (auto *C : candidate) { NamedDecl *ND = C->getUnderlyingDecl(); if (isa(ND) && !isa(ND)) return true; } return false; } std::unique_ptr clone() override { return std::make_unique(*this); } private: Token NextToken; bool AllowNonTypes; }; } bool Parser::isRevertibleTypeTrait(const IdentifierInfo *II, tok::TokenKind *Kind) { if (RevertibleTypeTraits.empty()) { // Revertible type trait is a feature for backwards compatibility with older // standard libraries that declare their own structs with the same name as // the builtins listed below. New builtins should NOT be added to this list. #define RTT_JOIN(X, Y) X##Y #define REVERTIBLE_TYPE_TRAIT(Name) \ RevertibleTypeTraits[PP.getIdentifierInfo(#Name)] = RTT_JOIN(tok::kw_, Name) REVERTIBLE_TYPE_TRAIT(__is_abstract); REVERTIBLE_TYPE_TRAIT(__is_aggregate); REVERTIBLE_TYPE_TRAIT(__is_arithmetic); REVERTIBLE_TYPE_TRAIT(__is_array); REVERTIBLE_TYPE_TRAIT(__is_assignable); REVERTIBLE_TYPE_TRAIT(__is_base_of); REVERTIBLE_TYPE_TRAIT(__is_bounded_array); REVERTIBLE_TYPE_TRAIT(__is_class); REVERTIBLE_TYPE_TRAIT(__is_complete_type); REVERTIBLE_TYPE_TRAIT(__is_compound); REVERTIBLE_TYPE_TRAIT(__is_const); REVERTIBLE_TYPE_TRAIT(__is_constructible); REVERTIBLE_TYPE_TRAIT(__is_convertible); REVERTIBLE_TYPE_TRAIT(__is_convertible_to); REVERTIBLE_TYPE_TRAIT(__is_destructible); REVERTIBLE_TYPE_TRAIT(__is_empty); REVERTIBLE_TYPE_TRAIT(__is_enum); REVERTIBLE_TYPE_TRAIT(__is_floating_point); REVERTIBLE_TYPE_TRAIT(__is_final); REVERTIBLE_TYPE_TRAIT(__is_function); REVERTIBLE_TYPE_TRAIT(__is_fundamental); REVERTIBLE_TYPE_TRAIT(__is_integral); REVERTIBLE_TYPE_TRAIT(__is_interface_class); REVERTIBLE_TYPE_TRAIT(__is_literal); REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr); REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference); REVERTIBLE_TYPE_TRAIT(__is_member_function_pointer); REVERTIBLE_TYPE_TRAIT(__is_member_object_pointer); REVERTIBLE_TYPE_TRAIT(__is_member_pointer); REVERTIBLE_TYPE_TRAIT(__is_nothrow_assignable); REVERTIBLE_TYPE_TRAIT(__is_nothrow_constructible); REVERTIBLE_TYPE_TRAIT(__is_nothrow_destructible); REVERTIBLE_TYPE_TRAIT(__is_object); REVERTIBLE_TYPE_TRAIT(__is_pod); REVERTIBLE_TYPE_TRAIT(__is_pointer); REVERTIBLE_TYPE_TRAIT(__is_polymorphic); REVERTIBLE_TYPE_TRAIT(__is_reference); REVERTIBLE_TYPE_TRAIT(__is_rvalue_expr); REVERTIBLE_TYPE_TRAIT(__is_rvalue_reference); REVERTIBLE_TYPE_TRAIT(__is_same); REVERTIBLE_TYPE_TRAIT(__is_scalar); REVERTIBLE_TYPE_TRAIT(__is_scoped_enum); REVERTIBLE_TYPE_TRAIT(__is_sealed); REVERTIBLE_TYPE_TRAIT(__is_signed); REVERTIBLE_TYPE_TRAIT(__is_standard_layout); REVERTIBLE_TYPE_TRAIT(__is_trivial); REVERTIBLE_TYPE_TRAIT(__is_trivially_assignable); REVERTIBLE_TYPE_TRAIT(__is_trivially_constructible); REVERTIBLE_TYPE_TRAIT(__is_trivially_copyable); REVERTIBLE_TYPE_TRAIT(__is_unbounded_array); REVERTIBLE_TYPE_TRAIT(__is_union); REVERTIBLE_TYPE_TRAIT(__is_unsigned); REVERTIBLE_TYPE_TRAIT(__is_void); REVERTIBLE_TYPE_TRAIT(__is_volatile); REVERTIBLE_TYPE_TRAIT(__reference_binds_to_temporary); #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \ REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait)); #include "clang/Basic/TransformTypeTraits.def" #undef REVERTIBLE_TYPE_TRAIT #undef RTT_JOIN } llvm::SmallDenseMap::iterator Known = RevertibleTypeTraits.find(II); if (Known != RevertibleTypeTraits.end()) { if (Kind) *Kind = Known->second; return true; } return false; } ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() { SourceLocation Loc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); if (T.expectAndConsume()) return ExprError(); TypeResult Ty = ParseTypeName(); if (Ty.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } SourceLocation EndLoc = Tok.getLocation(); T.consumeClose(); return Actions.ActOnUnaryExprOrTypeTraitExpr( Loc, UETT_PtrAuthTypeDiscriminator, /*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc)); } ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, bool &NotCastExpr, TypoCorrectionTypeBehavior CorrectionBehavior, bool isVectorLiteral, bool *NotPrimaryExpression) { ExprResult Res; tok::TokenKind SavedKind = Tok.getKind(); auto SavedType = PreferredType; NotCastExpr = false; // Are postfix-expression suffix operators permitted after this // cast-expression? If not, and we find some, we'll parse them anyway and // diagnose them. bool AllowSuffix = true; // This handles all of cast-expression, unary-expression, postfix-expression, // and primary-expression. We handle them together like this for efficiency // and to simplify handling of an expression starting with a '(' token: which // may be one of a parenthesized expression, cast-expression, compound literal // expression, or statement expression. // // If the parsed tokens consist of a primary-expression, the cases below // break out of the switch; at the end we call ParsePostfixExpressionSuffix // to handle the postfix expression suffixes. Cases that cannot be followed // by postfix exprs should set AllowSuffix to false. switch (SavedKind) { case tok::l_paren: { // If this expression is limited to being a unary-expression, the paren can // not start a cast expression. ParenParseOption ParenExprType; switch (ParseKind) { case CastParseKind::UnaryExprOnly: assert(getLangOpts().CPlusPlus && "not possible to get here in C"); [[fallthrough]]; case CastParseKind::AnyCastExpr: ParenExprType = ParenParseOption::CastExpr; break; case CastParseKind::PrimaryExprOnly: ParenExprType = ParenParseOption::FoldExpr; break; } ParsedType CastTy; SourceLocation RParenLoc; Res = ParseParenExpression(ParenExprType, /*StopIfCastExr=*/false, ParenExprKind::Unknown, CorrectionBehavior, CastTy, RParenLoc); // FIXME: What should we do if a vector literal is followed by a // postfix-expression suffix? Usually postfix operators are permitted on // literals. if (isVectorLiteral) return Res; switch (ParenExprType) { case ParenParseOption::SimpleExpr: break; // Nothing else to do. case ParenParseOption::CompoundStmt: break; // Nothing else to do. case ParenParseOption::CompoundLiteral: // We parsed '(' type-name ')' '{' ... '}'. If any suffixes of // postfix-expression exist, parse them now. break; case ParenParseOption::CastExpr: // We have parsed the cast-expression and no postfix-expr pieces are // following. return Res; case ParenParseOption::FoldExpr: // We only parsed a fold-expression. There might be postfix-expr pieces // afterwards; parse them now. break; } break; } // primary-expression case tok::numeric_constant: case tok::binary_data: // constant: integer-constant // constant: floating-constant Res = Actions.ActOnNumericConstant(Tok, /*UDLScope*/getCurScope()); ConsumeToken(); break; case tok::kw_true: case tok::kw_false: Res = ParseCXXBoolLiteral(); break; case tok::kw___objc_yes: case tok::kw___objc_no: Res = ParseObjCBoolLiteral(); break; case tok::kw_nullptr: if (getLangOpts().CPlusPlus) Diag(Tok, diag::warn_cxx98_compat_nullptr); else Diag(Tok, getLangOpts().C23 ? diag::warn_c23_compat_keyword : diag::ext_c_nullptr) << Tok.getName(); Res = Actions.ActOnCXXNullPtrLiteral(ConsumeToken()); break; case tok::annot_primary_expr: case tok::annot_overload_set: Res = getExprAnnotation(Tok); if (!Res.isInvalid() && Tok.getKind() == tok::annot_overload_set) Res = Actions.ActOnNameClassifiedAsOverloadSet(getCurScope(), Res.get()); ConsumeAnnotationToken(); if (!Res.isInvalid() && Tok.is(tok::less)) checkPotentialAngleBracket(Res); break; case tok::annot_non_type: case tok::annot_non_type_dependent: case tok::annot_non_type_undeclared: { CXXScopeSpec SS; Token Replacement; Res = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement); assert(!Res.isUnset() && "should not perform typo correction on annotation token"); break; } case tok::annot_embed: { injectEmbedTokens(); return ParseCastExpression(ParseKind, isAddressOfOperand, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } case tok::kw___super: case tok::kw_decltype: // Annotate the token and tail recurse. if (TryAnnotateTypeOrScopeToken()) return ExprError(); assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super)); return ParseCastExpression(ParseKind, isAddressOfOperand, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); case tok::identifier: ParseIdentifier: { // primary-expression: identifier // unqualified-id: identifier // constant: enumeration-constant // Turn a potentially qualified name into a annot_typename or // annot_cxxscope if it would be valid. This handles things like x::y, etc. if (getLangOpts().CPlusPlus) { // Avoid the unnecessary parse-time lookup in the common case // where the syntax forbids a type. Token Next = NextToken(); if (Next.is(tok::ellipsis) && Tok.is(tok::identifier) && GetLookAheadToken(2).is(tok::l_square)) { // Annotate the token and tail recurse. // If the token is not annotated, then it might be an expression pack // indexing if (!TryAnnotateTypeOrScopeToken() && Tok.isOneOf(tok::annot_pack_indexing_type, tok::annot_cxxscope)) return ParseCastExpression(ParseKind, isAddressOfOperand, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } // If this identifier was reverted from a token ID, and the next token // is a parenthesis, this is likely to be a use of a type trait. Check // those tokens. else if (Next.is(tok::l_paren) && Tok.is(tok::identifier) && Tok.getIdentifierInfo()->hasRevertedTokenIDToIdentifier()) { IdentifierInfo *II = Tok.getIdentifierInfo(); tok::TokenKind Kind; if (isRevertibleTypeTrait(II, &Kind)) { Tok.setKind(Kind); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } } else if ((!ColonIsSacred && Next.is(tok::colon)) || Next.isOneOf(tok::coloncolon, tok::less, tok::l_paren, tok::l_brace)) { // If TryAnnotateTypeOrScopeToken annotates the token, tail recurse. if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::identifier)) return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } } // Consume the identifier so that we can see if it is followed by a '(' or // '.'. IdentifierInfo &II = *Tok.getIdentifierInfo(); SourceLocation ILoc = ConsumeToken(); // Support 'Class.property' and 'super.property' notation. if (getLangOpts().ObjC && Tok.is(tok::period) && (Actions.getTypeName(II, ILoc, getCurScope()) || // Allow the base to be 'super' if in an objc-method. (&II == Ident_super && getCurScope()->isInObjcMethodScope()))) { ConsumeToken(); if (Tok.is(tok::code_completion) && &II != Ident_super) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteObjCClassPropertyRefExpr( getCurScope(), II, ILoc, ExprStatementTokLoc == ILoc); return ExprError(); } // Allow either an identifier or the keyword 'class' (in C++). if (Tok.isNot(tok::identifier) && !(getLangOpts().CPlusPlus && Tok.is(tok::kw_class))) { Diag(Tok, diag::err_expected_property_name); return ExprError(); } IdentifierInfo &PropertyName = *Tok.getIdentifierInfo(); SourceLocation PropertyLoc = ConsumeToken(); Res = Actions.ObjC().ActOnClassPropertyRefExpr(II, PropertyName, ILoc, PropertyLoc); break; } // In an Objective-C method, if we have "super" followed by an identifier, // the token sequence is ill-formed. However, if there's a ':' or ']' after // that identifier, this is probably a message send with a missing open // bracket. Treat it as such. if (getLangOpts().ObjC && &II == Ident_super && !InMessageExpression && getCurScope()->isInObjcMethodScope() && ((Tok.is(tok::identifier) && (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) || Tok.is(tok::code_completion))) { Res = ParseObjCMessageExpressionBody(SourceLocation(), ILoc, nullptr, nullptr); break; } // If we have an Objective-C class name followed by an identifier // and either ':' or ']', this is an Objective-C class message // send that's missing the opening '['. Recovery // appropriately. Also take this path if we're performing code // completion after an Objective-C class name. if (getLangOpts().ObjC && ((Tok.is(tok::identifier) && !InMessageExpression) || Tok.is(tok::code_completion))) { const Token& Next = NextToken(); if (Tok.is(tok::code_completion) || Next.is(tok::colon) || Next.is(tok::r_square)) if (ParsedType Typ = Actions.getTypeName(II, ILoc, getCurScope())) if (Typ.get()->isObjCObjectOrInterfaceType()) { // Fake up a Declarator to use with ActOnTypeName. DeclSpec DS(AttrFactory); DS.SetRangeStart(ILoc); DS.SetRangeEnd(ILoc); const char *PrevSpec = nullptr; unsigned DiagID; DS.SetTypeSpecType(TST_typename, ILoc, PrevSpec, DiagID, Typ, Actions.getASTContext().getPrintingPolicy()); Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); TypeResult Ty = Actions.ActOnTypeName(DeclaratorInfo); if (Ty.isInvalid()) break; Res = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(), Ty.get(), nullptr); break; } } // Make sure to pass down the right value for isAddressOfOperand. if (isAddressOfOperand && isPostfixExpressionSuffixStart()) isAddressOfOperand = false; // Function designators are allowed to be undeclared (C99 6.5.1p2), so we // need to know whether or not this identifier is a function designator or // not. UnqualifiedId Name; CXXScopeSpec ScopeSpec; SourceLocation TemplateKWLoc; Token Replacement; CastExpressionIdValidator Validator(Tok, CorrectionBehavior); Validator.IsAddressOfOperand = isAddressOfOperand; if (Tok.isOneOf(tok::periodstar, tok::arrowstar)) { Validator.WantExpressionKeywords = false; Validator.WantRemainingKeywords = false; } else { Validator.WantRemainingKeywords = Tok.isNot(tok::r_paren); } Name.setIdentifier(&II, ILoc); Res = Actions.ActOnIdExpression( getCurScope(), ScopeSpec, TemplateKWLoc, Name, Tok.is(tok::l_paren), isAddressOfOperand, &Validator, /*IsInlineAsmIdentifier=*/false, Tok.is(tok::r_paren) ? nullptr : &Replacement); if (!Res.isInvalid() && Res.isUnset()) { UnconsumeToken(Replacement); return ParseCastExpression( ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, /*isVectorLiteral=*/false, NotPrimaryExpression); } Res = tryParseCXXPackIndexingExpression(Res); if (!Res.isInvalid() && Tok.is(tok::less)) checkPotentialAngleBracket(Res); break; } case tok::char_constant: // constant: character-constant case tok::wide_char_constant: case tok::utf8_char_constant: case tok::utf16_char_constant: case tok::utf32_char_constant: Res = Actions.ActOnCharacterConstant(Tok, /*UDLScope*/getCurScope()); ConsumeToken(); break; case tok::kw___func__: // primary-expression: __func__ [C99 6.4.2.2] case tok::kw___FUNCTION__: // primary-expression: __FUNCTION__ [GNU] case tok::kw___FUNCDNAME__: // primary-expression: __FUNCDNAME__ [MS] case tok::kw___FUNCSIG__: // primary-expression: __FUNCSIG__ [MS] case tok::kw_L__FUNCTION__: // primary-expression: L__FUNCTION__ [MS] case tok::kw_L__FUNCSIG__: // primary-expression: L__FUNCSIG__ [MS] case tok::kw___PRETTY_FUNCTION__: // primary-expression: __P..Y_F..N__ [GNU] // Function local predefined macros are represented by PredefinedExpr except // when Microsoft extensions are enabled and one of these macros is adjacent // to a string literal or another one of these macros. if (!(getLangOpts().MicrosoftExt && tokenIsLikeStringLiteral(Tok, getLangOpts()) && tokenIsLikeStringLiteral(NextToken(), getLangOpts()))) { Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), SavedKind); ConsumeToken(); break; } [[fallthrough]]; // treat MS function local macros as concatenable strings case tok::string_literal: // primary-expression: string-literal case tok::wide_string_literal: case tok::utf8_string_literal: case tok::utf16_string_literal: case tok::utf32_string_literal: Res = ParseStringLiteralExpression(true); break; case tok::kw__Generic: // primary-expression: generic-selection [C11 6.5.1] Res = ParseGenericSelectionExpression(); break; case tok::kw___builtin_available: Res = ParseAvailabilityCheckExpr(Tok.getLocation()); break; case tok::kw___builtin_va_arg: case tok::kw___builtin_offsetof: case tok::kw___builtin_choose_expr: case tok::kw___builtin_astype: // primary-expression: [OCL] as_type() case tok::kw___builtin_convertvector: case tok::kw___builtin_COLUMN: case tok::kw___builtin_FILE: case tok::kw___builtin_FILE_NAME: case tok::kw___builtin_FUNCTION: case tok::kw___builtin_FUNCSIG: case tok::kw___builtin_LINE: case tok::kw___builtin_source_location: if (NotPrimaryExpression) *NotPrimaryExpression = true; // This parses the complete suffix; we can return early. return ParseBuiltinPrimaryExpression(); case tok::kw___null: Res = Actions.ActOnGNUNullExpr(ConsumeToken()); break; case tok::plusplus: // unary-expression: '++' unary-expression [C99] case tok::minusminus: { // unary-expression: '--' unary-expression [C99] if (NotPrimaryExpression) *NotPrimaryExpression = true; // C++ [expr.unary] has: // unary-expression: // ++ cast-expression // -- cast-expression Token SavedTok = Tok; ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), SavedTok.getKind(), SavedTok.getLocation()); // One special case is implicitly handled here: if the preceding tokens are // an ambiguous cast expression, such as "(T())++", then we recurse to // determine whether the '++' is prefix or postfix. Res = ParseCastExpression(getLangOpts().CPlusPlus ? CastParseKind::UnaryExprOnly : CastParseKind::AnyCastExpr, /*isAddressOfOperand*/ false, NotCastExpr, TypoCorrectionTypeBehavior::AllowNonTypes); if (NotCastExpr) { // If we return with NotCastExpr = true, we must not consume any tokens, // so put the token back where we found it. assert(Res.isInvalid()); UnconsumeToken(SavedTok); return ExprError(); } if (!Res.isInvalid()) { Expr *Arg = Res.get(); Res = Actions.ActOnUnaryOp(getCurScope(), SavedTok.getLocation(), SavedKind, Arg); if (Res.isInvalid()) Res = Actions.CreateRecoveryExpr(SavedTok.getLocation(), Arg->getEndLoc(), Arg); } return Res; } case tok::amp: { // unary-expression: '&' cast-expression if (NotPrimaryExpression) *NotPrimaryExpression = true; // Special treatment because of member pointers SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc); Res = ParseCastExpression(CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/true); if (!Res.isInvalid()) { Expr *Arg = Res.get(); Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg); if (Res.isInvalid()) Res = Actions.CreateRecoveryExpr(Tok.getLocation(), Arg->getEndLoc(), Arg); } return Res; } case tok::star: // unary-expression: '*' cast-expression case tok::plus: // unary-expression: '+' cast-expression case tok::minus: // unary-expression: '-' cast-expression case tok::tilde: // unary-expression: '~' cast-expression case tok::exclaim: // unary-expression: '!' cast-expression case tok::kw___real: // unary-expression: '__real' cast-expression [GNU] case tok::kw___imag: { // unary-expression: '__imag' cast-expression [GNU] if (NotPrimaryExpression) *NotPrimaryExpression = true; SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc); Res = ParseCastExpression(CastParseKind::AnyCastExpr); if (!Res.isInvalid()) { Expr *Arg = Res.get(); Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg, isAddressOfOperand); if (Res.isInvalid()) Res = Actions.CreateRecoveryExpr(SavedLoc, Arg->getEndLoc(), Arg); } return Res; } case tok::kw_co_await: { // unary-expression: 'co_await' cast-expression if (NotPrimaryExpression) *NotPrimaryExpression = true; SourceLocation CoawaitLoc = ConsumeToken(); Res = ParseCastExpression(CastParseKind::AnyCastExpr); if (!Res.isInvalid()) Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get()); return Res; } case tok::kw___extension__:{//unary-expression:'__extension__' cast-expr [GNU] // __extension__ silences extension warnings in the subexpression. if (NotPrimaryExpression) *NotPrimaryExpression = true; ExtensionRAIIObject O(Diags); // Use RAII to do this. SourceLocation SavedLoc = ConsumeToken(); Res = ParseCastExpression(CastParseKind::AnyCastExpr); if (!Res.isInvalid()) Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); return Res; } case tok::kw__Alignof: // unary-expression: '_Alignof' '(' type-name ')' diagnoseUseOfC11Keyword(Tok); [[fallthrough]]; case tok::kw_alignof: // unary-expression: 'alignof' '(' type-id ')' case tok::kw___alignof: // unary-expression: '__alignof' unary-expression // unary-expression: '__alignof' '(' type-name ')' case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression // unary-expression: 'sizeof' '(' type-name ')' // unary-expression: '__datasizeof' unary-expression // unary-expression: '__datasizeof' '(' type-name ')' case tok::kw___datasizeof: case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')' case tok::kw___builtin_omp_required_simd_align: case tok::kw___builtin_vectorelements: case tok::kw__Countof: if (NotPrimaryExpression) *NotPrimaryExpression = true; AllowSuffix = false; Res = ParseUnaryExprOrTypeTraitExpression(); break; case tok::ampamp: { // unary-expression: '&&' identifier if (NotPrimaryExpression) *NotPrimaryExpression = true; SourceLocation AmpAmpLoc = ConsumeToken(); if (Tok.isNot(tok::identifier)) return ExprError(Diag(Tok, diag::err_expected) << tok::identifier); if (getCurScope()->getFnParent() == nullptr) return ExprError(Diag(Tok, diag::err_address_of_label_outside_fn)); Diag(AmpAmpLoc, diag::ext_gnu_address_of_label); LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(), Tok.getLocation()); Res = Actions.ActOnAddrLabel(AmpAmpLoc, Tok.getLocation(), LD); ConsumeToken(); AllowSuffix = false; break; } case tok::kw_const_cast: case tok::kw_dynamic_cast: case tok::kw_reinterpret_cast: case tok::kw_static_cast: case tok::kw_addrspace_cast: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXCasts(); break; case tok::kw___builtin_bit_cast: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseBuiltinBitCast(); break; case tok::kw_typeid: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXTypeid(); break; case tok::kw___uuidof: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXUuidof(); break; case tok::kw_this: Res = ParseCXXThis(); break; case tok::kw___builtin_sycl_unique_stable_name: Res = ParseSYCLUniqueStableNameExpression(); break; case tok::annot_typename: if (isStartOfObjCClassMessageMissingOpenBracket()) { TypeResult Type = getTypeAnnotation(Tok); // Fake up a Declarator to use with ActOnTypeName. DeclSpec DS(AttrFactory); DS.SetRangeStart(Tok.getLocation()); DS.SetRangeEnd(Tok.getLastLoc()); const char *PrevSpec = nullptr; unsigned DiagID; DS.SetTypeSpecType(TST_typename, Tok.getAnnotationEndLoc(), PrevSpec, DiagID, Type, Actions.getASTContext().getPrintingPolicy()); Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); TypeResult Ty = Actions.ActOnTypeName(DeclaratorInfo); if (Ty.isInvalid()) break; ConsumeAnnotationToken(); Res = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(), Ty.get(), nullptr); break; } [[fallthrough]]; case tok::annot_decltype: case tok::annot_pack_indexing_type: case tok::kw_char: case tok::kw_wchar_t: case tok::kw_char8_t: case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw_bool: case tok::kw_short: case tok::kw_int: case tok::kw_long: case tok::kw___int64: case tok::kw___int128: case tok::kw__ExtInt: case tok::kw__BitInt: case tok::kw_signed: case tok::kw_unsigned: case tok::kw_half: case tok::kw_float: case tok::kw_double: case tok::kw___bf16: case tok::kw__Float16: case tok::kw___float128: case tok::kw___ibm128: case tok::kw_void: case tok::kw_auto: case tok::kw_typename: case tok::kw_typeof: case tok::kw___vector: case tok::kw__Accum: case tok::kw__Fract: case tok::kw__Sat: #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: #include "clang/Basic/OpenCLImageTypes.def" #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case tok::kw_##Name: #include "clang/Basic/HLSLIntangibleTypes.def" { if (!getLangOpts().CPlusPlus) { Diag(Tok, diag::err_expected_expression); return ExprError(); } // Everything henceforth is a postfix-expression. if (NotPrimaryExpression) *NotPrimaryExpression = true; if (SavedKind == tok::kw_typename) { // postfix-expression: typename-specifier '(' expression-list[opt] ')' // typename-specifier braced-init-list if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.isSimpleTypeSpecifier(getLangOpts())) // We are trying to parse a simple-type-specifier but might not get such // a token after error recovery. return ExprError(); } // postfix-expression: simple-type-specifier '(' expression-list[opt] ')' // simple-type-specifier braced-init-list // DeclSpec DS(AttrFactory); ParseCXXSimpleTypeSpecifier(DS); if (Tok.isNot(tok::l_paren) && (!getLangOpts().CPlusPlus11 || Tok.isNot(tok::l_brace))) return ExprError(Diag(Tok, diag::err_expected_lparen_after_type) << DS.getSourceRange()); if (Tok.is(tok::l_brace)) Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); Res = ParseCXXTypeConstructExpression(DS); break; } case tok::annot_cxxscope: { // [C++] id-expression: qualified-id // If TryAnnotateTypeOrScopeToken annotates the token, tail recurse. // (We can end up in this situation after tentative parsing.) if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::annot_cxxscope)) return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); Token Next = NextToken(); if (Next.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next); if (TemplateId->Kind == TNK_Type_template) { // We have a qualified template-id that we know refers to a // type, translate it into a type and continue parsing as a // cast expression. CXXScopeSpec SS; ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, /*EnteringContext=*/false); AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } } // Parse as an id-expression. Res = ParseCXXIdExpression(isAddressOfOperand); break; } case tok::annot_template_id: { // [C++] template-id TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template) { // We have a template-id that we know refers to a type, // translate it into a type and continue parsing as a cast // expression. CXXScopeSpec SS; AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } // Fall through to treat the template-id as an id-expression. [[fallthrough]]; } case tok::kw_operator: // [C++] id-expression: operator/conversion-function-id Res = ParseCXXIdExpression(isAddressOfOperand); break; case tok::coloncolon: { // ::foo::bar -> global qualified name etc. If TryAnnotateTypeOrScopeToken // annotates the token, tail recurse. if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::coloncolon)) return ParseCastExpression(ParseKind, isAddressOfOperand, CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); // ::new -> [C++] new-expression // ::delete -> [C++] delete-expression SourceLocation CCLoc = ConsumeToken(); if (Tok.is(tok::kw_new)) { if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXNewExpression(true, CCLoc); AllowSuffix = false; break; } if (Tok.is(tok::kw_delete)) { if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXDeleteExpression(true, CCLoc); AllowSuffix = false; break; } // This is not a type name or scope specifier, it is an invalid expression. Diag(CCLoc, diag::err_expected_expression); return ExprError(); } case tok::kw_new: // [C++] new-expression if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXNewExpression(false, Tok.getLocation()); AllowSuffix = false; break; case tok::kw_delete: // [C++] delete-expression if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXDeleteExpression(false, Tok.getLocation()); AllowSuffix = false; break; case tok::kw_requires: // [C++2a] requires-expression Res = ParseRequiresExpression(); AllowSuffix = false; break; case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (NotPrimaryExpression) *NotPrimaryExpression = true; Diag(Tok, diag::warn_cxx98_compat_noexcept_expr); SourceLocation KeyLoc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); if (T.expectAndConsume(diag::err_expected_lparen_after, "noexcept")) return ExprError(); // C++11 [expr.unary.noexcept]p1: // The noexcept operator determines whether the evaluation of its operand, // which is an unevaluated operand, can throw an exception. EnterExpressionEvaluationContext Unevaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated); Res = ParseExpression(); T.consumeClose(); if (!Res.isInvalid()) Res = Actions.ActOnNoexceptExpr(KeyLoc, T.getOpenLocation(), Res.get(), T.getCloseLocation()); AllowSuffix = false; break; } #define TYPE_TRAIT(N,Spelling,K) \ case tok::kw_##Spelling: #include "clang/Basic/TokenKinds.def" Res = ParseTypeTrait(); break; case tok::kw___array_rank: case tok::kw___array_extent: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseArrayTypeTrait(); break; case tok::kw___builtin_ptrauth_type_discriminator: return ParseBuiltinPtrauthTypeDiscriminator(); case tok::kw___is_lvalue_expr: case tok::kw___is_rvalue_expr: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseExpressionTrait(); break; case tok::at: { if (NotPrimaryExpression) *NotPrimaryExpression = true; SourceLocation AtLoc = ConsumeToken(); return ParseObjCAtExpression(AtLoc); } case tok::caret: Res = ParseBlockLiteralExpression(); break; case tok::code_completion: { cutOffParsing(); Actions.CodeCompletion().CodeCompleteExpression( getCurScope(), PreferredType.get(Tok.getLocation())); return ExprError(); } #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait: #include "clang/Basic/TransformTypeTraits.def" // HACK: libstdc++ uses some of the transform-type-traits as alias // templates, so we need to work around this. if (!NextToken().is(tok::l_paren)) { Tok.setKind(tok::identifier); Diag(Tok, diag::ext_keyword_as_ident) << Tok.getIdentifierInfo()->getName() << 0; goto ParseIdentifier; } goto ExpectedExpression; case tok::l_square: if (getLangOpts().CPlusPlus) { if (getLangOpts().ObjC) { // C++11 lambda expressions and Objective-C message sends both start with a // square bracket. There are three possibilities here: // we have a valid lambda expression, we have an invalid lambda // expression, or we have something that doesn't appear to be a lambda. // If we're in the last case, we fall back to ParseObjCMessageExpression. Res = TryParseLambdaExpression(); if (!Res.isInvalid() && !Res.get()) { // We assume Objective-C++ message expressions are not // primary-expressions. if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseObjCMessageExpression(); } break; } Res = ParseLambdaExpression(); break; } if (getLangOpts().ObjC) { Res = ParseObjCMessageExpression(); break; } [[fallthrough]]; default: ExpectedExpression: NotCastExpr = true; return ExprError(); } // Check to see whether Res is a function designator only. If it is and we // are compiling for OpenCL, we need to return an error as this implies // that the address of the function is being taken, which is illegal in CL. if (ParseKind == CastParseKind::PrimaryExprOnly) // This is strictly a primary-expression - no postfix-expr pieces should be // parsed. return Res; if (!AllowSuffix) { // FIXME: Don't parse a primary-expression suffix if we encountered a parse // error already. if (Res.isInvalid()) return Res; switch (Tok.getKind()) { case tok::l_square: case tok::l_paren: case tok::plusplus: case tok::minusminus: // "expected ';'" or similar is probably the right diagnostic here. Let // the caller decide what to do. if (Tok.isAtStartOfLine()) return Res; [[fallthrough]]; case tok::period: case tok::arrow: break; default: return Res; } // This was a unary-expression for which a postfix-expression suffix is // not permitted by the grammar (eg, a sizeof expression or // new-expression or similar). Diagnose but parse the suffix anyway. Diag(Tok.getLocation(), diag::err_postfix_after_unary_requires_parens) << Tok.getKind() << Res.get()->getSourceRange() << FixItHint::CreateInsertion(Res.get()->getBeginLoc(), "(") << FixItHint::CreateInsertion(PP.getLocForEndOfToken(PrevTokLocation), ")"); } // These can be followed by postfix-expr pieces. PreferredType = SavedType; Res = ParsePostfixExpressionSuffix(Res); if (getLangOpts().OpenCL && !getActions().getOpenCLOptions().isAvailableOption( "__cl_clang_function_pointers", getLangOpts())) if (Expr *PostfixExpr = Res.get()) { QualType Ty = PostfixExpr->getType(); if (!Ty.isNull() && Ty->isFunctionType()) { Diag(PostfixExpr->getExprLoc(), diag::err_opencl_taking_function_address_parser); return ExprError(); } } return Res; } ExprResult Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { // Now that the primary-expression piece of the postfix-expression has been // parsed, see if there are any postfix-expression pieces here. SourceLocation Loc; auto SavedType = PreferredType; while (true) { // Each iteration relies on preferred type for the whole expression. PreferredType = SavedType; switch (Tok.getKind()) { case tok::code_completion: if (InMessageExpression) return LHS; cutOffParsing(); Actions.CodeCompletion().CodeCompletePostfixExpression( getCurScope(), LHS, PreferredType.get(Tok.getLocation())); return ExprError(); case tok::identifier: // If we see identifier: after an expression, and we're not already in a // message send, then this is probably a message send with a missing // opening bracket '['. if (getLangOpts().ObjC && !InMessageExpression && (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) { LHS = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(), nullptr, LHS.get()); break; } // Fall through; this isn't a message send. [[fallthrough]]; default: // Not a postfix-expression suffix. return LHS; case tok::l_square: { // postfix-expression: p-e '[' expression ']' // If we have a array postfix expression that starts on a new line and // Objective-C is enabled, it is highly likely that the user forgot a // semicolon after the base expression and that the array postfix-expr is // actually another message send. In this case, do some look-ahead to see // if the contents of the square brackets are obviously not a valid // expression and recover by pretending there is no suffix. if (getLangOpts().ObjC && Tok.isAtStartOfLine() && isSimpleObjCMessageExpression()) return LHS; // Reject array indices starting with a lambda-expression. '[[' is // reserved for attributes. if (CheckProhibitedCXX11Attribute()) { return ExprError(); } BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); Loc = T.getOpenLocation(); ExprResult Length, Stride; SourceLocation ColonLocFirst, ColonLocSecond; ExprVector ArgExprs; bool HasError = false; PreferredType.enterSubscript(Actions, Tok.getLocation(), LHS.get()); // We try to parse a list of indexes in all language mode first // and, in we find 0 or one index, we try to parse an OpenMP/OpenACC array // section. This allow us to support C++23 multi dimensional subscript and // OpenMP/OpenACC sections in the same language mode. if ((!getLangOpts().OpenMP && !AllowOpenACCArraySections) || Tok.isNot(tok::colon)) { if (!getLangOpts().CPlusPlus23) { ExprResult Idx; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); Idx = ParseBraceInitializer(); } else { Idx = ParseExpression(); // May be a comma expression } if (Idx.isInvalid()) { HasError = true; } else { ArgExprs.push_back(Idx.get()); } } else if (Tok.isNot(tok::r_square)) { if (ParseExpressionList(ArgExprs)) { HasError = true; } } } // Handle OpenACC first, since 'AllowOpenACCArraySections' is only enabled // when actively parsing a 'var' in a 'var-list' during clause/'cache' // parsing, so it is the most specific, and best allows us to handle // OpenACC and OpenMP at the same time. if (ArgExprs.size() <= 1 && AllowOpenACCArraySections) { ColonProtectionRAIIObject RAII(*this); if (Tok.is(tok::colon)) { // Consume ':' ColonLocFirst = ConsumeToken(); if (Tok.isNot(tok::r_square)) Length = ParseExpression(); } } else if (ArgExprs.size() <= 1 && getLangOpts().OpenMP) { ColonProtectionRAIIObject RAII(*this); if (Tok.is(tok::colon)) { // Consume ':' ColonLocFirst = ConsumeToken(); if (Tok.isNot(tok::r_square) && (getLangOpts().OpenMP < 50 || ((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50)))) { Length = ParseExpression(); } } if (getLangOpts().OpenMP >= 50 && (OMPClauseKind == llvm::omp::Clause::OMPC_to || OMPClauseKind == llvm::omp::Clause::OMPC_from) && Tok.is(tok::colon)) { // Consume ':' ColonLocSecond = ConsumeToken(); if (Tok.isNot(tok::r_square)) { Stride = ParseExpression(); } } } SourceLocation RLoc = Tok.getLocation(); if (!LHS.isInvalid() && !HasError && !Length.isInvalid() && !Stride.isInvalid() && Tok.is(tok::r_square)) { if (ColonLocFirst.isValid() || ColonLocSecond.isValid()) { // Like above, AllowOpenACCArraySections is 'more specific' and only // enabled when actively parsing a 'var' in a 'var-list' during // clause/'cache' construct parsing, so it is more specific. So we // should do it first, so that the correct node gets created. if (AllowOpenACCArraySections) { assert(!Stride.isUsable() && !ColonLocSecond.isValid() && "Stride/second colon not allowed for OpenACC"); LHS = Actions.OpenACC().ActOnArraySectionExpr( LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0], ColonLocFirst, Length.get(), RLoc); } else { LHS = Actions.OpenMP().ActOnOMPArraySectionExpr( LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0], ColonLocFirst, ColonLocSecond, Length.get(), Stride.get(), RLoc); } } else { LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc, ArgExprs, RLoc); } } else { LHS = ExprError(); } // Match the ']'. T.consumeClose(); break; } case tok::l_paren: // p-e: p-e '(' argument-expression-list[opt] ')' case tok::lesslessless: { // p-e: p-e '<<<' argument-expression-list '>>>' // '(' argument-expression-list[opt] ')' tok::TokenKind OpKind = Tok.getKind(); InMessageExpressionRAIIObject InMessage(*this, false); Expr *ExecConfig = nullptr; BalancedDelimiterTracker PT(*this, tok::l_paren); if (OpKind == tok::lesslessless) { ExprVector ExecConfigExprs; SourceLocation OpenLoc = ConsumeToken(); if (ParseSimpleExpressionList(ExecConfigExprs)) { LHS = ExprError(); } SourceLocation CloseLoc; if (TryConsumeToken(tok::greatergreatergreater, CloseLoc)) { } else if (LHS.isInvalid()) { SkipUntil(tok::greatergreatergreater, StopAtSemi); } else { // There was an error closing the brackets Diag(Tok, diag::err_expected) << tok::greatergreatergreater; Diag(OpenLoc, diag::note_matching) << tok::lesslessless; SkipUntil(tok::greatergreatergreater, StopAtSemi); LHS = ExprError(); } if (!LHS.isInvalid()) { if (ExpectAndConsume(tok::l_paren)) LHS = ExprError(); else Loc = PrevTokLocation; } if (!LHS.isInvalid()) { ExprResult ECResult = Actions.CUDA().ActOnExecConfigExpr( getCurScope(), OpenLoc, ExecConfigExprs, CloseLoc); if (ECResult.isInvalid()) LHS = ExprError(); else ExecConfig = ECResult.get(); } } else { PT.consumeOpen(); Loc = PT.getOpenLocation(); } ExprVector ArgExprs; auto RunSignatureHelp = [&]() -> QualType { QualType PreferredType = Actions.CodeCompletion().ProduceCallSignatureHelp( LHS.get(), ArgExprs, PT.getOpenLocation()); CalledSignatureHelp = true; return PreferredType; }; bool ExpressionListIsInvalid = false; if (OpKind == tok::l_paren || !LHS.isInvalid()) { if (Tok.isNot(tok::r_paren)) { if ((ExpressionListIsInvalid = ParseExpressionList(ArgExprs, [&] { PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp); }))) { // If we got an error when parsing expression list, we don't call // the CodeCompleteCall handler inside the parser. So call it here // to make sure we get overload suggestions even when we are in the // middle of a parameter. if (PP.isCodeCompletionReached() && !CalledSignatureHelp) RunSignatureHelp(); } } } // Match the ')'. if (LHS.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); } else if (ExpressionListIsInvalid) { Expr *Fn = LHS.get(); ArgExprs.insert(ArgExprs.begin(), Fn); LHS = Actions.CreateRecoveryExpr(Fn->getBeginLoc(), Tok.getLocation(), ArgExprs); SkipUntil(tok::r_paren, StopAtSemi); } else if (Tok.isNot(tok::r_paren)) { bool HadErrors = false; if (LHS.get()->containsErrors()) HadErrors = true; for (auto &E : ArgExprs) if (E->containsErrors()) HadErrors = true; // If there were errors in the LHS or ArgExprs, call SkipUntil instead // of PT.consumeClose() to avoid emitting extra diagnostics for the // unmatched l_paren. if (HadErrors) SkipUntil(tok::r_paren, StopAtSemi); else PT.consumeClose(); LHS = ExprError(); } else { Expr *Fn = LHS.get(); SourceLocation RParLoc = Tok.getLocation(); LHS = Actions.ActOnCallExpr(getCurScope(), Fn, Loc, ArgExprs, RParLoc, ExecConfig); if (LHS.isInvalid()) { ArgExprs.insert(ArgExprs.begin(), Fn); LHS = Actions.CreateRecoveryExpr(Fn->getBeginLoc(), RParLoc, ArgExprs); } PT.consumeClose(); } break; } case tok::arrow: case tok::period: { // postfix-expression: p-e '->' template[opt] id-expression // postfix-expression: p-e '.' template[opt] id-expression tok::TokenKind OpKind = Tok.getKind(); SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token. CXXScopeSpec SS; ParsedType ObjectType; bool MayBePseudoDestructor = false; Expr* OrigLHS = !LHS.isInvalid() ? LHS.get() : nullptr; PreferredType.enterMemAccess(Actions, Tok.getLocation(), OrigLHS); if (getLangOpts().CPlusPlus && !LHS.isInvalid()) { Expr *Base = OrigLHS; const Type* BaseType = Base->getType().getTypePtrOrNull(); if (BaseType && Tok.is(tok::l_paren) && (BaseType->isFunctionType() || BaseType->isSpecificPlaceholderType(BuiltinType::BoundMember))) { Diag(OpLoc, diag::err_function_is_not_record) << OpKind << Base->getSourceRange() << FixItHint::CreateRemoval(OpLoc); return ParsePostfixExpressionSuffix(Base); } LHS = Actions.ActOnStartCXXMemberReference(getCurScope(), Base, OpLoc, OpKind, ObjectType, MayBePseudoDestructor); if (LHS.isInvalid()) { // Clang will try to perform expression based completion as a // fallback, which is confusing in case of member references. So we // stop here without any completions. if (Tok.is(tok::code_completion)) { cutOffParsing(); return ExprError(); } break; } ParseOptionalCXXScopeSpecifier( SS, ObjectType, LHS.get() && LHS.get()->containsErrors(), /*EnteringContext=*/false, &MayBePseudoDestructor); if (SS.isNotEmpty()) ObjectType = nullptr; } if (Tok.is(tok::code_completion)) { tok::TokenKind CorrectedOpKind = OpKind == tok::arrow ? tok::period : tok::arrow; ExprResult CorrectedLHS(/*Invalid=*/true); if (getLangOpts().CPlusPlus && OrigLHS) { // FIXME: Creating a TentativeAnalysisScope from outside Sema is a // hack. Sema::TentativeAnalysisScope Trap(Actions); CorrectedLHS = Actions.ActOnStartCXXMemberReference( getCurScope(), OrigLHS, OpLoc, CorrectedOpKind, ObjectType, MayBePseudoDestructor); } Expr *Base = LHS.get(); Expr *CorrectedBase = CorrectedLHS.get(); if (!CorrectedBase && !getLangOpts().CPlusPlus) CorrectedBase = Base; // Code completion for a member access expression. cutOffParsing(); Actions.CodeCompletion().CodeCompleteMemberReferenceExpr( getCurScope(), Base, CorrectedBase, OpLoc, OpKind == tok::arrow, Base && ExprStatementTokLoc == Base->getBeginLoc(), PreferredType.get(Tok.getLocation())); return ExprError(); } if (MayBePseudoDestructor && !LHS.isInvalid()) { LHS = ParseCXXPseudoDestructor(LHS.get(), OpLoc, OpKind, SS, ObjectType); break; } // Either the action has told us that this cannot be a // pseudo-destructor expression (based on the type of base // expression), or we didn't see a '~' in the right place. We // can still parse a destructor name here, but in that case it // names a real destructor. // Allow explicit constructor calls in Microsoft mode. // FIXME: Add support for explicit call of template constructor. SourceLocation TemplateKWLoc; UnqualifiedId Name; if (getLangOpts().ObjC && OpKind == tok::period && Tok.is(tok::kw_class)) { // Objective-C++: // After a '.' in a member access expression, treat the keyword // 'class' as if it were an identifier. // // This hack allows property access to the 'class' method because it is // such a common method name. For other C++ keywords that are // Objective-C method names, one must use the message send syntax. IdentifierInfo *Id = Tok.getIdentifierInfo(); SourceLocation Loc = ConsumeToken(); Name.setIdentifier(Id, Loc); } else if (ParseUnqualifiedId( SS, ObjectType, LHS.get() && LHS.get()->containsErrors(), /*EnteringContext=*/false, /*AllowDestructorName=*/true, /*AllowConstructorName=*/ getLangOpts().MicrosoftExt && SS.isNotEmpty(), /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name)) { LHS = ExprError(); } if (!LHS.isInvalid()) LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc, OpKind, SS, TemplateKWLoc, Name, CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : nullptr); if (!LHS.isInvalid()) { if (Tok.is(tok::less)) checkPotentialAngleBracket(LHS); } else if (OrigLHS && Name.isValid()) { // Preserve the LHS if the RHS is an invalid member. LHS = Actions.CreateRecoveryExpr(OrigLHS->getBeginLoc(), Name.getEndLoc(), {OrigLHS}); } break; } case tok::plusplus: // postfix-expression: postfix-expression '++' case tok::minusminus: // postfix-expression: postfix-expression '--' if (!LHS.isInvalid()) { Expr *Arg = LHS.get(); LHS = Actions.ActOnPostfixUnaryOp(getCurScope(), Tok.getLocation(), Tok.getKind(), Arg); if (LHS.isInvalid()) LHS = Actions.CreateRecoveryExpr(Arg->getBeginLoc(), Tok.getLocation(), Arg); } ConsumeToken(); break; } } } ExprResult Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, bool &isCastExpr, ParsedType &CastTy, SourceRange &CastRange) { assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements, tok::kw__Countof) && "Not a typeof/sizeof/alignof/vec_step expression!"); ExprResult Operand; // If the operand doesn't start with an '(', it must be an expression. if (Tok.isNot(tok::l_paren)) { // If construct allows a form without parenthesis, user may forget to put // pathenthesis around type name. if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof)) { if (isTypeIdUnambiguously()) { DeclSpec DS(AttrFactory); ParseSpecifierQualifierList(DS); Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); ParseDeclarator(DeclaratorInfo); SourceLocation LParenLoc = PP.getLocForEndOfToken(OpTok.getLocation()); SourceLocation RParenLoc = PP.getLocForEndOfToken(PrevTokLocation); if (LParenLoc.isInvalid() || RParenLoc.isInvalid()) { Diag(OpTok.getLocation(), diag::err_expected_parentheses_around_typename) << OpTok.getName(); } else { Diag(LParenLoc, diag::err_expected_parentheses_around_typename) << OpTok.getName() << FixItHint::CreateInsertion(LParenLoc, "(") << FixItHint::CreateInsertion(RParenLoc, ")"); } isCastExpr = true; return ExprEmpty(); } } isCastExpr = false; if (OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) && !getLangOpts().CPlusPlus) { Diag(Tok, diag::err_expected_after) << OpTok.getIdentifierInfo() << tok::l_paren; return ExprError(); } // If we're parsing a chain that consists of keywords that could be // followed by a non-parenthesized expression, BalancedDelimiterTracker // is not going to help when the nesting is too deep. In this corner case // we continue to parse with sufficient stack space to avoid crashing. if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw__Countof) && Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw__Countof)) Actions.runWithSufficientStackSpace(Tok.getLocation(), [&] { Operand = ParseCastExpression(CastParseKind::UnaryExprOnly); }); else Operand = ParseCastExpression(CastParseKind::UnaryExprOnly); } else { // If it starts with a '(', we know that it is either a parenthesized // type-name, or it is a unary-expression that starts with a compound // literal, or starts with a primary-expression that is a parenthesized // expression. Most unary operators have an expression form without parens // as part of the grammar for the operator, and a type form with the parens // as part of the grammar for the operator. However, typeof and // typeof_unqual require parens for both forms. This means that we *know* // that the open and close parens cannot be part of a cast expression, // which means we definitely are not parsing a compound literal expression. // This disambiguates a case like enum E : typeof(int) { }; where we've // parsed typeof and need to handle the (int){} tokens properly despite // them looking like a compound literal, as in sizeof (int){}; where the // parens could be part of a parenthesized type name or for a cast // expression of some kind. bool ParenKnownToBeNonCast = OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual); ParenParseOption ExprType = ParenParseOption::CastExpr; SourceLocation LParenLoc = Tok.getLocation(), RParenLoc; Operand = ParseParenExpression( ExprType, /*StopIfCastExr=*/true, ParenKnownToBeNonCast ? ParenExprKind::PartOfOperator : ParenExprKind::Unknown, TypoCorrectionTypeBehavior::AllowBoth, CastTy, RParenLoc); CastRange = SourceRange(LParenLoc, RParenLoc); // If ParseParenExpression parsed a '(typename)' sequence only, then this is // a type. if (ExprType == ParenParseOption::CastExpr) { isCastExpr = true; return ExprEmpty(); } if (getLangOpts().CPlusPlus || !OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual)) { // GNU typeof in C requires the expression to be parenthesized. Not so for // sizeof/alignof or in C++. Therefore, the parenthesized expression is // the start of a unary-expression, but doesn't include any postfix // pieces. Parse these now if present. if (!Operand.isInvalid()) Operand = ParsePostfixExpressionSuffix(Operand.get()); } } // If we get here, the operand to the typeof/sizeof/alignof was an expression. isCastExpr = false; return Operand; } ExprResult Parser::ParseSYCLUniqueStableNameExpression() { assert(Tok.is(tok::kw___builtin_sycl_unique_stable_name) && "Not __builtin_sycl_unique_stable_name"); SourceLocation OpLoc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); // __builtin_sycl_unique_stable_name expressions are always parenthesized. if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_sycl_unique_stable_name")) return ExprError(); TypeResult Ty = ParseTypeName(); if (Ty.isInvalid()) { T.skipToEnd(); return ExprError(); } if (T.consumeClose()) return ExprError(); return Actions.SYCL().ActOnUniqueStableNameExpr( OpLoc, T.getOpenLocation(), T.getCloseLocation(), Ty.get()); } ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements, tok::kw__Countof) && "Not a sizeof/alignof/vec_step expression!"); Token OpTok = Tok; ConsumeToken(); // [C++11] 'sizeof' '...' '(' identifier ')' if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof)) { SourceLocation EllipsisLoc = ConsumeToken(); SourceLocation LParenLoc, RParenLoc; IdentifierInfo *Name = nullptr; SourceLocation NameLoc; if (Tok.is(tok::l_paren)) { BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); LParenLoc = T.getOpenLocation(); if (Tok.is(tok::identifier)) { Name = Tok.getIdentifierInfo(); NameLoc = ConsumeToken(); T.consumeClose(); RParenLoc = T.getCloseLocation(); if (RParenLoc.isInvalid()) RParenLoc = PP.getLocForEndOfToken(NameLoc); } else { Diag(Tok, diag::err_expected_parameter_pack); SkipUntil(tok::r_paren, StopAtSemi); } } else if (Tok.is(tok::identifier)) { Name = Tok.getIdentifierInfo(); NameLoc = ConsumeToken(); LParenLoc = PP.getLocForEndOfToken(EllipsisLoc); RParenLoc = PP.getLocForEndOfToken(NameLoc); Diag(LParenLoc, diag::err_paren_sizeof_parameter_pack) << Name << FixItHint::CreateInsertion(LParenLoc, "(") << FixItHint::CreateInsertion(RParenLoc, ")"); } else { Diag(Tok, diag::err_sizeof_parameter_pack); } if (!Name) return ExprError(); EnterExpressionEvaluationContext Unevaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated, Sema::ReuseLambdaContextDecl); return Actions.ActOnSizeofParameterPackExpr(getCurScope(), OpTok.getLocation(), *Name, NameLoc, RParenLoc); } if (getLangOpts().CPlusPlus && OpTok.isOneOf(tok::kw_alignof, tok::kw__Alignof)) Diag(OpTok, diag::warn_cxx98_compat_alignof); else if (getLangOpts().C23 && OpTok.is(tok::kw_alignof)) Diag(OpTok, diag::warn_c23_compat_keyword) << OpTok.getName(); else if (getLangOpts().C2y && OpTok.is(tok::kw__Countof)) Diag(OpTok, diag::warn_c2y_compat_keyword) << OpTok.getName(); EnterExpressionEvaluationContext Unevaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated, Sema::ReuseLambdaContextDecl); bool isCastExpr; ParsedType CastTy; SourceRange CastRange; ExprResult Operand = ParseExprAfterUnaryExprOrTypeTrait(OpTok, isCastExpr, CastTy, CastRange); UnaryExprOrTypeTrait ExprKind = UETT_SizeOf; switch (OpTok.getKind()) { case tok::kw_alignof: case tok::kw__Alignof: ExprKind = UETT_AlignOf; break; case tok::kw___alignof: ExprKind = UETT_PreferredAlignOf; break; case tok::kw_vec_step: ExprKind = UETT_VecStep; break; case tok::kw___builtin_omp_required_simd_align: ExprKind = UETT_OpenMPRequiredSimdAlign; break; case tok::kw___datasizeof: ExprKind = UETT_DataSizeOf; break; case tok::kw___builtin_vectorelements: ExprKind = UETT_VectorElements; break; case tok::kw__Countof: ExprKind = UETT_CountOf; assert(!getLangOpts().CPlusPlus && "_Countof in C++ mode?"); if (!getLangOpts().C2y) Diag(OpTok, diag::ext_c2y_feature) << OpTok.getName(); break; default: break; } if (isCastExpr) return Actions.ActOnUnaryExprOrTypeTraitExpr(OpTok.getLocation(), ExprKind, /*IsType=*/true, CastTy.getAsOpaquePtr(), CastRange); if (OpTok.isOneOf(tok::kw_alignof, tok::kw__Alignof)) Diag(OpTok, diag::ext_alignof_expr) << OpTok.getIdentifierInfo(); // If we get here, the operand to the sizeof/alignof was an expression. if (!Operand.isInvalid()) Operand = Actions.ActOnUnaryExprOrTypeTraitExpr(OpTok.getLocation(), ExprKind, /*IsType=*/false, Operand.get(), CastRange); return Operand; } ExprResult Parser::ParseBuiltinPrimaryExpression() { ExprResult Res; const IdentifierInfo *BuiltinII = Tok.getIdentifierInfo(); tok::TokenKind T = Tok.getKind(); SourceLocation StartLoc = ConsumeToken(); // Eat the builtin identifier. // All of these start with an open paren. if (Tok.isNot(tok::l_paren)) return ExprError(Diag(Tok, diag::err_expected_after) << BuiltinII << tok::l_paren); BalancedDelimiterTracker PT(*this, tok::l_paren); PT.consumeOpen(); // TODO: Build AST. switch (T) { default: llvm_unreachable("Not a builtin primary expression!"); case tok::kw___builtin_va_arg: { ExprResult Expr(ParseAssignmentExpression()); if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); Expr = ExprError(); } TypeResult Ty = ParseTypeName(); if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; Expr = ExprError(); } if (Expr.isInvalid() || Ty.isInvalid()) Res = ExprError(); else Res = Actions.ActOnVAArg(StartLoc, Expr.get(), Ty.get(), ConsumeParen()); break; } case tok::kw___builtin_offsetof: { SourceLocation TypeLoc = Tok.getLocation(); auto OOK = OffsetOfKind::Builtin; if (Tok.getLocation().isMacroID()) { StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( Tok.getLocation(), PP.getSourceManager(), getLangOpts()); if (MacroName == "offsetof") OOK = OffsetOfKind::Macro; } TypeResult Ty; { OffsetOfStateRAIIObject InOffsetof(*this, OOK); Ty = ParseTypeName(); if (Ty.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } } if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } // We must have at least one identifier here. if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected) << tok::identifier; SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } // Keep track of the various subcomponents we see. SmallVector Comps; Comps.push_back(Sema::OffsetOfComponent()); Comps.back().isBrackets = false; Comps.back().U.IdentInfo = Tok.getIdentifierInfo(); Comps.back().LocStart = Comps.back().LocEnd = ConsumeToken(); // FIXME: This loop leaks the index expressions on error. while (true) { if (Tok.is(tok::period)) { // offsetof-member-designator: offsetof-member-designator '.' identifier Comps.push_back(Sema::OffsetOfComponent()); Comps.back().isBrackets = false; Comps.back().LocStart = ConsumeToken(); if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected) << tok::identifier; SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } Comps.back().U.IdentInfo = Tok.getIdentifierInfo(); Comps.back().LocEnd = ConsumeToken(); } else if (Tok.is(tok::l_square)) { if (CheckProhibitedCXX11Attribute()) return ExprError(); // offsetof-member-designator: offsetof-member-design '[' expression ']' Comps.push_back(Sema::OffsetOfComponent()); Comps.back().isBrackets = true; BalancedDelimiterTracker ST(*this, tok::l_square); ST.consumeOpen(); Comps.back().LocStart = ST.getOpenLocation(); Res = ParseExpression(); if (Res.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return Res; } Comps.back().U.E = Res.get(); ST.consumeClose(); Comps.back().LocEnd = ST.getCloseLocation(); } else { if (Tok.isNot(tok::r_paren)) { PT.consumeClose(); Res = ExprError(); } else if (Ty.isInvalid()) { Res = ExprError(); } else { PT.consumeClose(); Res = Actions.ActOnBuiltinOffsetOf(getCurScope(), StartLoc, TypeLoc, Ty.get(), Comps, PT.getCloseLocation()); } break; } } break; } case tok::kw___builtin_choose_expr: { ExprResult Cond(ParseAssignmentExpression()); if (Cond.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return Cond; } if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } ExprResult Expr1(ParseAssignmentExpression()); if (Expr1.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return Expr1; } if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } ExprResult Expr2(ParseAssignmentExpression()); if (Expr2.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return Expr2; } if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; return ExprError(); } Res = Actions.ActOnChooseExpr(StartLoc, Cond.get(), Expr1.get(), Expr2.get(), ConsumeParen()); break; } case tok::kw___builtin_astype: { // The first argument is an expression to be converted, followed by a comma. ExprResult Expr(ParseAssignmentExpression()); if (Expr.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } // Second argument is the type to bitcast to. TypeResult DestTy = ParseTypeName(); if (DestTy.isInvalid()) return ExprError(); // Attempt to consume the r-paren. if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } Res = Actions.ActOnAsTypeExpr(Expr.get(), DestTy.get(), StartLoc, ConsumeParen()); break; } case tok::kw___builtin_convertvector: { // The first argument is an expression to be converted, followed by a comma. ExprResult Expr(ParseAssignmentExpression()); if (Expr.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } // Second argument is the type to bitcast to. TypeResult DestTy = ParseTypeName(); if (DestTy.isInvalid()) return ExprError(); // Attempt to consume the r-paren. if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } Res = Actions.ActOnConvertVectorExpr(Expr.get(), DestTy.get(), StartLoc, ConsumeParen()); break; } case tok::kw___builtin_COLUMN: case tok::kw___builtin_FILE: case tok::kw___builtin_FILE_NAME: case tok::kw___builtin_FUNCTION: case tok::kw___builtin_FUNCSIG: case tok::kw___builtin_LINE: case tok::kw___builtin_source_location: { // Attempt to consume the r-paren. if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } SourceLocIdentKind Kind = [&] { switch (T) { case tok::kw___builtin_FILE: return SourceLocIdentKind::File; case tok::kw___builtin_FILE_NAME: return SourceLocIdentKind::FileName; case tok::kw___builtin_FUNCTION: return SourceLocIdentKind::Function; case tok::kw___builtin_FUNCSIG: return SourceLocIdentKind::FuncSig; case tok::kw___builtin_LINE: return SourceLocIdentKind::Line; case tok::kw___builtin_COLUMN: return SourceLocIdentKind::Column; case tok::kw___builtin_source_location: return SourceLocIdentKind::SourceLocStruct; default: llvm_unreachable("invalid keyword"); } }(); Res = Actions.ActOnSourceLocExpr(Kind, StartLoc, ConsumeParen()); break; } } if (Res.isInvalid()) return ExprError(); // These can be followed by postfix-expr pieces because they are // primary-expressions. return ParsePostfixExpressionSuffix(Res.get()); } bool Parser::tryParseOpenMPArrayShapingCastPart() { assert(Tok.is(tok::l_square) && "Expected open bracket"); bool ErrorFound = true; TentativeParsingAction TPA(*this); do { if (Tok.isNot(tok::l_square)) break; // Consume '[' ConsumeBracket(); // Skip inner expression. while (!SkipUntil(tok::r_square, tok::annot_pragma_openmp_end, StopAtSemi | StopBeforeMatch)) ; if (Tok.isNot(tok::r_square)) break; // Consume ']' ConsumeBracket(); // Found ')' - done. if (Tok.is(tok::r_paren)) { ErrorFound = false; break; } } while (Tok.isNot(tok::annot_pragma_openmp_end)); TPA.Revert(); return !ErrorFound; } ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr, ParenExprKind ParenBehavior, TypoCorrectionTypeBehavior CorrectionBehavior, ParsedType &CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); ColonProtectionRAIIObject ColonProtection(*this, false); BalancedDelimiterTracker T(*this, tok::l_paren); if (T.consumeOpen()) return ExprError(); SourceLocation OpenLoc = T.getOpenLocation(); PreferredType.enterParenExpr(Tok.getLocation(), OpenLoc); ExprResult Result(true); bool isAmbiguousTypeId; CastTy = nullptr; if (Tok.is(tok::code_completion)) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteExpression( getCurScope(), PreferredType.get(Tok.getLocation()), /*IsParenthesized=*/ExprType >= ParenParseOption::CompoundLiteral); return ExprError(); } // Diagnose use of bridge casts in non-arc mode. bool BridgeCast = (getLangOpts().ObjC && Tok.isOneOf(tok::kw___bridge, tok::kw___bridge_transfer, tok::kw___bridge_retained, tok::kw___bridge_retain)); if (BridgeCast && !getLangOpts().ObjCAutoRefCount) { if (!TryConsumeToken(tok::kw___bridge)) { StringRef BridgeCastName = Tok.getName(); SourceLocation BridgeKeywordLoc = ConsumeToken(); if (!PP.getSourceManager().isInSystemHeader(BridgeKeywordLoc)) Diag(BridgeKeywordLoc, diag::warn_arc_bridge_cast_nonarc) << BridgeCastName << FixItHint::CreateReplacement(BridgeKeywordLoc, ""); } BridgeCast = false; } // None of these cases should fall through with an invalid Result // unless they've already reported an error. if (ExprType >= ParenParseOption::CompoundStmt && Tok.is(tok::l_brace)) { Diag(Tok, OpenLoc.isMacroID() ? diag::ext_gnu_statement_expr_macro : diag::ext_gnu_statement_expr); checkCompoundToken(OpenLoc, tok::l_paren, CompoundToken::StmtExprBegin); if (!getCurScope()->getFnParent() && !getCurScope()->getBlockParent()) { Result = ExprError(Diag(OpenLoc, diag::err_stmtexpr_file_scope)); } else { // Find the nearest non-record decl context. Variables declared in a // statement expression behave as if they were declared in the enclosing // function, block, or other code construct. DeclContext *CodeDC = Actions.CurContext; while (CodeDC->isRecord() || isa(CodeDC)) { CodeDC = CodeDC->getParent(); assert(CodeDC && !CodeDC->isFileContext() && "statement expr not in code context"); } Sema::ContextRAII SavedContext(Actions, CodeDC, /*NewThisContext=*/false); Actions.ActOnStartStmtExpr(); StmtResult Stmt(ParseCompoundStatement(true)); ExprType = ParenParseOption::CompoundStmt; // If the substmt parsed correctly, build the AST node. if (!Stmt.isInvalid()) { Result = Actions.ActOnStmtExpr(getCurScope(), OpenLoc, Stmt.get(), Tok.getLocation()); } else { Actions.ActOnStmtExprError(); } } } else if (ExprType >= ParenParseOption::CompoundLiteral && BridgeCast) { tok::TokenKind tokenKind = Tok.getKind(); SourceLocation BridgeKeywordLoc = ConsumeToken(); // Parse an Objective-C ARC ownership cast expression. ObjCBridgeCastKind Kind; if (tokenKind == tok::kw___bridge) Kind = OBC_Bridge; else if (tokenKind == tok::kw___bridge_transfer) Kind = OBC_BridgeTransfer; else if (tokenKind == tok::kw___bridge_retained) Kind = OBC_BridgeRetained; else { // As a hopefully temporary workaround, allow __bridge_retain as // a synonym for __bridge_retained, but only in system headers. assert(tokenKind == tok::kw___bridge_retain); Kind = OBC_BridgeRetained; if (!PP.getSourceManager().isInSystemHeader(BridgeKeywordLoc)) Diag(BridgeKeywordLoc, diag::err_arc_bridge_retain) << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge_retained"); } TypeResult Ty = ParseTypeName(); T.consumeClose(); ColonProtection.restore(); RParenLoc = T.getCloseLocation(); PreferredType.enterTypeCast(Tok.getLocation(), Ty.get().get()); ExprResult SubExpr = ParseCastExpression(CastParseKind::AnyCastExpr); if (Ty.isInvalid() || SubExpr.isInvalid()) return ExprError(); return Actions.ObjC().ActOnObjCBridgedCast(getCurScope(), OpenLoc, Kind, BridgeKeywordLoc, Ty.get(), RParenLoc, SubExpr.get()); } else if (ExprType >= ParenParseOption::CompoundLiteral && isTypeIdInParens(isAmbiguousTypeId)) { // Otherwise, this is a compound literal expression or cast expression. // In C++, if the type-id is ambiguous we disambiguate based on context. // If stopIfCastExpr is true the context is a typeof/sizeof/alignof // in which case we should treat it as type-id. // if stopIfCastExpr is false, we need to determine the context past the // parens, so we defer to ParseCXXAmbiguousParenExpression for that. if (isAmbiguousTypeId && !StopIfCastExpr) { ExprResult res = ParseCXXAmbiguousParenExpression(ExprType, CastTy, T, ColonProtection); RParenLoc = T.getCloseLocation(); return res; } // Parse the type declarator. DeclSpec DS(AttrFactory); ParseSpecifierQualifierList(DS); Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); ParseDeclarator(DeclaratorInfo); // If our type is followed by an identifier and either ':' or ']', then // this is probably an Objective-C message send where the leading '[' is // missing. Recover as if that were the case. if (!DeclaratorInfo.isInvalidType() && Tok.is(tok::identifier) && !InMessageExpression && getLangOpts().ObjC && (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) { TypeResult Ty; { InMessageExpressionRAIIObject InMessage(*this, false); Ty = Actions.ActOnTypeName(DeclaratorInfo); } Result = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(), Ty.get(), nullptr); } else { // Match the ')'. T.consumeClose(); ColonProtection.restore(); RParenLoc = T.getCloseLocation(); if (ParenBehavior == ParenExprKind::Unknown && Tok.is(tok::l_brace)) { ExprType = ParenParseOption::CompoundLiteral; TypeResult Ty; { InMessageExpressionRAIIObject InMessage(*this, false); Ty = Actions.ActOnTypeName(DeclaratorInfo); } return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc); } if (ParenBehavior == ParenExprKind::Unknown && Tok.is(tok::l_paren)) { // This could be OpenCL vector Literals if (getLangOpts().OpenCL) { TypeResult Ty; { InMessageExpressionRAIIObject InMessage(*this, false); Ty = Actions.ActOnTypeName(DeclaratorInfo); } if(Ty.isInvalid()) { return ExprError(); } QualType QT = Ty.get().get().getCanonicalType(); if (QT->isVectorType()) { // We parsed '(' vector-type-name ')' followed by '(' // Parse the cast-expression that follows it next. // isVectorLiteral = true will make sure we don't parse any // Postfix expression yet Result = ParseCastExpression( /*isUnaryExpression=*/CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/false, TypoCorrectionTypeBehavior::AllowTypes, /*isVectorLiteral=*/true); if (!Result.isInvalid()) { Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc, DeclaratorInfo, CastTy, RParenLoc, Result.get()); } // After we performed the cast we can check for postfix-expr pieces. if (!Result.isInvalid()) { Result = ParsePostfixExpressionSuffix(Result); } return Result; } } } if (ExprType == ParenParseOption::CastExpr) { // We parsed '(' type-name ')' and the thing after it wasn't a '{'. if (DeclaratorInfo.isInvalidType()) return ExprError(); // Note that this doesn't parse the subsequent cast-expression, it just // returns the parsed type to the callee. if (StopIfCastExpr) { TypeResult Ty; { InMessageExpressionRAIIObject InMessage(*this, false); Ty = Actions.ActOnTypeName(DeclaratorInfo); } CastTy = Ty.get(); return ExprResult(); } // Reject the cast of super idiom in ObjC. if (Tok.is(tok::identifier) && getLangOpts().ObjC && Tok.getIdentifierInfo() == Ident_super && getCurScope()->isInObjcMethodScope() && GetLookAheadToken(1).isNot(tok::period)) { Diag(Tok.getLocation(), diag::err_illegal_super_cast) << SourceRange(OpenLoc, RParenLoc); return ExprError(); } PreferredType.enterTypeCast(Tok.getLocation(), CastTy.get()); // Parse the cast-expression that follows it next. // TODO: For cast expression with CastTy. Result = ParseCastExpression( /*isUnaryExpression=*/CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/false, TypoCorrectionTypeBehavior::AllowTypes); if (!Result.isInvalid()) { Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc, DeclaratorInfo, CastTy, RParenLoc, Result.get()); } return Result; } Diag(Tok, diag::err_expected_lbrace_in_compound_literal); return ExprError(); } } else if (ExprType >= ParenParseOption::FoldExpr && Tok.is(tok::ellipsis) && isFoldOperator(NextToken().getKind())) { ExprType = ParenParseOption::FoldExpr; return ParseFoldExpression(ExprResult(), T); } else if (CorrectionBehavior == TypoCorrectionTypeBehavior::AllowTypes) { // FIXME: This should not be predicated on typo correction behavior. // Parse the expression-list. InMessageExpressionRAIIObject InMessage(*this, false); ExprVector ArgExprs; if (!ParseSimpleExpressionList(ArgExprs)) { // FIXME: If we ever support comma expressions as operands to // fold-expressions, we'll need to allow multiple ArgExprs here. if (ExprType >= ParenParseOption::FoldExpr && ArgExprs.size() == 1 && isFoldOperator(Tok.getKind()) && NextToken().is(tok::ellipsis)) { ExprType = ParenParseOption::FoldExpr; return ParseFoldExpression(ArgExprs[0], T); } ExprType = ParenParseOption::SimpleExpr; Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(), ArgExprs); } } else if (getLangOpts().OpenMP >= 50 && OpenMPDirectiveParsing && ExprType == ParenParseOption::CastExpr && Tok.is(tok::l_square) && tryParseOpenMPArrayShapingCastPart()) { bool ErrorFound = false; SmallVector OMPDimensions; SmallVector OMPBracketsRanges; do { BalancedDelimiterTracker TS(*this, tok::l_square); TS.consumeOpen(); ExprResult NumElements = ParseExpression(); if (!NumElements.isUsable()) { ErrorFound = true; while (!SkipUntil(tok::r_square, tok::r_paren, StopAtSemi | StopBeforeMatch)) ; } TS.consumeClose(); OMPDimensions.push_back(NumElements.get()); OMPBracketsRanges.push_back(TS.getRange()); } while (Tok.isNot(tok::r_paren)); // Match the ')'. T.consumeClose(); RParenLoc = T.getCloseLocation(); Result = ParseAssignmentExpression(); if (ErrorFound) { Result = ExprError(); } else if (!Result.isInvalid()) { Result = Actions.OpenMP().ActOnOMPArrayShapingExpr( Result.get(), OpenLoc, RParenLoc, OMPDimensions, OMPBracketsRanges); } return Result; } else { InMessageExpressionRAIIObject InMessage(*this, false); Result = ParseExpression(TypoCorrectionTypeBehavior::AllowBoth); if (ExprType >= ParenParseOption::FoldExpr && isFoldOperator(Tok.getKind()) && NextToken().is(tok::ellipsis)) { ExprType = ParenParseOption::FoldExpr; return ParseFoldExpression(Result, T); } ExprType = ParenParseOption::SimpleExpr; // Don't build a paren expression unless we actually match a ')'. if (!Result.isInvalid() && Tok.is(tok::r_paren)) Result = Actions.ActOnParenExpr(OpenLoc, Tok.getLocation(), Result.get()); } // Match the ')'. if (Result.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } T.consumeClose(); RParenLoc = T.getCloseLocation(); return Result; } ExprResult Parser::ParseCompoundLiteralExpression(ParsedType Ty, SourceLocation LParenLoc, SourceLocation RParenLoc) { assert(Tok.is(tok::l_brace) && "Not a compound literal!"); if (!getLangOpts().C99) // Compound literals don't exist in C90. Diag(LParenLoc, diag::ext_c99_compound_literal); PreferredType.enterTypeCast(Tok.getLocation(), Ty.get()); ExprResult Result = ParseInitializer(); if (!Result.isInvalid() && Ty) return Actions.ActOnCompoundLiteral(LParenLoc, Ty, RParenLoc, Result.get()); return Result; } ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) { return ParseStringLiteralExpression(AllowUserDefinedLiteral, /*Unevaluated=*/false); } ExprResult Parser::ParseUnevaluatedStringLiteralExpression() { return ParseStringLiteralExpression(/*AllowUserDefinedLiteral=*/false, /*Unevaluated=*/true); } ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral, bool Unevaluated) { assert(tokenIsLikeStringLiteral(Tok, getLangOpts()) && "Not a string-literal-like token!"); // String concatenation. // Note: some keywords like __FUNCTION__ are not considered to be strings // for concatenation purposes, unless Microsoft extensions are enabled. SmallVector StringToks; do { StringToks.push_back(Tok); ConsumeAnyToken(); } while (tokenIsLikeStringLiteral(Tok, getLangOpts())); if (Unevaluated) { assert(!AllowUserDefinedLiteral && "UDL are always evaluated"); return Actions.ActOnUnevaluatedStringLiteral(StringToks); } // Pass the set of string tokens, ready for concatenation, to the actions. return Actions.ActOnStringLiteral(StringToks, AllowUserDefinedLiteral ? getCurScope() : nullptr); } ExprResult Parser::ParseGenericSelectionExpression() { assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected"); diagnoseUseOfC11Keyword(Tok); SourceLocation KeyLoc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); if (T.expectAndConsume()) return ExprError(); // We either have a controlling expression or we have a controlling type, and // we need to figure out which it is. TypeResult ControllingType; ExprResult ControllingExpr; if (isTypeIdForGenericSelection()) { ControllingType = ParseTypeName(); if (ControllingType.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } const auto *LIT = cast(ControllingType.get().get()); SourceLocation Loc = LIT->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); Diag(Loc, getLangOpts().C2y ? diag::warn_c2y_compat_generic_with_type_arg : diag::ext_c2y_generic_with_type_arg); } else { // C11 6.5.1.1p3 "The controlling expression of a generic selection is // not evaluated." EnterExpressionEvaluationContext Unevaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated); ControllingExpr = ParseAssignmentExpression(); if (ControllingExpr.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } } if (ExpectAndConsume(tok::comma)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } SourceLocation DefaultLoc; SmallVector Types; ExprVector Exprs; do { ParsedType Ty; if (Tok.is(tok::kw_default)) { // C11 6.5.1.1p2 "A generic selection shall have no more than one default // generic association." if (!DefaultLoc.isInvalid()) { Diag(Tok, diag::err_duplicate_default_assoc); Diag(DefaultLoc, diag::note_previous_default_assoc); SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } DefaultLoc = ConsumeToken(); Ty = nullptr; } else { ColonProtectionRAIIObject X(*this); TypeResult TR = ParseTypeName(nullptr, DeclaratorContext::Association); if (TR.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } Ty = TR.get(); } Types.push_back(Ty); if (ExpectAndConsume(tok::colon)) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } // FIXME: These expressions should be parsed in a potentially potentially // evaluated context. ExprResult ER = ParseAssignmentExpression(); if (ER.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } Exprs.push_back(ER.get()); } while (TryConsumeToken(tok::comma)); T.consumeClose(); if (T.getCloseLocation().isInvalid()) return ExprError(); void *ExprOrTy = ControllingExpr.isUsable() ? ControllingExpr.get() : ControllingType.get().getAsOpaquePtr(); return Actions.ActOnGenericSelectionExpr( KeyLoc, DefaultLoc, T.getCloseLocation(), ControllingExpr.isUsable(), ExprOrTy, Types, Exprs); } ExprResult Parser::ParseFoldExpression(ExprResult LHS, BalancedDelimiterTracker &T) { if (LHS.isInvalid()) { T.skipToEnd(); return true; } tok::TokenKind Kind = tok::unknown; SourceLocation FirstOpLoc; if (LHS.isUsable()) { Kind = Tok.getKind(); assert(isFoldOperator(Kind) && "missing fold-operator"); FirstOpLoc = ConsumeToken(); } assert(Tok.is(tok::ellipsis) && "not a fold-expression"); SourceLocation EllipsisLoc = ConsumeToken(); ExprResult RHS; if (Tok.isNot(tok::r_paren)) { if (!isFoldOperator(Tok.getKind())) return Diag(Tok.getLocation(), diag::err_expected_fold_operator); if (Kind != tok::unknown && Tok.getKind() != Kind) Diag(Tok.getLocation(), diag::err_fold_operator_mismatch) << SourceRange(FirstOpLoc); Kind = Tok.getKind(); ConsumeToken(); RHS = ParseExpression(); if (RHS.isInvalid()) { T.skipToEnd(); return true; } } Diag(EllipsisLoc, getLangOpts().CPlusPlus17 ? diag::warn_cxx14_compat_fold_expression : diag::ext_fold_expression); T.consumeClose(); return Actions.ActOnCXXFoldExpr(getCurScope(), T.getOpenLocation(), LHS.get(), Kind, EllipsisLoc, RHS.get(), T.getCloseLocation()); } void Parser::injectEmbedTokens() { EmbedAnnotationData *Data = reinterpret_cast(Tok.getAnnotationValue()); MutableArrayRef Toks(PP.getPreprocessorAllocator().Allocate( Data->BinaryData.size() * 2 - 1), Data->BinaryData.size() * 2 - 1); unsigned I = 0; for (auto &Byte : Data->BinaryData) { Toks[I].startToken(); Toks[I].setKind(tok::binary_data); Toks[I].setLocation(Tok.getLocation()); Toks[I].setLength(1); Toks[I].setLiteralData(&Byte); if (I != ((Data->BinaryData.size() - 1) * 2)) { Toks[I + 1].startToken(); Toks[I + 1].setKind(tok::comma); Toks[I + 1].setLocation(Tok.getLocation()); } I += 2; } PP.EnterTokenStream(std::move(Toks), /*DisableMacroExpansion=*/true, /*IsReinject=*/true); ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); } bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, llvm::function_ref ExpressionStarts, bool FailImmediatelyOnInvalidExpr) { bool SawError = false; while (true) { if (ExpressionStarts) ExpressionStarts(); ExprResult Expr; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); Expr = ParseBraceInitializer(); } else Expr = ParseAssignmentExpression(); if (Tok.is(tok::ellipsis)) Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken()); else if (Tok.is(tok::code_completion)) { // There's nothing to suggest in here as we parsed a full expression. // Instead fail and propagate the error since caller might have something // the suggest, e.g. signature help in function call. Note that this is // performed before pushing the \p Expr, so that signature help can report // current argument correctly. SawError = true; cutOffParsing(); break; } if (Expr.isInvalid()) { SawError = true; if (FailImmediatelyOnInvalidExpr) break; SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); } else { Exprs.push_back(Expr.get()); } if (Tok.isNot(tok::comma)) break; // Move to the next argument, remember where the comma was. Token Comma = Tok; ConsumeToken(); checkPotentialAngleBracketDelimiter(Comma); } return SawError; } bool Parser::ParseSimpleExpressionList(SmallVectorImpl &Exprs) { while (true) { ExprResult Expr = ParseAssignmentExpression(); if (Expr.isInvalid()) return true; Exprs.push_back(Expr.get()); // We might be parsing the LHS of a fold-expression. If we reached the fold // operator, stop. if (Tok.isNot(tok::comma) || NextToken().is(tok::ellipsis)) return false; // Move to the next argument, remember where the comma was. Token Comma = Tok; ConsumeToken(); checkPotentialAngleBracketDelimiter(Comma); } } void Parser::ParseBlockId(SourceLocation CaretLoc) { if (Tok.is(tok::code_completion)) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteOrdinaryName( getCurScope(), SemaCodeCompletion::PCC_Type); return; } // Parse the specifier-qualifier-list piece. DeclSpec DS(AttrFactory); ParseSpecifierQualifierList(DS); // Parse the block-declarator. Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), DeclaratorContext::BlockLiteral); DeclaratorInfo.setFunctionDefinitionKind(FunctionDefinitionKind::Definition); ParseDeclarator(DeclaratorInfo); MaybeParseGNUAttributes(DeclaratorInfo); // Inform sema that we are starting a block. Actions.ActOnBlockArguments(CaretLoc, DeclaratorInfo, getCurScope()); } ExprResult Parser::ParseBlockLiteralExpression() { assert(Tok.is(tok::caret) && "block literal starts with ^"); SourceLocation CaretLoc = ConsumeToken(); PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), CaretLoc, "block literal parsing"); // Enter a scope to hold everything within the block. This includes the // argument decls, decls within the compound expression, etc. This also // allows determining whether a variable reference inside the block is // within or outside of the block. ParseScope BlockScope(this, Scope::BlockScope | Scope::FnScope | Scope::CompoundStmtScope | Scope::DeclScope); // Inform sema that we are starting a block. Actions.ActOnBlockStart(CaretLoc, getCurScope()); // Parse the return type if present. DeclSpec DS(AttrFactory); Declarator ParamInfo(DS, ParsedAttributesView::none(), DeclaratorContext::BlockLiteral); ParamInfo.setFunctionDefinitionKind(FunctionDefinitionKind::Definition); // FIXME: Since the return type isn't actually parsed, it can't be used to // fill ParamInfo with an initial valid range, so do it manually. ParamInfo.SetSourceRange(SourceRange(Tok.getLocation(), Tok.getLocation())); // If this block has arguments, parse them. There is no ambiguity here with // the expression case, because the expression case requires a parameter list. if (Tok.is(tok::l_paren)) { ParseParenDeclarator(ParamInfo); // Parse the pieces after the identifier as if we had "int(...)". // SetIdentifier sets the source range end, but in this case we're past // that location. SourceLocation Tmp = ParamInfo.getSourceRange().getEnd(); ParamInfo.SetIdentifier(nullptr, CaretLoc); ParamInfo.SetRangeEnd(Tmp); if (ParamInfo.isInvalidType()) { // If there was an error parsing the arguments, they may have // tried to use ^(x+y) which requires an argument list. Just // skip the whole block literal. Actions.ActOnBlockError(CaretLoc, getCurScope()); return ExprError(); } MaybeParseGNUAttributes(ParamInfo); // Inform sema that we are starting a block. Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope()); } else if (!Tok.is(tok::l_brace)) { ParseBlockId(CaretLoc); } else { // Otherwise, pretend we saw (void). SourceLocation NoLoc; ParamInfo.AddTypeInfo( DeclaratorChunk::getFunction(/*HasProto=*/true, /*IsAmbiguous=*/false, /*RParenLoc=*/NoLoc, /*ArgInfo=*/nullptr, /*NumParams=*/0, /*EllipsisLoc=*/NoLoc, /*RParenLoc=*/NoLoc, /*RefQualifierIsLvalueRef=*/true, /*RefQualifierLoc=*/NoLoc, /*MutableLoc=*/NoLoc, EST_None, /*ESpecRange=*/SourceRange(), /*Exceptions=*/nullptr, /*ExceptionRanges=*/nullptr, /*NumExceptions=*/0, /*NoexceptExpr=*/nullptr, /*ExceptionSpecTokens=*/nullptr, /*DeclsInPrototype=*/{}, CaretLoc, CaretLoc, ParamInfo), CaretLoc); MaybeParseGNUAttributes(ParamInfo); // Inform sema that we are starting a block. Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope()); } ExprResult Result(true); if (!Tok.is(tok::l_brace)) { // Saw something like: ^expr Diag(Tok, diag::err_expected_expression); Actions.ActOnBlockError(CaretLoc, getCurScope()); return ExprError(); } StmtResult Stmt(ParseCompoundStatementBody()); BlockScope.Exit(); if (!Stmt.isInvalid()) Result = Actions.ActOnBlockStmtExpr(CaretLoc, Stmt.get(), getCurScope()); else Actions.ActOnBlockError(CaretLoc, getCurScope()); return Result; } ExprResult Parser::ParseObjCBoolLiteral() { tok::TokenKind Kind = Tok.getKind(); return Actions.ObjC().ActOnObjCBoolLiteral(ConsumeToken(), Kind); } /// Validate availability spec list, emitting diagnostics if necessary. Returns /// true if invalid. static bool CheckAvailabilitySpecList(Parser &P, ArrayRef AvailSpecs) { llvm::SmallSet Platforms; bool HasOtherPlatformSpec = false; bool Valid = true; for (const auto &Spec : AvailSpecs) { if (Spec.isOtherPlatformSpec()) { if (HasOtherPlatformSpec) { P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_star); Valid = false; } HasOtherPlatformSpec = true; continue; } bool Inserted = Platforms.insert(Spec.getPlatform()).second; if (!Inserted) { // Rule out multiple version specs referring to the same platform. // For example, we emit an error for: // @available(macos 10.10, macos 10.11, *) StringRef Platform = Spec.getPlatform(); P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_platform) << Spec.getEndLoc() << Platform; Valid = false; } } if (!HasOtherPlatformSpec) { SourceLocation InsertWildcardLoc = AvailSpecs.back().getEndLoc(); P.Diag(InsertWildcardLoc, diag::err_availability_query_wildcard_required) << FixItHint::CreateInsertion(InsertWildcardLoc, ", *"); return true; } return !Valid; } std::optional Parser::ParseAvailabilitySpec() { if (Tok.is(tok::star)) { return AvailabilitySpec(ConsumeToken()); } else { // Parse the platform name. if (Tok.is(tok::code_completion)) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteAvailabilityPlatformName(); return std::nullopt; } if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_avail_query_expected_platform_name); return std::nullopt; } IdentifierLoc *PlatformIdentifier = ParseIdentifierLoc(); SourceRange VersionRange; VersionTuple Version = ParseVersionTuple(VersionRange); if (Version.empty()) return std::nullopt; StringRef GivenPlatform = PlatformIdentifier->getIdentifierInfo()->getName(); StringRef Platform = AvailabilityAttr::canonicalizePlatformName(GivenPlatform); if (AvailabilityAttr::getPrettyPlatformName(Platform).empty() || (GivenPlatform.contains("xros") || GivenPlatform.contains("xrOS"))) { Diag(PlatformIdentifier->getLoc(), diag::err_avail_query_unrecognized_platform_name) << GivenPlatform; return std::nullopt; } return AvailabilitySpec(Version, Platform, PlatformIdentifier->getLoc(), VersionRange.getEnd()); } } ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) { assert(Tok.is(tok::kw___builtin_available) || Tok.isObjCAtKeyword(tok::objc_available)); // Eat the available or __builtin_available. ConsumeToken(); BalancedDelimiterTracker Parens(*this, tok::l_paren); if (Parens.expectAndConsume()) return ExprError(); SmallVector AvailSpecs; bool HasError = false; while (true) { std::optional Spec = ParseAvailabilitySpec(); if (!Spec) HasError = true; else AvailSpecs.push_back(*Spec); if (!TryConsumeToken(tok::comma)) break; } if (HasError) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); } CheckAvailabilitySpecList(*this, AvailSpecs); if (Parens.consumeClose()) return ExprError(); return Actions.ObjC().ActOnObjCAvailabilityCheckExpr( AvailSpecs, BeginLoc, Parens.getCloseLocation()); }