//===--- ParseObjC.cpp - Objective C Parsing ------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the Objective-C portions of the Parser interface. // //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/CharInfo.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/PrettyDeclStackTrace.h" #include "clang/Sema/Scope.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" using namespace clang; /// Skips attributes after an Objective-C @ directive. Emits a diagnostic. void Parser::MaybeSkipAttributes(tok::ObjCKeywordKind Kind) { ParsedAttributes attrs(AttrFactory); if (Tok.is(tok::kw___attribute)) { if (Kind == tok::objc_interface || Kind == tok::objc_protocol) Diag(Tok, diag::err_objc_postfix_attribute_hint) << (Kind == tok::objc_protocol); else Diag(Tok, diag::err_objc_postfix_attribute); ParseGNUAttributes(attrs); } } /// ParseObjCAtDirectives - Handle parts of the external-declaration production: /// external-declaration: [C99 6.9] /// [OBJC] objc-class-definition /// [OBJC] objc-class-declaration /// [OBJC] objc-alias-declaration /// [OBJC] objc-protocol-definition /// [OBJC] objc-method-definition /// [OBJC] '@' 'end' Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives() { SourceLocation AtLoc = ConsumeToken(); // the "@" if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCAtDirective(getCurScope()); cutOffParsing(); return nullptr; } Decl *SingleDecl = nullptr; switch (Tok.getObjCKeywordID()) { case tok::objc_class: return ParseObjCAtClassDeclaration(AtLoc); case tok::objc_interface: { ParsedAttributes attrs(AttrFactory); SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, attrs); break; } case tok::objc_protocol: { ParsedAttributes attrs(AttrFactory); return ParseObjCAtProtocolDeclaration(AtLoc, attrs); } case tok::objc_implementation: return ParseObjCAtImplementationDeclaration(AtLoc); case tok::objc_end: return ParseObjCAtEndDeclaration(AtLoc); case tok::objc_compatibility_alias: SingleDecl = ParseObjCAtAliasDeclaration(AtLoc); break; case tok::objc_synthesize: SingleDecl = ParseObjCPropertySynthesize(AtLoc); break; case tok::objc_dynamic: SingleDecl = ParseObjCPropertyDynamic(AtLoc); break; case tok::objc_import: if (getLangOpts().Modules || getLangOpts().DebuggerSupport) return ParseModuleImport(AtLoc); Diag(AtLoc, diag::err_atimport); SkipUntil(tok::semi); return Actions.ConvertDeclToDeclGroup(nullptr); default: Diag(AtLoc, diag::err_unexpected_at); SkipUntil(tok::semi); SingleDecl = nullptr; break; } return Actions.ConvertDeclToDeclGroup(SingleDecl); } /// Class to handle popping type parameters when leaving the scope. class Parser::ObjCTypeParamListScope { Sema &Actions; Scope *S; ObjCTypeParamList *Params; public: ObjCTypeParamListScope(Sema &Actions, Scope *S) : Actions(Actions), S(S), Params(nullptr) {} ~ObjCTypeParamListScope() { leave(); } void enter(ObjCTypeParamList *P) { assert(!Params); Params = P; } void leave() { if (Params) Actions.popObjCTypeParamList(S, Params); Params = nullptr; } }; /// /// objc-class-declaration: /// '@' 'class' objc-class-forward-decl (',' objc-class-forward-decl)* ';' /// /// objc-class-forward-decl: /// identifier objc-type-parameter-list[opt] /// Parser::DeclGroupPtrTy Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { ConsumeToken(); // the identifier "class" SmallVector ClassNames; SmallVector ClassLocs; SmallVector ClassTypeParams; while (1) { MaybeSkipAttributes(tok::objc_class); if (expectIdentifier()) { SkipUntil(tok::semi); return Actions.ConvertDeclToDeclGroup(nullptr); } ClassNames.push_back(Tok.getIdentifierInfo()); ClassLocs.push_back(Tok.getLocation()); ConsumeToken(); // Parse the optional objc-type-parameter-list. ObjCTypeParamList *TypeParams = nullptr; if (Tok.is(tok::less)) TypeParams = parseObjCTypeParamList(); ClassTypeParams.push_back(TypeParams); if (!TryConsumeToken(tok::comma)) break; } // Consume the ';'. if (ExpectAndConsume(tok::semi, diag::err_expected_after, "@class")) return Actions.ConvertDeclToDeclGroup(nullptr); return Actions.ActOnForwardClassDeclaration(atLoc, ClassNames.data(), ClassLocs.data(), ClassTypeParams, ClassNames.size()); } void Parser::CheckNestedObjCContexts(SourceLocation AtLoc) { Sema::ObjCContainerKind ock = Actions.getObjCContainerKind(); if (ock == Sema::OCK_None) return; Decl *Decl = Actions.getObjCDeclContext(); if (CurParsedObjCImpl) { CurParsedObjCImpl->finish(AtLoc); } else { Actions.ActOnAtEnd(getCurScope(), AtLoc); } Diag(AtLoc, diag::err_objc_missing_end) << FixItHint::CreateInsertion(AtLoc, "@end\n"); if (Decl) Diag(Decl->getLocStart(), diag::note_objc_container_start) << (int) ock; } /// /// objc-interface: /// objc-class-interface-attributes[opt] objc-class-interface /// objc-category-interface /// /// objc-class-interface: /// '@' 'interface' identifier objc-type-parameter-list[opt] /// objc-superclass[opt] objc-protocol-refs[opt] /// objc-class-instance-variables[opt] /// objc-interface-decl-list /// @end /// /// objc-category-interface: /// '@' 'interface' identifier objc-type-parameter-list[opt] /// '(' identifier[opt] ')' objc-protocol-refs[opt] /// objc-interface-decl-list /// @end /// /// objc-superclass: /// ':' identifier objc-type-arguments[opt] /// /// objc-class-interface-attributes: /// __attribute__((visibility("default"))) /// __attribute__((visibility("hidden"))) /// __attribute__((deprecated)) /// __attribute__((unavailable)) /// __attribute__((objc_exception)) - used by NSException on 64-bit /// __attribute__((objc_root_class)) /// Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParsedAttributes &attrs) { assert(Tok.isObjCAtKeyword(tok::objc_interface) && "ParseObjCAtInterfaceDeclaration(): Expected @interface"); CheckNestedObjCContexts(AtLoc); ConsumeToken(); // the "interface" identifier // Code completion after '@interface'. if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCInterfaceDecl(getCurScope()); cutOffParsing(); return nullptr; } MaybeSkipAttributes(tok::objc_interface); if (expectIdentifier()) return nullptr; // missing class or category name. // We have a class or category name - consume it. IdentifierInfo *nameId = Tok.getIdentifierInfo(); SourceLocation nameLoc = ConsumeToken(); // Parse the objc-type-parameter-list or objc-protocol-refs. For the latter // case, LAngleLoc will be valid and ProtocolIdents will capture the // protocol references (that have not yet been resolved). SourceLocation LAngleLoc, EndProtoLoc; SmallVector ProtocolIdents; ObjCTypeParamList *typeParameterList = nullptr; ObjCTypeParamListScope typeParamScope(Actions, getCurScope()); if (Tok.is(tok::less)) typeParameterList = parseObjCTypeParamListOrProtocolRefs( typeParamScope, LAngleLoc, ProtocolIdents, EndProtoLoc); if (Tok.is(tok::l_paren) && !isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { // we have a category. BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); SourceLocation categoryLoc; IdentifierInfo *categoryId = nullptr; if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCInterfaceCategory(getCurScope(), nameId, nameLoc); cutOffParsing(); return nullptr; } // For ObjC2, the category name is optional (not an error). if (Tok.is(tok::identifier)) { categoryId = Tok.getIdentifierInfo(); categoryLoc = ConsumeToken(); } else if (!getLangOpts().ObjC2) { Diag(Tok, diag::err_expected) << tok::identifier; // missing category name. return nullptr; } T.consumeClose(); if (T.getCloseLocation().isInvalid()) return nullptr; // Next, we need to check for any protocol references. assert(LAngleLoc.isInvalid() && "Cannot have already parsed protocols"); SmallVector ProtocolRefs; SmallVector ProtocolLocs; if (Tok.is(tok::less) && ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, true, true, LAngleLoc, EndProtoLoc, /*consumeLastToken=*/true)) return nullptr; Decl *CategoryType = Actions.ActOnStartCategoryInterface( AtLoc, nameId, nameLoc, typeParameterList, categoryId, categoryLoc, ProtocolRefs.data(), ProtocolRefs.size(), ProtocolLocs.data(), EndProtoLoc, attrs.getList()); if (Tok.is(tok::l_brace)) ParseObjCClassInstanceVariables(CategoryType, tok::objc_private, AtLoc); ParseObjCInterfaceDeclList(tok::objc_not_keyword, CategoryType); return CategoryType; } // Parse a class interface. IdentifierInfo *superClassId = nullptr; SourceLocation superClassLoc; SourceLocation typeArgsLAngleLoc; SmallVector typeArgs; SourceLocation typeArgsRAngleLoc; SmallVector protocols; SmallVector protocolLocs; if (Tok.is(tok::colon)) { // a super class is specified. ConsumeToken(); // Code completion of superclass names. if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCSuperclass(getCurScope(), nameId, nameLoc); cutOffParsing(); return nullptr; } if (expectIdentifier()) return nullptr; // missing super class name. superClassId = Tok.getIdentifierInfo(); superClassLoc = ConsumeToken(); // Type arguments for the superclass or protocol conformances. if (Tok.is(tok::less)) { parseObjCTypeArgsOrProtocolQualifiers( nullptr, typeArgsLAngleLoc, typeArgs, typeArgsRAngleLoc, LAngleLoc, protocols, protocolLocs, EndProtoLoc, /*consumeLastToken=*/true, /*warnOnIncompleteProtocols=*/true); if (Tok.is(tok::eof)) return nullptr; } } // Next, we need to check for any protocol references. if (LAngleLoc.isValid()) { if (!ProtocolIdents.empty()) { // We already parsed the protocols named when we thought we had a // type parameter list. Translate them into actual protocol references. for (const auto &pair : ProtocolIdents) { protocolLocs.push_back(pair.second); } Actions.FindProtocolDeclaration(/*WarnOnDeclarations=*/true, /*ForObjCContainer=*/true, ProtocolIdents, protocols); } } else if (protocols.empty() && Tok.is(tok::less) && ParseObjCProtocolReferences(protocols, protocolLocs, true, true, LAngleLoc, EndProtoLoc, /*consumeLastToken=*/true)) { return nullptr; } if (Tok.isNot(tok::less)) Actions.ActOnTypedefedProtocols(protocols, protocolLocs, superClassId, superClassLoc); Decl *ClsType = Actions.ActOnStartClassInterface(getCurScope(), AtLoc, nameId, nameLoc, typeParameterList, superClassId, superClassLoc, typeArgs, SourceRange(typeArgsLAngleLoc, typeArgsRAngleLoc), protocols.data(), protocols.size(), protocolLocs.data(), EndProtoLoc, attrs.getList()); if (Tok.is(tok::l_brace)) ParseObjCClassInstanceVariables(ClsType, tok::objc_protected, AtLoc); ParseObjCInterfaceDeclList(tok::objc_interface, ClsType); return ClsType; } /// Add an attribute for a context-sensitive type nullability to the given /// declarator. static void addContextSensitiveTypeNullability(Parser &P, Declarator &D, NullabilityKind nullability, SourceLocation nullabilityLoc, bool &addedToDeclSpec) { // Create the attribute. auto getNullabilityAttr = [&]() -> AttributeList * { return D.getAttributePool().create( P.getNullabilityKeyword(nullability), SourceRange(nullabilityLoc), nullptr, SourceLocation(), nullptr, 0, AttributeList::AS_ContextSensitiveKeyword); }; if (D.getNumTypeObjects() > 0) { // Add the attribute to the declarator chunk nearest the declarator. auto nullabilityAttr = getNullabilityAttr(); DeclaratorChunk &chunk = D.getTypeObject(0); nullabilityAttr->setNext(chunk.getAttrListRef()); chunk.getAttrListRef() = nullabilityAttr; } else if (!addedToDeclSpec) { // Otherwise, just put it on the declaration specifiers (if one // isn't there already). D.getMutableDeclSpec().addAttributes(getNullabilityAttr()); addedToDeclSpec = true; } } /// Parse an Objective-C type parameter list, if present, or capture /// the locations of the protocol identifiers for a list of protocol /// references. /// /// objc-type-parameter-list: /// '<' objc-type-parameter (',' objc-type-parameter)* '>' /// /// objc-type-parameter: /// objc-type-parameter-variance? identifier objc-type-parameter-bound[opt] /// /// objc-type-parameter-bound: /// ':' type-name /// /// objc-type-parameter-variance: /// '__covariant' /// '__contravariant' /// /// \param lAngleLoc The location of the starting '<'. /// /// \param protocolIdents Will capture the list of identifiers, if the /// angle brackets contain a list of protocol references rather than a /// type parameter list. /// /// \param rAngleLoc The location of the ending '>'. ObjCTypeParamList *Parser::parseObjCTypeParamListOrProtocolRefs( ObjCTypeParamListScope &Scope, SourceLocation &lAngleLoc, SmallVectorImpl &protocolIdents, SourceLocation &rAngleLoc, bool mayBeProtocolList) { assert(Tok.is(tok::less) && "Not at the beginning of a type parameter list"); // Within the type parameter list, don't treat '>' as an operator. GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); // Local function to "flush" the protocol identifiers, turning them into // type parameters. SmallVector typeParams; auto makeProtocolIdentsIntoTypeParameters = [&]() { unsigned index = 0; for (const auto &pair : protocolIdents) { DeclResult typeParam = Actions.actOnObjCTypeParam( getCurScope(), ObjCTypeParamVariance::Invariant, SourceLocation(), index++, pair.first, pair.second, SourceLocation(), nullptr); if (typeParam.isUsable()) typeParams.push_back(typeParam.get()); } protocolIdents.clear(); mayBeProtocolList = false; }; bool invalid = false; lAngleLoc = ConsumeToken(); do { // Parse the variance, if any. SourceLocation varianceLoc; ObjCTypeParamVariance variance = ObjCTypeParamVariance::Invariant; if (Tok.is(tok::kw___covariant) || Tok.is(tok::kw___contravariant)) { variance = Tok.is(tok::kw___covariant) ? ObjCTypeParamVariance::Covariant : ObjCTypeParamVariance::Contravariant; varianceLoc = ConsumeToken(); // Once we've seen a variance specific , we know this is not a // list of protocol references. if (mayBeProtocolList) { // Up until now, we have been queuing up parameters because they // might be protocol references. Turn them into parameters now. makeProtocolIdentsIntoTypeParameters(); } } // Parse the identifier. if (!Tok.is(tok::identifier)) { // Code completion. if (Tok.is(tok::code_completion)) { // FIXME: If these aren't protocol references, we'll need different // completions. Actions.CodeCompleteObjCProtocolReferences(protocolIdents); cutOffParsing(); // FIXME: Better recovery here?. return nullptr; } Diag(Tok, diag::err_objc_expected_type_parameter); invalid = true; break; } IdentifierInfo *paramName = Tok.getIdentifierInfo(); SourceLocation paramLoc = ConsumeToken(); // If there is a bound, parse it. SourceLocation colonLoc; TypeResult boundType; if (TryConsumeToken(tok::colon, colonLoc)) { // Once we've seen a bound, we know this is not a list of protocol // references. if (mayBeProtocolList) { // Up until now, we have been queuing up parameters because they // might be protocol references. Turn them into parameters now. makeProtocolIdentsIntoTypeParameters(); } // type-name boundType = ParseTypeName(); if (boundType.isInvalid()) invalid = true; } else if (mayBeProtocolList) { // If this could still be a protocol list, just capture the identifier. // We don't want to turn it into a parameter. protocolIdents.push_back(std::make_pair(paramName, paramLoc)); continue; } // Create the type parameter. DeclResult typeParam = Actions.actOnObjCTypeParam( getCurScope(), variance, varianceLoc, typeParams.size(), paramName, paramLoc, colonLoc, boundType.isUsable() ? boundType.get() : nullptr); if (typeParam.isUsable()) typeParams.push_back(typeParam.get()); } while (TryConsumeToken(tok::comma)); // Parse the '>'. if (invalid) { SkipUntil(tok::greater, tok::at, StopBeforeMatch); if (Tok.is(tok::greater)) ConsumeToken(); } else if (ParseGreaterThanInTemplateList(rAngleLoc, /*ConsumeLastToken=*/true, /*ObjCGenericList=*/true)) { Diag(lAngleLoc, diag::note_matching) << "'<'"; SkipUntil({tok::greater, tok::greaterequal, tok::at, tok::minus, tok::minus, tok::plus, tok::colon, tok::l_paren, tok::l_brace, tok::comma, tok::semi }, StopBeforeMatch); if (Tok.is(tok::greater)) ConsumeToken(); } if (mayBeProtocolList) { // A type parameter list must be followed by either a ':' (indicating the // presence of a superclass) or a '(' (indicating that this is a category // or extension). This disambiguates between an objc-type-parameter-list // and a objc-protocol-refs. if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_paren)) { // Returning null indicates that we don't have a type parameter list. // The results the caller needs to handle the protocol references are // captured in the reference parameters already. return nullptr; } // We have a type parameter list that looks like a list of protocol // references. Turn that parameter list into type parameters. makeProtocolIdentsIntoTypeParameters(); } // Form the type parameter list and enter its scope. ObjCTypeParamList *list = Actions.actOnObjCTypeParamList( getCurScope(), lAngleLoc, typeParams, rAngleLoc); Scope.enter(list); // Clear out the angle locations; they're used by the caller to indicate // whether there are any protocol references. lAngleLoc = SourceLocation(); rAngleLoc = SourceLocation(); return invalid ? nullptr : list; } /// Parse an objc-type-parameter-list. ObjCTypeParamList *Parser::parseObjCTypeParamList() { SourceLocation lAngleLoc; SmallVector protocolIdents; SourceLocation rAngleLoc; ObjCTypeParamListScope Scope(Actions, getCurScope()); return parseObjCTypeParamListOrProtocolRefs(Scope, lAngleLoc, protocolIdents, rAngleLoc, /*mayBeProtocolList=*/false); } /// objc-interface-decl-list: /// empty /// objc-interface-decl-list objc-property-decl [OBJC2] /// objc-interface-decl-list objc-method-requirement [OBJC2] /// objc-interface-decl-list objc-method-proto ';' /// objc-interface-decl-list declaration /// objc-interface-decl-list ';' /// /// objc-method-requirement: [OBJC2] /// @required /// @optional /// void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, Decl *CDecl) { SmallVector allMethods; SmallVector allTUVariables; tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword; SourceRange AtEnd; while (1) { // If this is a method prototype, parse it. if (Tok.isOneOf(tok::minus, tok::plus)) { if (Decl *methodPrototype = ParseObjCMethodPrototype(MethodImplKind, false)) allMethods.push_back(methodPrototype); // Consume the ';' here, since ParseObjCMethodPrototype() is re-used for // method definitions. if (ExpectAndConsumeSemi(diag::err_expected_semi_after_method_proto)) { // We didn't find a semi and we error'ed out. Skip until a ';' or '@'. SkipUntil(tok::at, StopAtSemi | StopBeforeMatch); if (Tok.is(tok::semi)) ConsumeToken(); } continue; } if (Tok.is(tok::l_paren)) { Diag(Tok, diag::err_expected_minus_or_plus); ParseObjCMethodDecl(Tok.getLocation(), tok::minus, MethodImplKind, false); continue; } // Ignore excess semicolons. if (Tok.is(tok::semi)) { ConsumeToken(); continue; } // If we got to the end of the file, exit the loop. if (isEofOrEom()) break; // Code completion within an Objective-C interface. if (Tok.is(tok::code_completion)) { Actions.CodeCompleteOrdinaryName(getCurScope(), CurParsedObjCImpl? Sema::PCC_ObjCImplementation : Sema::PCC_ObjCInterface); return cutOffParsing(); } // If we don't have an @ directive, parse it as a function definition. if (Tok.isNot(tok::at)) { // The code below does not consume '}'s because it is afraid of eating the // end of a namespace. Because of the way this code is structured, an // erroneous r_brace would cause an infinite loop if not handled here. if (Tok.is(tok::r_brace)) break; ParsedAttributesWithRange attrs(AttrFactory); allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs)); continue; } // Otherwise, we have an @ directive, eat the @. SourceLocation AtLoc = ConsumeToken(); // the "@" if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCAtDirective(getCurScope()); return cutOffParsing(); } tok::ObjCKeywordKind DirectiveKind = Tok.getObjCKeywordID(); if (DirectiveKind == tok::objc_end) { // @end -> terminate list AtEnd.setBegin(AtLoc); AtEnd.setEnd(Tok.getLocation()); break; } else if (DirectiveKind == tok::objc_not_keyword) { Diag(Tok, diag::err_objc_unknown_at); SkipUntil(tok::semi); continue; } // Eat the identifier. ConsumeToken(); switch (DirectiveKind) { default: // FIXME: If someone forgets an @end on a protocol, this loop will // continue to eat up tons of stuff and spew lots of nonsense errors. It // would probably be better to bail out if we saw an @class or @interface // or something like that. Diag(AtLoc, diag::err_objc_illegal_interface_qual); // Skip until we see an '@' or '}' or ';'. SkipUntil(tok::r_brace, tok::at, StopAtSemi); break; case tok::objc_implementation: case tok::objc_interface: Diag(AtLoc, diag::err_objc_missing_end) << FixItHint::CreateInsertion(AtLoc, "@end\n"); Diag(CDecl->getLocStart(), diag::note_objc_container_start) << (int) Actions.getObjCContainerKind(); ConsumeToken(); break; case tok::objc_required: case tok::objc_optional: // This is only valid on protocols. // FIXME: Should this check for ObjC2 being enabled? if (contextKey != tok::objc_protocol) Diag(AtLoc, diag::err_objc_directive_only_in_protocol); else MethodImplKind = DirectiveKind; break; case tok::objc_property: if (!getLangOpts().ObjC2) Diag(AtLoc, diag::err_objc_properties_require_objc2); ObjCDeclSpec OCDS; SourceLocation LParenLoc; // Parse property attribute list, if any. if (Tok.is(tok::l_paren)) { LParenLoc = Tok.getLocation(); ParseObjCPropertyAttribute(OCDS); } bool addedToDeclSpec = false; auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) { if (FD.D.getIdentifier() == nullptr) { Diag(AtLoc, diag::err_objc_property_requires_field_name) << FD.D.getSourceRange(); return; } if (FD.BitfieldSize) { Diag(AtLoc, diag::err_objc_property_bitfield) << FD.D.getSourceRange(); return; } // Map a nullability property attribute to a context-sensitive keyword // attribute. if (OCDS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) addContextSensitiveTypeNullability(*this, FD.D, OCDS.getNullability(), OCDS.getNullabilityLoc(), addedToDeclSpec); // Install the property declarator into interfaceDecl. IdentifierInfo *SelName = OCDS.getGetterName() ? OCDS.getGetterName() : FD.D.getIdentifier(); Selector GetterSel = PP.getSelectorTable().getNullarySelector(SelName); IdentifierInfo *SetterName = OCDS.getSetterName(); Selector SetterSel; if (SetterName) SetterSel = PP.getSelectorTable().getSelector(1, &SetterName); else SetterSel = SelectorTable::constructSetterSelector( PP.getIdentifierTable(), PP.getSelectorTable(), FD.D.getIdentifier()); Decl *Property = Actions.ActOnProperty( getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel, MethodImplKind); FD.complete(Property); }; // Parse all the comma separated declarators. ParsingDeclSpec DS(*this); ParseStructDeclaration(DS, ObjCPropertyCallback); ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list); break; } } // We break out of the big loop in two cases: when we see @end or when we see // EOF. In the former case, eat the @end. In the later case, emit an error. if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCAtDirective(getCurScope()); return cutOffParsing(); } else if (Tok.isObjCAtKeyword(tok::objc_end)) { ConsumeToken(); // the "end" identifier } else { Diag(Tok, diag::err_objc_missing_end) << FixItHint::CreateInsertion(Tok.getLocation(), "\n@end\n"); Diag(CDecl->getLocStart(), diag::note_objc_container_start) << (int) Actions.getObjCContainerKind(); AtEnd.setBegin(Tok.getLocation()); AtEnd.setEnd(Tok.getLocation()); } // Insert collected methods declarations into the @interface object. // This passes in an invalid SourceLocation for AtEndLoc when EOF is hit. Actions.ActOnAtEnd(getCurScope(), AtEnd, allMethods, allTUVariables); } /// Diagnose redundant or conflicting nullability information. static void diagnoseRedundantPropertyNullability(Parser &P, ObjCDeclSpec &DS, NullabilityKind nullability, SourceLocation nullabilityLoc){ if (DS.getNullability() == nullability) { P.Diag(nullabilityLoc, diag::warn_nullability_duplicate) << DiagNullabilityKind(nullability, true) << SourceRange(DS.getNullabilityLoc()); return; } P.Diag(nullabilityLoc, diag::err_nullability_conflicting) << DiagNullabilityKind(nullability, true) << DiagNullabilityKind(DS.getNullability(), true) << SourceRange(DS.getNullabilityLoc()); } /// Parse property attribute declarations. /// /// property-attr-decl: '(' property-attrlist ')' /// property-attrlist: /// property-attribute /// property-attrlist ',' property-attribute /// property-attribute: /// getter '=' identifier /// setter '=' identifier ':' /// readonly /// readwrite /// assign /// retain /// copy /// nonatomic /// atomic /// strong /// weak /// unsafe_unretained /// nonnull /// nullable /// null_unspecified /// null_resettable /// class /// void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { assert(Tok.getKind() == tok::l_paren); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); while (1) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCPropertyFlags(getCurScope(), DS); return cutOffParsing(); } const IdentifierInfo *II = Tok.getIdentifierInfo(); // If this is not an identifier at all, bail out early. if (!II) { T.consumeClose(); return; } SourceLocation AttrName = ConsumeToken(); // consume last attribute name if (II->isStr("readonly")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly); else if (II->isStr("assign")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign); else if (II->isStr("unsafe_unretained")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_unsafe_unretained); else if (II->isStr("readwrite")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite); else if (II->isStr("retain")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain); else if (II->isStr("strong")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_strong); else if (II->isStr("copy")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy); else if (II->isStr("nonatomic")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic); else if (II->isStr("atomic")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_atomic); else if (II->isStr("weak")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_weak); else if (II->isStr("getter") || II->isStr("setter")) { bool IsSetter = II->getNameStart()[0] == 's'; // getter/setter require extra treatment. unsigned DiagID = IsSetter ? diag::err_objc_expected_equal_for_setter : diag::err_objc_expected_equal_for_getter; if (ExpectAndConsume(tok::equal, DiagID)) { SkipUntil(tok::r_paren, StopAtSemi); return; } if (Tok.is(tok::code_completion)) { if (IsSetter) Actions.CodeCompleteObjCPropertySetter(getCurScope()); else Actions.CodeCompleteObjCPropertyGetter(getCurScope()); return cutOffParsing(); } SourceLocation SelLoc; IdentifierInfo *SelIdent = ParseObjCSelectorPiece(SelLoc); if (!SelIdent) { Diag(Tok, diag::err_objc_expected_selector_for_getter_setter) << IsSetter; SkipUntil(tok::r_paren, StopAtSemi); return; } if (IsSetter) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_setter); DS.setSetterName(SelIdent, SelLoc); if (ExpectAndConsume(tok::colon, diag::err_expected_colon_after_setter_name)) { SkipUntil(tok::r_paren, StopAtSemi); return; } } else { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter); DS.setGetterName(SelIdent, SelLoc); } } else if (II->isStr("nonnull")) { if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::NonNull, Tok.getLocation()); DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull); } else if (II->isStr("nullable")) { if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::Nullable, Tok.getLocation()); DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable); } else if (II->isStr("null_unspecified")) { if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::Unspecified, Tok.getLocation()); DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified); } else if (II->isStr("null_resettable")) { if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::Unspecified, Tok.getLocation()); DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified); // Also set the null_resettable bit. DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable); } else if (II->isStr("class")) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class); } else { Diag(AttrName, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); return; } if (Tok.isNot(tok::comma)) break; ConsumeToken(); } T.consumeClose(); } /// objc-method-proto: /// objc-instance-method objc-method-decl objc-method-attributes[opt] /// objc-class-method objc-method-decl objc-method-attributes[opt] /// /// objc-instance-method: '-' /// objc-class-method: '+' /// /// objc-method-attributes: [OBJC2] /// __attribute__((deprecated)) /// Decl *Parser::ParseObjCMethodPrototype(tok::ObjCKeywordKind MethodImplKind, bool MethodDefinition) { assert(Tok.isOneOf(tok::minus, tok::plus) && "expected +/-"); tok::TokenKind methodType = Tok.getKind(); SourceLocation mLoc = ConsumeToken(); Decl *MDecl = ParseObjCMethodDecl(mLoc, methodType, MethodImplKind, MethodDefinition); // Since this rule is used for both method declarations and definitions, // the caller is (optionally) responsible for consuming the ';'. return MDecl; } /// objc-selector: /// identifier /// one of /// enum struct union if else while do for switch case default /// break continue return goto asm sizeof typeof __alignof /// unsigned long const short volatile signed restrict _Complex /// in out inout bycopy byref oneway int char float double void _Bool /// IdentifierInfo *Parser::ParseObjCSelectorPiece(SourceLocation &SelectorLoc) { switch (Tok.getKind()) { default: return nullptr; case tok::colon: // Empty selector piece uses the location of the ':'. SelectorLoc = Tok.getLocation(); return nullptr; case tok::ampamp: case tok::ampequal: case tok::amp: case tok::pipe: case tok::tilde: case tok::exclaim: case tok::exclaimequal: case tok::pipepipe: case tok::pipeequal: case tok::caret: case tok::caretequal: { std::string ThisTok(PP.getSpelling(Tok)); if (isLetter(ThisTok[0])) { IdentifierInfo *II = &PP.getIdentifierTable().get(ThisTok); Tok.setKind(tok::identifier); SelectorLoc = ConsumeToken(); return II; } return nullptr; } case tok::identifier: case tok::kw_asm: case tok::kw_auto: case tok::kw_bool: case tok::kw_break: case tok::kw_case: case tok::kw_catch: case tok::kw_char: case tok::kw_class: case tok::kw_const: case tok::kw_const_cast: case tok::kw_continue: case tok::kw_default: case tok::kw_delete: case tok::kw_do: case tok::kw_double: case tok::kw_dynamic_cast: case tok::kw_else: case tok::kw_enum: case tok::kw_explicit: case tok::kw_export: case tok::kw_extern: case tok::kw_false: case tok::kw_float: case tok::kw_for: case tok::kw_friend: case tok::kw_goto: case tok::kw_if: case tok::kw_inline: case tok::kw_int: case tok::kw_long: case tok::kw_mutable: case tok::kw_namespace: case tok::kw_new: case tok::kw_operator: case tok::kw_private: case tok::kw_protected: case tok::kw_public: case tok::kw_register: case tok::kw_reinterpret_cast: case tok::kw_restrict: case tok::kw_return: case tok::kw_short: case tok::kw_signed: case tok::kw_sizeof: case tok::kw_static: case tok::kw_static_cast: case tok::kw_struct: case tok::kw_switch: case tok::kw_template: case tok::kw_this: case tok::kw_throw: case tok::kw_true: case tok::kw_try: case tok::kw_typedef: case tok::kw_typeid: case tok::kw_typename: case tok::kw_typeof: case tok::kw_union: case tok::kw_unsigned: case tok::kw_using: case tok::kw_virtual: case tok::kw_void: case tok::kw_volatile: case tok::kw_wchar_t: case tok::kw_while: case tok::kw__Bool: case tok::kw__Complex: case tok::kw___alignof: case tok::kw___auto_type: IdentifierInfo *II = Tok.getIdentifierInfo(); SelectorLoc = ConsumeToken(); return II; } } /// objc-for-collection-in: 'in' /// bool Parser::isTokIdentifier_in() const { // FIXME: May have to do additional look-ahead to only allow for // valid tokens following an 'in'; such as an identifier, unary operators, // '[' etc. return (getLangOpts().ObjC2 && Tok.is(tok::identifier) && Tok.getIdentifierInfo() == ObjCTypeQuals[objc_in]); } /// ParseObjCTypeQualifierList - This routine parses the objective-c's type /// qualifier list and builds their bitmask representation in the input /// argument. /// /// objc-type-qualifiers: /// objc-type-qualifier /// objc-type-qualifiers objc-type-qualifier /// /// objc-type-qualifier: /// 'in' /// 'out' /// 'inout' /// 'oneway' /// 'bycopy' /// 'byref' /// 'nonnull' /// 'nullable' /// 'null_unspecified' /// void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS, Declarator::TheContext Context) { assert(Context == Declarator::ObjCParameterContext || Context == Declarator::ObjCResultContext); while (1) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCPassingType(getCurScope(), DS, Context == Declarator::ObjCParameterContext); return cutOffParsing(); } if (Tok.isNot(tok::identifier)) return; const IdentifierInfo *II = Tok.getIdentifierInfo(); for (unsigned i = 0; i != objc_NumQuals; ++i) { if (II != ObjCTypeQuals[i] || NextToken().is(tok::less) || NextToken().is(tok::coloncolon)) continue; ObjCDeclSpec::ObjCDeclQualifier Qual; NullabilityKind Nullability; switch (i) { default: llvm_unreachable("Unknown decl qualifier"); case objc_in: Qual = ObjCDeclSpec::DQ_In; break; case objc_out: Qual = ObjCDeclSpec::DQ_Out; break; case objc_inout: Qual = ObjCDeclSpec::DQ_Inout; break; case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break; case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break; case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break; case objc_nonnull: Qual = ObjCDeclSpec::DQ_CSNullability; Nullability = NullabilityKind::NonNull; break; case objc_nullable: Qual = ObjCDeclSpec::DQ_CSNullability; Nullability = NullabilityKind::Nullable; break; case objc_null_unspecified: Qual = ObjCDeclSpec::DQ_CSNullability; Nullability = NullabilityKind::Unspecified; break; } // FIXME: Diagnose redundant specifiers. DS.setObjCDeclQualifier(Qual); if (Qual == ObjCDeclSpec::DQ_CSNullability) DS.setNullability(Tok.getLocation(), Nullability); ConsumeToken(); II = nullptr; break; } // If this wasn't a recognized qualifier, bail out. if (II) return; } } /// Take all the decl attributes out of the given list and add /// them to the given attribute set. static void takeDeclAttributes(ParsedAttributes &attrs, AttributeList *list) { while (list) { AttributeList *cur = list; list = cur->getNext(); if (!cur->isUsedAsTypeAttr()) { // Clear out the next pointer. We're really completely // destroying the internal invariants of the declarator here, // but it doesn't matter because we're done with it. cur->setNext(nullptr); attrs.add(cur); } } } /// takeDeclAttributes - Take all the decl attributes from the given /// declarator and add them to the given list. static void takeDeclAttributes(ParsedAttributes &attrs, Declarator &D) { // First, take ownership of all attributes. attrs.getPool().takeAllFrom(D.getAttributePool()); attrs.getPool().takeAllFrom(D.getDeclSpec().getAttributePool()); // Now actually move the attributes over. takeDeclAttributes(attrs, D.getDeclSpec().getAttributes().getList()); takeDeclAttributes(attrs, D.getAttributes()); for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) takeDeclAttributes(attrs, const_cast(D.getTypeObject(i).getAttrs())); } /// objc-type-name: /// '(' objc-type-qualifiers[opt] type-name ')' /// '(' objc-type-qualifiers[opt] ')' /// ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, Declarator::TheContext context, ParsedAttributes *paramAttrs) { assert(context == Declarator::ObjCParameterContext || context == Declarator::ObjCResultContext); assert((paramAttrs != nullptr) == (context == Declarator::ObjCParameterContext)); assert(Tok.is(tok::l_paren) && "expected ("); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); SourceLocation TypeStartLoc = Tok.getLocation(); ObjCDeclContextSwitch ObjCDC(*this); // Parse type qualifiers, in, inout, etc. ParseObjCTypeQualifierList(DS, context); ParsedType Ty; if (isTypeSpecifierQualifier() || isObjCInstancetype()) { // Parse an abstract declarator. DeclSpec declSpec(AttrFactory); declSpec.setObjCQualifiers(&DS); DeclSpecContext dsContext = DSC_normal; if (context == Declarator::ObjCResultContext) dsContext = DSC_objc_method_result; ParseSpecifierQualifierList(declSpec, AS_none, dsContext); Declarator declarator(declSpec, context); ParseDeclarator(declarator); // If that's not invalid, extract a type. if (!declarator.isInvalidType()) { // Map a nullability specifier to a context-sensitive keyword attribute. bool addedToDeclSpec = false; if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability) addContextSensitiveTypeNullability(*this, declarator, DS.getNullability(), DS.getNullabilityLoc(), addedToDeclSpec); TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator); if (!type.isInvalid()) Ty = type.get(); // If we're parsing a parameter, steal all the decl attributes // and add them to the decl spec. if (context == Declarator::ObjCParameterContext) takeDeclAttributes(*paramAttrs, declarator); } } if (Tok.is(tok::r_paren)) T.consumeClose(); else if (Tok.getLocation() == TypeStartLoc) { // If we didn't eat any tokens, then this isn't a type. Diag(Tok, diag::err_expected_type); SkipUntil(tok::r_paren, StopAtSemi); } else { // Otherwise, we found *something*, but didn't get a ')' in the right // place. Emit an error then return what we have as the type. T.consumeClose(); } return Ty; } /// objc-method-decl: /// objc-selector /// objc-keyword-selector objc-parmlist[opt] /// objc-type-name objc-selector /// objc-type-name objc-keyword-selector objc-parmlist[opt] /// /// objc-keyword-selector: /// objc-keyword-decl /// objc-keyword-selector objc-keyword-decl /// /// objc-keyword-decl: /// objc-selector ':' objc-type-name objc-keyword-attributes[opt] identifier /// objc-selector ':' objc-keyword-attributes[opt] identifier /// ':' objc-type-name objc-keyword-attributes[opt] identifier /// ':' objc-keyword-attributes[opt] identifier /// /// objc-parmlist: /// objc-parms objc-ellipsis[opt] /// /// objc-parms: /// objc-parms , parameter-declaration /// /// objc-ellipsis: /// , ... /// /// objc-keyword-attributes: [OBJC2] /// __attribute__((unused)) /// Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, tok::TokenKind mType, tok::ObjCKeywordKind MethodImplKind, bool MethodDefinition) { ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent); if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus, /*ReturnType=*/nullptr); cutOffParsing(); return nullptr; } // Parse the return type if present. ParsedType ReturnType; ObjCDeclSpec DSRet; if (Tok.is(tok::l_paren)) ReturnType = ParseObjCTypeName(DSRet, Declarator::ObjCResultContext, nullptr); // If attributes exist before the method, parse them. ParsedAttributes methodAttrs(AttrFactory); if (getLangOpts().ObjC2) MaybeParseGNUAttributes(methodAttrs); if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus, ReturnType); cutOffParsing(); return nullptr; } // Now parse the selector. SourceLocation selLoc; IdentifierInfo *SelIdent = ParseObjCSelectorPiece(selLoc); // An unnamed colon is valid. if (!SelIdent && Tok.isNot(tok::colon)) { // missing selector name. Diag(Tok, diag::err_expected_selector_for_method) << SourceRange(mLoc, Tok.getLocation()); // Skip until we get a ; or @. SkipUntil(tok::at, StopAtSemi | StopBeforeMatch); return nullptr; } SmallVector CParamInfo; if (Tok.isNot(tok::colon)) { // If attributes exist after the method, parse them. if (getLangOpts().ObjC2) MaybeParseGNUAttributes(methodAttrs); Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent); Decl *Result = Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType, selLoc, Sel, nullptr, CParamInfo.data(), CParamInfo.size(), methodAttrs.getList(), MethodImplKind, false, MethodDefinition); PD.complete(Result); return Result; } SmallVector KeyIdents; SmallVector KeyLocs; SmallVector ArgInfos; ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | Scope::FunctionDeclarationScope | Scope::DeclScope); AttributePool allParamAttrs(AttrFactory); while (1) { ParsedAttributes paramAttrs(AttrFactory); Sema::ObjCArgInfo ArgInfo; // Each iteration parses a single keyword argument. if (ExpectAndConsume(tok::colon)) break; ArgInfo.Type = nullptr; if (Tok.is(tok::l_paren)) // Parse the argument type if present. ArgInfo.Type = ParseObjCTypeName(ArgInfo.DeclSpec, Declarator::ObjCParameterContext, ¶mAttrs); // If attributes exist before the argument name, parse them. // Regardless, collect all the attributes we've parsed so far. ArgInfo.ArgAttrs = nullptr; if (getLangOpts().ObjC2) { MaybeParseGNUAttributes(paramAttrs); ArgInfo.ArgAttrs = paramAttrs.getList(); } // Code completion for the next piece of the selector. if (Tok.is(tok::code_completion)) { KeyIdents.push_back(SelIdent); Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(), mType == tok::minus, /*AtParameterName=*/true, ReturnType, KeyIdents); cutOffParsing(); return nullptr; } if (expectIdentifier()) break; // missing argument name. ArgInfo.Name = Tok.getIdentifierInfo(); ArgInfo.NameLoc = Tok.getLocation(); ConsumeToken(); // Eat the identifier. ArgInfos.push_back(ArgInfo); KeyIdents.push_back(SelIdent); KeyLocs.push_back(selLoc); // Make sure the attributes persist. allParamAttrs.takeAllFrom(paramAttrs.getPool()); // Code completion for the next piece of the selector. if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(), mType == tok::minus, /*AtParameterName=*/false, ReturnType, KeyIdents); cutOffParsing(); return nullptr; } // Check for another keyword selector. SelIdent = ParseObjCSelectorPiece(selLoc); if (!SelIdent && Tok.isNot(tok::colon)) break; if (!SelIdent) { SourceLocation ColonLoc = Tok.getLocation(); if (PP.getLocForEndOfToken(ArgInfo.NameLoc) == ColonLoc) { Diag(ArgInfo.NameLoc, diag::warn_missing_selector_name) << ArgInfo.Name; Diag(ArgInfo.NameLoc, diag::note_missing_selector_name) << ArgInfo.Name; Diag(ColonLoc, diag::note_force_empty_selector_name) << ArgInfo.Name; } } // We have a selector or a colon, continue parsing. } bool isVariadic = false; bool cStyleParamWarned = false; // Parse the (optional) parameter list. while (Tok.is(tok::comma)) { ConsumeToken(); if (Tok.is(tok::ellipsis)) { isVariadic = true; ConsumeToken(); break; } if (!cStyleParamWarned) { Diag(Tok, diag::warn_cstyle_param); cStyleParamWarned = true; } DeclSpec DS(AttrFactory); ParseDeclarationSpecifiers(DS); // Parse the declarator. Declarator ParmDecl(DS, Declarator::PrototypeContext); ParseDeclarator(ParmDecl); IdentifierInfo *ParmII = ParmDecl.getIdentifier(); Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDecl); CParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, ParmDecl.getIdentifierLoc(), Param, nullptr)); } // FIXME: Add support for optional parameter list... // If attributes exist after the method, parse them. if (getLangOpts().ObjC2) MaybeParseGNUAttributes(methodAttrs); if (KeyIdents.size() == 0) return nullptr; Selector Sel = PP.getSelectorTable().getSelector(KeyIdents.size(), &KeyIdents[0]); Decl *Result = Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType, KeyLocs, Sel, &ArgInfos[0], CParamInfo.data(), CParamInfo.size(), methodAttrs.getList(), MethodImplKind, isVariadic, MethodDefinition); PD.complete(Result); return Result; } /// objc-protocol-refs: /// '<' identifier-list '>' /// bool Parser:: ParseObjCProtocolReferences(SmallVectorImpl &Protocols, SmallVectorImpl &ProtocolLocs, bool WarnOnDeclarations, bool ForObjCContainer, SourceLocation &LAngleLoc, SourceLocation &EndLoc, bool consumeLastToken) { assert(Tok.is(tok::less) && "expected <"); LAngleLoc = ConsumeToken(); // the "<" SmallVector ProtocolIdents; while (1) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCProtocolReferences(ProtocolIdents); cutOffParsing(); return true; } if (expectIdentifier()) { SkipUntil(tok::greater, StopAtSemi); return true; } ProtocolIdents.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation())); ProtocolLocs.push_back(Tok.getLocation()); ConsumeToken(); if (!TryConsumeToken(tok::comma)) break; } // Consume the '>'. if (ParseGreaterThanInTemplateList(EndLoc, consumeLastToken, /*ObjCGenericList=*/false)) return true; // Convert the list of protocols identifiers into a list of protocol decls. Actions.FindProtocolDeclaration(WarnOnDeclarations, ForObjCContainer, ProtocolIdents, Protocols); return false; } TypeResult Parser::parseObjCProtocolQualifierType(SourceLocation &rAngleLoc) { assert(Tok.is(tok::less) && "Protocol qualifiers start with '<'"); assert(getLangOpts().ObjC1 && "Protocol qualifiers only exist in Objective-C"); SourceLocation lAngleLoc; SmallVector protocols; SmallVector protocolLocs; (void)ParseObjCProtocolReferences(protocols, protocolLocs, false, false, lAngleLoc, rAngleLoc, /*consumeLastToken=*/true); TypeResult result = Actions.actOnObjCProtocolQualifierType(lAngleLoc, protocols, protocolLocs, rAngleLoc); if (result.isUsable()) { Diag(lAngleLoc, diag::warn_objc_protocol_qualifier_missing_id) << FixItHint::CreateInsertion(lAngleLoc, "id") << SourceRange(lAngleLoc, rAngleLoc); } return result; } /// Parse Objective-C type arguments or protocol qualifiers. /// /// objc-type-arguments: /// '<' type-name '...'[opt] (',' type-name '...'[opt])* '>' /// void Parser::parseObjCTypeArgsOrProtocolQualifiers( ParsedType baseType, SourceLocation &typeArgsLAngleLoc, SmallVectorImpl &typeArgs, SourceLocation &typeArgsRAngleLoc, SourceLocation &protocolLAngleLoc, SmallVectorImpl &protocols, SmallVectorImpl &protocolLocs, SourceLocation &protocolRAngleLoc, bool consumeLastToken, bool warnOnIncompleteProtocols) { assert(Tok.is(tok::less) && "Not at the start of type args or protocols"); SourceLocation lAngleLoc = ConsumeToken(); // Whether all of the elements we've parsed thus far are single // identifiers, which might be types or might be protocols. bool allSingleIdentifiers = true; SmallVector identifiers; SmallVectorImpl &identifierLocs = protocolLocs; // Parse a list of comma-separated identifiers, bailing out if we // see something different. do { // Parse a single identifier. if (Tok.is(tok::identifier) && (NextToken().is(tok::comma) || NextToken().is(tok::greater) || NextToken().is(tok::greatergreater))) { identifiers.push_back(Tok.getIdentifierInfo()); identifierLocs.push_back(ConsumeToken()); continue; } if (Tok.is(tok::code_completion)) { // FIXME: Also include types here. SmallVector identifierLocPairs; for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { identifierLocPairs.push_back(IdentifierLocPair(identifiers[i], identifierLocs[i])); } QualType BaseT = Actions.GetTypeFromParser(baseType); if (!BaseT.isNull() && BaseT->acceptsObjCTypeParams()) { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Type); } else { Actions.CodeCompleteObjCProtocolReferences(identifierLocPairs); } cutOffParsing(); return; } allSingleIdentifiers = false; break; } while (TryConsumeToken(tok::comma)); // If we parsed an identifier list, semantic analysis sorts out // whether it refers to protocols or to type arguments. if (allSingleIdentifiers) { // Parse the closing '>'. SourceLocation rAngleLoc; (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken, /*ObjCGenericList=*/true); // Let Sema figure out what we parsed. Actions.actOnObjCTypeArgsOrProtocolQualifiers(getCurScope(), baseType, lAngleLoc, identifiers, identifierLocs, rAngleLoc, typeArgsLAngleLoc, typeArgs, typeArgsRAngleLoc, protocolLAngleLoc, protocols, protocolRAngleLoc, warnOnIncompleteProtocols); return; } // We parsed an identifier list but stumbled into non single identifiers, this // means we might (a) check that what we already parsed is a legitimate type // (not a protocol or unknown type) and (b) parse the remaining ones, which // must all be type args. // Convert the identifiers into type arguments. bool invalid = false; IdentifierInfo *foundProtocolId = nullptr, *foundValidTypeId = nullptr; SourceLocation foundProtocolSrcLoc, foundValidTypeSrcLoc; SmallVector unknownTypeArgs; SmallVector unknownTypeArgsLoc; for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { ParsedType typeArg = Actions.getTypeName(*identifiers[i], identifierLocs[i], getCurScope()); if (typeArg) { DeclSpec DS(AttrFactory); const char *prevSpec = nullptr; unsigned diagID; DS.SetTypeSpecType(TST_typename, identifierLocs[i], prevSpec, diagID, typeArg, Actions.getASTContext().getPrintingPolicy()); // Form a declarator to turn this into a type. Declarator D(DS, Declarator::TypeNameContext); TypeResult fullTypeArg = Actions.ActOnTypeName(getCurScope(), D); if (fullTypeArg.isUsable()) { typeArgs.push_back(fullTypeArg.get()); if (!foundValidTypeId) { foundValidTypeId = identifiers[i]; foundValidTypeSrcLoc = identifierLocs[i]; } } else { invalid = true; unknownTypeArgs.push_back(identifiers[i]); unknownTypeArgsLoc.push_back(identifierLocs[i]); } } else { invalid = true; if (!Actions.LookupProtocol(identifiers[i], identifierLocs[i])) { unknownTypeArgs.push_back(identifiers[i]); unknownTypeArgsLoc.push_back(identifierLocs[i]); } else if (!foundProtocolId) { foundProtocolId = identifiers[i]; foundProtocolSrcLoc = identifierLocs[i]; } } } // Continue parsing type-names. do { Token CurTypeTok = Tok; TypeResult typeArg = ParseTypeName(); // Consume the '...' for a pack expansion. SourceLocation ellipsisLoc; TryConsumeToken(tok::ellipsis, ellipsisLoc); if (typeArg.isUsable() && ellipsisLoc.isValid()) { typeArg = Actions.ActOnPackExpansion(typeArg.get(), ellipsisLoc); } if (typeArg.isUsable()) { typeArgs.push_back(typeArg.get()); if (!foundValidTypeId) { foundValidTypeId = CurTypeTok.getIdentifierInfo(); foundValidTypeSrcLoc = CurTypeTok.getLocation(); } } else { invalid = true; } } while (TryConsumeToken(tok::comma)); // Diagnose the mix between type args and protocols. if (foundProtocolId && foundValidTypeId) Actions.DiagnoseTypeArgsAndProtocols(foundProtocolId, foundProtocolSrcLoc, foundValidTypeId, foundValidTypeSrcLoc); // Diagnose unknown arg types. ParsedType T; if (unknownTypeArgs.size()) for (unsigned i = 0, e = unknownTypeArgsLoc.size(); i < e; ++i) Actions.DiagnoseUnknownTypeName(unknownTypeArgs[i], unknownTypeArgsLoc[i], getCurScope(), nullptr, T); // Parse the closing '>'. SourceLocation rAngleLoc; (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken, /*ObjCGenericList=*/true); if (invalid) { typeArgs.clear(); return; } // Record left/right angle locations. typeArgsLAngleLoc = lAngleLoc; typeArgsRAngleLoc = rAngleLoc; } void Parser::parseObjCTypeArgsAndProtocolQualifiers( ParsedType baseType, SourceLocation &typeArgsLAngleLoc, SmallVectorImpl &typeArgs, SourceLocation &typeArgsRAngleLoc, SourceLocation &protocolLAngleLoc, SmallVectorImpl &protocols, SmallVectorImpl &protocolLocs, SourceLocation &protocolRAngleLoc, bool consumeLastToken) { assert(Tok.is(tok::less)); // Parse the first angle-bracket-delimited clause. parseObjCTypeArgsOrProtocolQualifiers(baseType, typeArgsLAngleLoc, typeArgs, typeArgsRAngleLoc, protocolLAngleLoc, protocols, protocolLocs, protocolRAngleLoc, consumeLastToken, /*warnOnIncompleteProtocols=*/false); if (Tok.is(tok::eof)) // Nothing else to do here... return; // An Objective-C object pointer followed by type arguments // can then be followed again by a set of protocol references, e.g., // \c NSArray if ((consumeLastToken && Tok.is(tok::less)) || (!consumeLastToken && NextToken().is(tok::less))) { // If we aren't consuming the last token, the prior '>' is still hanging // there. Consume it before we parse the protocol qualifiers. if (!consumeLastToken) ConsumeToken(); if (!protocols.empty()) { SkipUntilFlags skipFlags = SkipUntilFlags(); if (!consumeLastToken) skipFlags = skipFlags | StopBeforeMatch; Diag(Tok, diag::err_objc_type_args_after_protocols) << SourceRange(protocolLAngleLoc, protocolRAngleLoc); SkipUntil(tok::greater, tok::greatergreater, skipFlags); } else { ParseObjCProtocolReferences(protocols, protocolLocs, /*WarnOnDeclarations=*/false, /*ForObjCContainer=*/false, protocolLAngleLoc, protocolRAngleLoc, consumeLastToken); } } } TypeResult Parser::parseObjCTypeArgsAndProtocolQualifiers( SourceLocation loc, ParsedType type, bool consumeLastToken, SourceLocation &endLoc) { assert(Tok.is(tok::less)); SourceLocation typeArgsLAngleLoc; SmallVector typeArgs; SourceLocation typeArgsRAngleLoc; SourceLocation protocolLAngleLoc; SmallVector protocols; SmallVector protocolLocs; SourceLocation protocolRAngleLoc; // Parse type arguments and protocol qualifiers. parseObjCTypeArgsAndProtocolQualifiers(type, typeArgsLAngleLoc, typeArgs, typeArgsRAngleLoc, protocolLAngleLoc, protocols, protocolLocs, protocolRAngleLoc, consumeLastToken); if (Tok.is(tok::eof)) return true; // Invalid type result. // Compute the location of the last token. if (consumeLastToken) endLoc = PrevTokLocation; else endLoc = Tok.getLocation(); return Actions.actOnObjCTypeArgsAndProtocolQualifiers( getCurScope(), loc, type, typeArgsLAngleLoc, typeArgs, typeArgsRAngleLoc, protocolLAngleLoc, protocols, protocolLocs, protocolRAngleLoc); } void Parser::HelperActionsForIvarDeclarations(Decl *interfaceDecl, SourceLocation atLoc, BalancedDelimiterTracker &T, SmallVectorImpl &AllIvarDecls, bool RBraceMissing) { if (!RBraceMissing) T.consumeClose(); Actions.ActOnObjCContainerStartDefinition(interfaceDecl); Actions.ActOnLastBitfield(T.getCloseLocation(), AllIvarDecls); Actions.ActOnObjCContainerFinishDefinition(); // Call ActOnFields() even if we don't have any decls. This is useful // for code rewriting tools that need to be aware of the empty list. Actions.ActOnFields(getCurScope(), atLoc, interfaceDecl, AllIvarDecls, T.getOpenLocation(), T.getCloseLocation(), nullptr); } /// objc-class-instance-variables: /// '{' objc-instance-variable-decl-list[opt] '}' /// /// objc-instance-variable-decl-list: /// objc-visibility-spec /// objc-instance-variable-decl ';' /// ';' /// objc-instance-variable-decl-list objc-visibility-spec /// objc-instance-variable-decl-list objc-instance-variable-decl ';' /// objc-instance-variable-decl-list ';' /// /// objc-visibility-spec: /// @private /// @protected /// @public /// @package [OBJC2] /// /// objc-instance-variable-decl: /// struct-declaration /// void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl, tok::ObjCKeywordKind visibility, SourceLocation atLoc) { assert(Tok.is(tok::l_brace) && "expected {"); SmallVector AllIvarDecls; ParseScope ClassScope(this, Scope::DeclScope|Scope::ClassScope); ObjCDeclContextSwitch ObjCDC(*this); BalancedDelimiterTracker T(*this, tok::l_brace); T.consumeOpen(); // While we still have something to read, read the instance variables. while (Tok.isNot(tok::r_brace) && !isEofOrEom()) { // Each iteration of this loop reads one objc-instance-variable-decl. // Check for extraneous top-level semicolon. if (Tok.is(tok::semi)) { ConsumeExtraSemi(InstanceVariableList); continue; } // Set the default visibility to private. if (TryConsumeToken(tok::at)) { // parse objc-visibility-spec if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCAtVisibility(getCurScope()); return cutOffParsing(); } switch (Tok.getObjCKeywordID()) { case tok::objc_private: case tok::objc_public: case tok::objc_protected: case tok::objc_package: visibility = Tok.getObjCKeywordID(); ConsumeToken(); continue; case tok::objc_end: Diag(Tok, diag::err_objc_unexpected_atend); Tok.setLocation(Tok.getLocation().getLocWithOffset(-1)); Tok.setKind(tok::at); Tok.setLength(1); PP.EnterToken(Tok); HelperActionsForIvarDeclarations(interfaceDecl, atLoc, T, AllIvarDecls, true); return; default: Diag(Tok, diag::err_objc_illegal_visibility_spec); continue; } } if (Tok.is(tok::code_completion)) { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_ObjCInstanceVariableList); return cutOffParsing(); } auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) { Actions.ActOnObjCContainerStartDefinition(interfaceDecl); // Install the declarator into the interface decl. FD.D.setObjCIvar(true); Decl *Field = Actions.ActOnIvar( getCurScope(), FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D, FD.BitfieldSize, visibility); Actions.ActOnObjCContainerFinishDefinition(); if (Field) AllIvarDecls.push_back(Field); FD.complete(Field); }; // Parse all the comma separated declarators. ParsingDeclSpec DS(*this); ParseStructDeclaration(DS, ObjCIvarCallback); if (Tok.is(tok::semi)) { ConsumeToken(); } else { Diag(Tok, diag::err_expected_semi_decl_list); // Skip to end of block or statement SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); } } HelperActionsForIvarDeclarations(interfaceDecl, atLoc, T, AllIvarDecls, false); } /// objc-protocol-declaration: /// objc-protocol-definition /// objc-protocol-forward-reference /// /// objc-protocol-definition: /// \@protocol identifier /// objc-protocol-refs[opt] /// objc-interface-decl-list /// \@end /// /// objc-protocol-forward-reference: /// \@protocol identifier-list ';' /// /// "\@protocol identifier ;" should be resolved as "\@protocol /// identifier-list ;": objc-interface-decl-list may not start with a /// semicolon in the first alternative if objc-protocol-refs are omitted. Parser::DeclGroupPtrTy Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc, ParsedAttributes &attrs) { assert(Tok.isObjCAtKeyword(tok::objc_protocol) && "ParseObjCAtProtocolDeclaration(): Expected @protocol"); ConsumeToken(); // the "protocol" identifier if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCProtocolDecl(getCurScope()); cutOffParsing(); return nullptr; } MaybeSkipAttributes(tok::objc_protocol); if (expectIdentifier()) return nullptr; // missing protocol name. // Save the protocol name, then consume it. IdentifierInfo *protocolName = Tok.getIdentifierInfo(); SourceLocation nameLoc = ConsumeToken(); if (TryConsumeToken(tok::semi)) { // forward declaration of one protocol. IdentifierLocPair ProtoInfo(protocolName, nameLoc); return Actions.ActOnForwardProtocolDeclaration(AtLoc, ProtoInfo, attrs.getList()); } CheckNestedObjCContexts(AtLoc); if (Tok.is(tok::comma)) { // list of forward declarations. SmallVector ProtocolRefs; ProtocolRefs.push_back(std::make_pair(protocolName, nameLoc)); // Parse the list of forward declarations. while (1) { ConsumeToken(); // the ',' if (expectIdentifier()) { SkipUntil(tok::semi); return nullptr; } ProtocolRefs.push_back(IdentifierLocPair(Tok.getIdentifierInfo(), Tok.getLocation())); ConsumeToken(); // the identifier if (Tok.isNot(tok::comma)) break; } // Consume the ';'. if (ExpectAndConsume(tok::semi, diag::err_expected_after, "@protocol")) return nullptr; return Actions.ActOnForwardProtocolDeclaration(AtLoc, ProtocolRefs, attrs.getList()); } // Last, and definitely not least, parse a protocol declaration. SourceLocation LAngleLoc, EndProtoLoc; SmallVector ProtocolRefs; SmallVector ProtocolLocs; if (Tok.is(tok::less) && ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, false, true, LAngleLoc, EndProtoLoc, /*consumeLastToken=*/true)) return nullptr; Decl *ProtoType = Actions.ActOnStartProtocolInterface(AtLoc, protocolName, nameLoc, ProtocolRefs.data(), ProtocolRefs.size(), ProtocolLocs.data(), EndProtoLoc, attrs.getList()); ParseObjCInterfaceDeclList(tok::objc_protocol, ProtoType); return Actions.ConvertDeclToDeclGroup(ProtoType); } /// objc-implementation: /// objc-class-implementation-prologue /// objc-category-implementation-prologue /// /// objc-class-implementation-prologue: /// @implementation identifier objc-superclass[opt] /// objc-class-instance-variables[opt] /// /// objc-category-implementation-prologue: /// @implementation identifier ( identifier ) Parser::DeclGroupPtrTy Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) { assert(Tok.isObjCAtKeyword(tok::objc_implementation) && "ParseObjCAtImplementationDeclaration(): Expected @implementation"); CheckNestedObjCContexts(AtLoc); ConsumeToken(); // the "implementation" identifier // Code completion after '@implementation'. if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCImplementationDecl(getCurScope()); cutOffParsing(); return nullptr; } MaybeSkipAttributes(tok::objc_implementation); if (expectIdentifier()) return nullptr; // missing class or category name. // We have a class or category name - consume it. IdentifierInfo *nameId = Tok.getIdentifierInfo(); SourceLocation nameLoc = ConsumeToken(); // consume class or category name Decl *ObjCImpDecl = nullptr; // Neither a type parameter list nor a list of protocol references is // permitted here. Parse and diagnose them. if (Tok.is(tok::less)) { SourceLocation lAngleLoc, rAngleLoc; SmallVector protocolIdents; SourceLocation diagLoc = Tok.getLocation(); ObjCTypeParamListScope typeParamScope(Actions, getCurScope()); if (parseObjCTypeParamListOrProtocolRefs(typeParamScope, lAngleLoc, protocolIdents, rAngleLoc)) { Diag(diagLoc, diag::err_objc_parameterized_implementation) << SourceRange(diagLoc, PrevTokLocation); } else if (lAngleLoc.isValid()) { Diag(lAngleLoc, diag::err_unexpected_protocol_qualifier) << FixItHint::CreateRemoval(SourceRange(lAngleLoc, rAngleLoc)); } } if (Tok.is(tok::l_paren)) { // we have a category implementation. ConsumeParen(); SourceLocation categoryLoc, rparenLoc; IdentifierInfo *categoryId = nullptr; if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCImplementationCategory(getCurScope(), nameId, nameLoc); cutOffParsing(); return nullptr; } if (Tok.is(tok::identifier)) { categoryId = Tok.getIdentifierInfo(); categoryLoc = ConsumeToken(); } else { Diag(Tok, diag::err_expected) << tok::identifier; // missing category name. return nullptr; } if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren); // don't stop at ';' return nullptr; } rparenLoc = ConsumeParen(); if (Tok.is(tok::less)) { // we have illegal '<' try to recover Diag(Tok, diag::err_unexpected_protocol_qualifier); SourceLocation protocolLAngleLoc, protocolRAngleLoc; SmallVector protocols; SmallVector protocolLocs; (void)ParseObjCProtocolReferences(protocols, protocolLocs, /*warnOnIncompleteProtocols=*/false, /*ForObjCContainer=*/false, protocolLAngleLoc, protocolRAngleLoc, /*consumeLastToken=*/true); } ObjCImpDecl = Actions.ActOnStartCategoryImplementation( AtLoc, nameId, nameLoc, categoryId, categoryLoc); } else { // We have a class implementation SourceLocation superClassLoc; IdentifierInfo *superClassId = nullptr; if (TryConsumeToken(tok::colon)) { // We have a super class if (expectIdentifier()) return nullptr; // missing super class name. superClassId = Tok.getIdentifierInfo(); superClassLoc = ConsumeToken(); // Consume super class name } ObjCImpDecl = Actions.ActOnStartClassImplementation( AtLoc, nameId, nameLoc, superClassId, superClassLoc); if (Tok.is(tok::l_brace)) // we have ivars ParseObjCClassInstanceVariables(ObjCImpDecl, tok::objc_private, AtLoc); else if (Tok.is(tok::less)) { // we have illegal '<' try to recover Diag(Tok, diag::err_unexpected_protocol_qualifier); SourceLocation protocolLAngleLoc, protocolRAngleLoc; SmallVector protocols; SmallVector protocolLocs; (void)ParseObjCProtocolReferences(protocols, protocolLocs, /*warnOnIncompleteProtocols=*/false, /*ForObjCContainer=*/false, protocolLAngleLoc, protocolRAngleLoc, /*consumeLastToken=*/true); } } assert(ObjCImpDecl); SmallVector DeclsInGroup; { ObjCImplParsingDataRAII ObjCImplParsing(*this, ObjCImpDecl); while (!ObjCImplParsing.isFinished() && !isEofOrEom()) { ParsedAttributesWithRange attrs(AttrFactory); MaybeParseCXX11Attributes(attrs); if (DeclGroupPtrTy DGP = ParseExternalDeclaration(attrs)) { DeclGroupRef DG = DGP.get(); DeclsInGroup.append(DG.begin(), DG.end()); } } } return Actions.ActOnFinishObjCImplementation(ObjCImpDecl, DeclsInGroup); } Parser::DeclGroupPtrTy Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) { assert(Tok.isObjCAtKeyword(tok::objc_end) && "ParseObjCAtEndDeclaration(): Expected @end"); ConsumeToken(); // the "end" identifier if (CurParsedObjCImpl) CurParsedObjCImpl->finish(atEnd); else // missing @implementation Diag(atEnd.getBegin(), diag::err_expected_objc_container); return nullptr; } Parser::ObjCImplParsingDataRAII::~ObjCImplParsingDataRAII() { if (!Finished) { finish(P.Tok.getLocation()); if (P.isEofOrEom()) { P.Diag(P.Tok, diag::err_objc_missing_end) << FixItHint::CreateInsertion(P.Tok.getLocation(), "\n@end\n"); P.Diag(Dcl->getLocStart(), diag::note_objc_container_start) << Sema::OCK_Implementation; } } P.CurParsedObjCImpl = nullptr; assert(LateParsedObjCMethods.empty()); } void Parser::ObjCImplParsingDataRAII::finish(SourceRange AtEnd) { assert(!Finished); P.Actions.DefaultSynthesizeProperties(P.getCurScope(), Dcl, AtEnd.getBegin()); for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i], true/*Methods*/); P.Actions.ActOnAtEnd(P.getCurScope(), AtEnd); if (HasCFunction) for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i], false/*c-functions*/); /// \brief Clear and free the cached objc methods. for (LateParsedObjCMethodContainer::iterator I = LateParsedObjCMethods.begin(), E = LateParsedObjCMethods.end(); I != E; ++I) delete *I; LateParsedObjCMethods.clear(); Finished = true; } /// compatibility-alias-decl: /// @compatibility_alias alias-name class-name ';' /// Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_compatibility_alias) && "ParseObjCAtAliasDeclaration(): Expected @compatibility_alias"); ConsumeToken(); // consume compatibility_alias if (expectIdentifier()) return nullptr; IdentifierInfo *aliasId = Tok.getIdentifierInfo(); SourceLocation aliasLoc = ConsumeToken(); // consume alias-name if (expectIdentifier()) return nullptr; IdentifierInfo *classId = Tok.getIdentifierInfo(); SourceLocation classLoc = ConsumeToken(); // consume class-name; ExpectAndConsume(tok::semi, diag::err_expected_after, "@compatibility_alias"); return Actions.ActOnCompatibilityAlias(atLoc, aliasId, aliasLoc, classId, classLoc); } /// property-synthesis: /// @synthesize property-ivar-list ';' /// /// property-ivar-list: /// property-ivar /// property-ivar-list ',' property-ivar /// /// property-ivar: /// identifier /// identifier '=' identifier /// Decl *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_synthesize) && "ParseObjCPropertySynthesize(): Expected '@synthesize'"); ConsumeToken(); // consume synthesize while (true) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCPropertyDefinition(getCurScope()); cutOffParsing(); return nullptr; } if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_synthesized_property_name); SkipUntil(tok::semi); return nullptr; } IdentifierInfo *propertyIvar = nullptr; IdentifierInfo *propertyId = Tok.getIdentifierInfo(); SourceLocation propertyLoc = ConsumeToken(); // consume property name SourceLocation propertyIvarLoc; if (TryConsumeToken(tok::equal)) { // property '=' ivar-name if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCPropertySynthesizeIvar(getCurScope(), propertyId); cutOffParsing(); return nullptr; } if (expectIdentifier()) break; propertyIvar = Tok.getIdentifierInfo(); propertyIvarLoc = ConsumeToken(); // consume ivar-name } Actions.ActOnPropertyImplDecl( getCurScope(), atLoc, propertyLoc, true, propertyId, propertyIvar, propertyIvarLoc, ObjCPropertyQueryKind::OBJC_PR_query_unknown); if (Tok.isNot(tok::comma)) break; ConsumeToken(); // consume ',' } ExpectAndConsume(tok::semi, diag::err_expected_after, "@synthesize"); return nullptr; } /// property-dynamic: /// @dynamic property-list /// /// property-list: /// identifier /// property-list ',' identifier /// Decl *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_dynamic) && "ParseObjCPropertyDynamic(): Expected '@dynamic'"); ConsumeToken(); // consume dynamic bool isClassProperty = false; if (Tok.is(tok::l_paren)) { ConsumeParen(); const IdentifierInfo *II = Tok.getIdentifierInfo(); if (!II) { Diag(Tok, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); } else { SourceLocation AttrName = ConsumeToken(); // consume attribute name if (II->isStr("class")) { isClassProperty = true; if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren, StopAtSemi); } else ConsumeParen(); } else { Diag(AttrName, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); } } } while (true) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCPropertyDefinition(getCurScope()); cutOffParsing(); return nullptr; } if (expectIdentifier()) { SkipUntil(tok::semi); return nullptr; } IdentifierInfo *propertyId = Tok.getIdentifierInfo(); SourceLocation propertyLoc = ConsumeToken(); // consume property name Actions.ActOnPropertyImplDecl( getCurScope(), atLoc, propertyLoc, false, propertyId, nullptr, SourceLocation(), isClassProperty ? ObjCPropertyQueryKind::OBJC_PR_query_class : ObjCPropertyQueryKind::OBJC_PR_query_unknown); if (Tok.isNot(tok::comma)) break; ConsumeToken(); // consume ',' } ExpectAndConsume(tok::semi, diag::err_expected_after, "@dynamic"); return nullptr; } /// objc-throw-statement: /// throw expression[opt]; /// StmtResult Parser::ParseObjCThrowStmt(SourceLocation atLoc) { ExprResult Res; ConsumeToken(); // consume throw if (Tok.isNot(tok::semi)) { Res = ParseExpression(); if (Res.isInvalid()) { SkipUntil(tok::semi); return StmtError(); } } // consume ';' ExpectAndConsume(tok::semi, diag::err_expected_after, "@throw"); return Actions.ActOnObjCAtThrowStmt(atLoc, Res.get(), getCurScope()); } /// objc-synchronized-statement: /// @synchronized '(' expression ')' compound-statement /// StmtResult Parser::ParseObjCSynchronizedStmt(SourceLocation atLoc) { ConsumeToken(); // consume synchronized if (Tok.isNot(tok::l_paren)) { Diag(Tok, diag::err_expected_lparen_after) << "@synchronized"; return StmtError(); } // The operand is surrounded with parentheses. ConsumeParen(); // '(' ExprResult operand(ParseExpression()); if (Tok.is(tok::r_paren)) { ConsumeParen(); // ')' } else { if (!operand.isInvalid()) Diag(Tok, diag::err_expected) << tok::r_paren; // Skip forward until we see a left brace, but don't consume it. SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch); } // Require a compound statement. if (Tok.isNot(tok::l_brace)) { if (!operand.isInvalid()) Diag(Tok, diag::err_expected) << tok::l_brace; return StmtError(); } // Check the @synchronized operand now. if (!operand.isInvalid()) operand = Actions.ActOnObjCAtSynchronizedOperand(atLoc, operand.get()); // Parse the compound statement within a new scope. ParseScope bodyScope(this, Scope::DeclScope); StmtResult body(ParseCompoundStatementBody()); bodyScope.Exit(); // If there was a semantic or parse error earlier with the // operand, fail now. if (operand.isInvalid()) return StmtError(); if (body.isInvalid()) body = Actions.ActOnNullStmt(Tok.getLocation()); return Actions.ActOnObjCAtSynchronizedStmt(atLoc, operand.get(), body.get()); } /// objc-try-catch-statement: /// @try compound-statement objc-catch-list[opt] /// @try compound-statement objc-catch-list[opt] @finally compound-statement /// /// objc-catch-list: /// @catch ( parameter-declaration ) compound-statement /// objc-catch-list @catch ( catch-parameter-declaration ) compound-statement /// catch-parameter-declaration: /// parameter-declaration /// '...' [OBJC2] /// StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { bool catch_or_finally_seen = false; ConsumeToken(); // consume try if (Tok.isNot(tok::l_brace)) { Diag(Tok, diag::err_expected) << tok::l_brace; return StmtError(); } StmtVector CatchStmts; StmtResult FinallyStmt; ParseScope TryScope(this, Scope::DeclScope); StmtResult TryBody(ParseCompoundStatementBody()); TryScope.Exit(); if (TryBody.isInvalid()) TryBody = Actions.ActOnNullStmt(Tok.getLocation()); while (Tok.is(tok::at)) { // At this point, we need to lookahead to determine if this @ is the start // of an @catch or @finally. We don't want to consume the @ token if this // is an @try or @encode or something else. Token AfterAt = GetLookAheadToken(1); if (!AfterAt.isObjCAtKeyword(tok::objc_catch) && !AfterAt.isObjCAtKeyword(tok::objc_finally)) break; SourceLocation AtCatchFinallyLoc = ConsumeToken(); if (Tok.isObjCAtKeyword(tok::objc_catch)) { Decl *FirstPart = nullptr; ConsumeToken(); // consume catch if (Tok.is(tok::l_paren)) { ConsumeParen(); ParseScope CatchScope(this, Scope::DeclScope|Scope::AtCatchScope); if (Tok.isNot(tok::ellipsis)) { DeclSpec DS(AttrFactory); ParseDeclarationSpecifiers(DS); Declarator ParmDecl(DS, Declarator::ObjCCatchContext); ParseDeclarator(ParmDecl); // Inform the actions module about the declarator, so it // gets added to the current scope. FirstPart = Actions.ActOnObjCExceptionDecl(getCurScope(), ParmDecl); } else ConsumeToken(); // consume '...' SourceLocation RParenLoc; if (Tok.is(tok::r_paren)) RParenLoc = ConsumeParen(); else // Skip over garbage, until we get to ')'. Eat the ')'. SkipUntil(tok::r_paren, StopAtSemi); StmtResult CatchBody(true); if (Tok.is(tok::l_brace)) CatchBody = ParseCompoundStatementBody(); else Diag(Tok, diag::err_expected) << tok::l_brace; if (CatchBody.isInvalid()) CatchBody = Actions.ActOnNullStmt(Tok.getLocation()); StmtResult Catch = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc, RParenLoc, FirstPart, CatchBody.get()); if (!Catch.isInvalid()) CatchStmts.push_back(Catch.get()); } else { Diag(AtCatchFinallyLoc, diag::err_expected_lparen_after) << "@catch clause"; return StmtError(); } catch_or_finally_seen = true; } else { assert(Tok.isObjCAtKeyword(tok::objc_finally) && "Lookahead confused?"); ConsumeToken(); // consume finally ParseScope FinallyScope(this, Scope::DeclScope); StmtResult FinallyBody(true); if (Tok.is(tok::l_brace)) FinallyBody = ParseCompoundStatementBody(); else Diag(Tok, diag::err_expected) << tok::l_brace; if (FinallyBody.isInvalid()) FinallyBody = Actions.ActOnNullStmt(Tok.getLocation()); FinallyStmt = Actions.ActOnObjCAtFinallyStmt(AtCatchFinallyLoc, FinallyBody.get()); catch_or_finally_seen = true; break; } } if (!catch_or_finally_seen) { Diag(atLoc, diag::err_missing_catch_finally); return StmtError(); } return Actions.ActOnObjCAtTryStmt(atLoc, TryBody.get(), CatchStmts, FinallyStmt.get()); } /// objc-autoreleasepool-statement: /// @autoreleasepool compound-statement /// StmtResult Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) { ConsumeToken(); // consume autoreleasepool if (Tok.isNot(tok::l_brace)) { Diag(Tok, diag::err_expected) << tok::l_brace; return StmtError(); } // Enter a scope to hold everything within the compound stmt. Compound // statements can always hold declarations. ParseScope BodyScope(this, Scope::DeclScope); StmtResult AutoreleasePoolBody(ParseCompoundStatementBody()); BodyScope.Exit(); if (AutoreleasePoolBody.isInvalid()) AutoreleasePoolBody = Actions.ActOnNullStmt(Tok.getLocation()); return Actions.ActOnObjCAutoreleasePoolStmt(atLoc, AutoreleasePoolBody.get()); } /// StashAwayMethodOrFunctionBodyTokens - Consume the tokens and store them /// for later parsing. void Parser::StashAwayMethodOrFunctionBodyTokens(Decl *MDecl) { if (SkipFunctionBodies && (!MDecl || Actions.canSkipFunctionBody(MDecl)) && trySkippingFunctionBody()) { Actions.ActOnSkippedFunctionBody(MDecl); return; } LexedMethod* LM = new LexedMethod(this, MDecl); CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM); CachedTokens &Toks = LM->Toks; // Begin by storing the '{' or 'try' or ':' token. Toks.push_back(Tok); if (Tok.is(tok::kw_try)) { ConsumeToken(); if (Tok.is(tok::colon)) { Toks.push_back(Tok); ConsumeToken(); while (Tok.isNot(tok::l_brace)) { ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false); ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false); } } Toks.push_back(Tok); // also store '{' } else if (Tok.is(tok::colon)) { ConsumeToken(); // FIXME: This is wrong, due to C++11 braced initialization. while (Tok.isNot(tok::l_brace)) { ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false); ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false); } Toks.push_back(Tok); // also store '{' } ConsumeBrace(); // Consume everything up to (and including) the matching right brace. ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); while (Tok.is(tok::kw_catch)) { ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false); ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); } } /// objc-method-def: objc-method-proto ';'[opt] '{' body '}' /// Decl *Parser::ParseObjCMethodDefinition() { Decl *MDecl = ParseObjCMethodPrototype(); PrettyDeclStackTraceEntry CrashInfo(Actions, MDecl, Tok.getLocation(), "parsing Objective-C method"); // parse optional ';' if (Tok.is(tok::semi)) { if (CurParsedObjCImpl) { Diag(Tok, diag::warn_semicolon_before_method_body) << FixItHint::CreateRemoval(Tok.getLocation()); } ConsumeToken(); } // We should have an opening brace now. if (Tok.isNot(tok::l_brace)) { Diag(Tok, diag::err_expected_method_body); // Skip over garbage, until we get to '{'. Don't eat the '{'. SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch); // If we didn't find the '{', bail out. if (Tok.isNot(tok::l_brace)) return nullptr; } if (!MDecl) { ConsumeBrace(); SkipUntil(tok::r_brace); return nullptr; } // Allow the rest of sema to find private method decl implementations. Actions.AddAnyMethodToGlobalPool(MDecl); assert (CurParsedObjCImpl && "ParseObjCMethodDefinition - Method out of @implementation"); // Consume the tokens and store them for later parsing. StashAwayMethodOrFunctionBodyTokens(MDecl); return MDecl; } StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCAtStatement(getCurScope()); cutOffParsing(); return StmtError(); } if (Tok.isObjCAtKeyword(tok::objc_try)) return ParseObjCTryStmt(AtLoc); if (Tok.isObjCAtKeyword(tok::objc_throw)) return ParseObjCThrowStmt(AtLoc); if (Tok.isObjCAtKeyword(tok::objc_synchronized)) return ParseObjCSynchronizedStmt(AtLoc); if (Tok.isObjCAtKeyword(tok::objc_autoreleasepool)) return ParseObjCAutoreleasePoolStmt(AtLoc); if (Tok.isObjCAtKeyword(tok::objc_import) && getLangOpts().DebuggerSupport) { SkipUntil(tok::semi); return Actions.ActOnNullStmt(Tok.getLocation()); } ExprStatementTokLoc = AtLoc; ExprResult Res(ParseExpressionWithLeadingAt(AtLoc)); if (Res.isInvalid()) { // If the expression is invalid, skip ahead to the next semicolon. Not // doing this opens us up to the possibility of infinite loops if // ParseExpression does not consume any tokens. SkipUntil(tok::semi); return StmtError(); } // Otherwise, eat the semicolon. ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); return Actions.ActOnExprStmt(Res); } ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { switch (Tok.getKind()) { case tok::code_completion: Actions.CodeCompleteObjCAtExpression(getCurScope()); cutOffParsing(); return ExprError(); case tok::minus: case tok::plus: { tok::TokenKind Kind = Tok.getKind(); SourceLocation OpLoc = ConsumeToken(); if (!Tok.is(tok::numeric_constant)) { const char *Symbol = nullptr; switch (Kind) { case tok::minus: Symbol = "-"; break; case tok::plus: Symbol = "+"; break; default: llvm_unreachable("missing unary operator case"); } Diag(Tok, diag::err_nsnumber_nonliteral_unary) << Symbol; return ExprError(); } ExprResult Lit(Actions.ActOnNumericConstant(Tok)); if (Lit.isInvalid()) { return Lit; } ConsumeToken(); // Consume the literal token. Lit = Actions.ActOnUnaryOp(getCurScope(), OpLoc, Kind, Lit.get()); if (Lit.isInvalid()) return Lit; return ParsePostfixExpressionSuffix( Actions.BuildObjCNumericLiteral(AtLoc, Lit.get())); } case tok::string_literal: // primary-expression: string-literal case tok::wide_string_literal: return ParsePostfixExpressionSuffix(ParseObjCStringLiteral(AtLoc)); case tok::char_constant: return ParsePostfixExpressionSuffix(ParseObjCCharacterLiteral(AtLoc)); case tok::numeric_constant: return ParsePostfixExpressionSuffix(ParseObjCNumericLiteral(AtLoc)); case tok::kw_true: // Objective-C++, etc. case tok::kw___objc_yes: // c/c++/objc/objc++ __objc_yes return ParsePostfixExpressionSuffix(ParseObjCBooleanLiteral(AtLoc, true)); case tok::kw_false: // Objective-C++, etc. case tok::kw___objc_no: // c/c++/objc/objc++ __objc_no return ParsePostfixExpressionSuffix(ParseObjCBooleanLiteral(AtLoc, false)); case tok::l_square: // Objective-C array literal return ParsePostfixExpressionSuffix(ParseObjCArrayLiteral(AtLoc)); case tok::l_brace: // Objective-C dictionary literal return ParsePostfixExpressionSuffix(ParseObjCDictionaryLiteral(AtLoc)); case tok::l_paren: // Objective-C boxed expression return ParsePostfixExpressionSuffix(ParseObjCBoxedExpr(AtLoc)); default: if (Tok.getIdentifierInfo() == nullptr) return ExprError(Diag(AtLoc, diag::err_unexpected_at)); switch (Tok.getIdentifierInfo()->getObjCKeywordID()) { case tok::objc_encode: return ParsePostfixExpressionSuffix(ParseObjCEncodeExpression(AtLoc)); case tok::objc_protocol: return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc)); case tok::objc_selector: return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc)); case tok::objc_available: return ParseAvailabilityCheckExpr(AtLoc); default: { const char *str = nullptr; // Only provide the @try/@finally/@autoreleasepool fixit when we're sure // that this is a proper statement where such directives could actually // occur. if (GetLookAheadToken(1).is(tok::l_brace) && ExprStatementTokLoc == AtLoc) { char ch = Tok.getIdentifierInfo()->getNameStart()[0]; str = ch == 't' ? "try" : (ch == 'f' ? "finally" : (ch == 'a' ? "autoreleasepool" : nullptr)); } if (str) { SourceLocation kwLoc = Tok.getLocation(); return ExprError(Diag(AtLoc, diag::err_unexpected_at) << FixItHint::CreateReplacement(kwLoc, str)); } else return ExprError(Diag(AtLoc, diag::err_unexpected_at)); } } } } /// \brief Parse the receiver of an Objective-C++ message send. /// /// This routine parses the receiver of a message send in /// Objective-C++ either as a type or as an expression. Note that this /// routine must not be called to parse a send to 'super', since it /// has no way to return such a result. /// /// \param IsExpr Whether the receiver was parsed as an expression. /// /// \param TypeOrExpr If the receiver was parsed as an expression (\c /// IsExpr is true), the parsed expression. If the receiver was parsed /// as a type (\c IsExpr is false), the parsed type. /// /// \returns True if an error occurred during parsing or semantic /// analysis, in which case the arguments do not have valid /// values. Otherwise, returns false for a successful parse. /// /// objc-receiver: [C++] /// 'super' [not parsed here] /// expression /// simple-type-specifier /// typename-specifier bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) { InMessageExpressionRAIIObject InMessage(*this, true); if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_typename, tok::annot_cxxscope)) TryAnnotateTypeOrScopeToken(); if (!Actions.isSimpleTypeSpecifier(Tok.getKind())) { // objc-receiver: // expression // Make sure any typos in the receiver are corrected or diagnosed, so that // proper recovery can happen. FIXME: Perhaps filter the corrected expr to // only the things that are valid ObjC receivers? ExprResult Receiver = Actions.CorrectDelayedTyposInExpr(ParseExpression()); if (Receiver.isInvalid()) return true; IsExpr = true; TypeOrExpr = Receiver.get(); return false; } // objc-receiver: // typename-specifier // simple-type-specifier // expression (that starts with one of the above) DeclSpec DS(AttrFactory); ParseCXXSimpleTypeSpecifier(DS); if (Tok.is(tok::l_paren)) { // If we see an opening parentheses at this point, we are // actually parsing an expression that starts with a // function-style cast, e.g., // // postfix-expression: // simple-type-specifier ( expression-list [opt] ) // typename-specifier ( expression-list [opt] ) // // Parse the remainder of this case, then the (optional) // postfix-expression suffix, followed by the (optional) // right-hand side of the binary expression. We have an // instance method. ExprResult Receiver = ParseCXXTypeConstructExpression(DS); if (!Receiver.isInvalid()) Receiver = ParsePostfixExpressionSuffix(Receiver.get()); if (!Receiver.isInvalid()) Receiver = ParseRHSOfBinaryExpression(Receiver.get(), prec::Comma); if (Receiver.isInvalid()) return true; IsExpr = true; TypeOrExpr = Receiver.get(); return false; } // We have a class message. Turn the simple-type-specifier or // typename-specifier we parsed into a type and parse the // remainder of the class message. Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); TypeResult Type = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); if (Type.isInvalid()) return true; IsExpr = false; TypeOrExpr = Type.get().getAsOpaquePtr(); return false; } /// \brief Determine whether the parser is currently referring to a an /// Objective-C message send, using a simplified heuristic to avoid overhead. /// /// This routine will only return true for a subset of valid message-send /// expressions. bool Parser::isSimpleObjCMessageExpression() { assert(Tok.is(tok::l_square) && getLangOpts().ObjC1 && "Incorrect start for isSimpleObjCMessageExpression"); return GetLookAheadToken(1).is(tok::identifier) && GetLookAheadToken(2).is(tok::identifier); } bool Parser::isStartOfObjCClassMessageMissingOpenBracket() { if (!getLangOpts().ObjC1 || !NextToken().is(tok::identifier) || InMessageExpression) return false; ParsedType Type; if (Tok.is(tok::annot_typename)) Type = getTypeAnnotation(Tok); else if (Tok.is(tok::identifier)) Type = Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope()); else return false; if (!Type.get().isNull() && Type.get()->isObjCObjectOrInterfaceType()) { const Token &AfterNext = GetLookAheadToken(2); if (AfterNext.isOneOf(tok::colon, tok::r_square)) { if (Tok.is(tok::identifier)) TryAnnotateTypeOrScopeToken(); return Tok.is(tok::annot_typename); } } return false; } /// objc-message-expr: /// '[' objc-receiver objc-message-args ']' /// /// objc-receiver: [C] /// 'super' /// expression /// class-name /// type-name /// ExprResult Parser::ParseObjCMessageExpression() { assert(Tok.is(tok::l_square) && "'[' expected"); SourceLocation LBracLoc = ConsumeBracket(); // consume '[' if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCMessageReceiver(getCurScope()); cutOffParsing(); return ExprError(); } InMessageExpressionRAIIObject InMessage(*this, true); if (getLangOpts().CPlusPlus) { // We completely separate the C and C++ cases because C++ requires // more complicated (read: slower) parsing. // Handle send to super. // FIXME: This doesn't benefit from the same typo-correction we // get in Objective-C. if (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_super && NextToken().isNot(tok::period) && getCurScope()->isInObjcMethodScope()) return ParseObjCMessageExpressionBody(LBracLoc, ConsumeToken(), nullptr, nullptr); // Parse the receiver, which is either a type or an expression. bool IsExpr; void *TypeOrExpr = nullptr; if (ParseObjCXXMessageReceiver(IsExpr, TypeOrExpr)) { SkipUntil(tok::r_square, StopAtSemi); return ExprError(); } if (IsExpr) return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), nullptr, static_cast(TypeOrExpr)); return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), ParsedType::getFromOpaquePtr(TypeOrExpr), nullptr); } if (Tok.is(tok::identifier)) { IdentifierInfo *Name = Tok.getIdentifierInfo(); SourceLocation NameLoc = Tok.getLocation(); ParsedType ReceiverType; switch (Actions.getObjCMessageKind(getCurScope(), Name, NameLoc, Name == Ident_super, NextToken().is(tok::period), ReceiverType)) { case Sema::ObjCSuperMessage: return ParseObjCMessageExpressionBody(LBracLoc, ConsumeToken(), nullptr, nullptr); case Sema::ObjCClassMessage: if (!ReceiverType) { SkipUntil(tok::r_square, StopAtSemi); return ExprError(); } ConsumeToken(); // the type name // Parse type arguments and protocol qualifiers. if (Tok.is(tok::less)) { SourceLocation NewEndLoc; TypeResult NewReceiverType = parseObjCTypeArgsAndProtocolQualifiers(NameLoc, ReceiverType, /*consumeLastToken=*/true, NewEndLoc); if (!NewReceiverType.isUsable()) { SkipUntil(tok::r_square, StopAtSemi); return ExprError(); } ReceiverType = NewReceiverType.get(); } return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), ReceiverType, nullptr); case Sema::ObjCInstanceMessage: // Fall through to parse an expression. break; } } // Otherwise, an arbitrary expression can be the receiver of a send. ExprResult Res = Actions.CorrectDelayedTyposInExpr(ParseExpression()); if (Res.isInvalid()) { SkipUntil(tok::r_square, StopAtSemi); return Res; } return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), nullptr, Res.get()); } /// \brief Parse the remainder of an Objective-C message following the /// '[' objc-receiver. /// /// This routine handles sends to super, class messages (sent to a /// class name), and instance messages (sent to an object), and the /// target is represented by \p SuperLoc, \p ReceiverType, or \p /// ReceiverExpr, respectively. Only one of these parameters may have /// a valid value. /// /// \param LBracLoc The location of the opening '['. /// /// \param SuperLoc If this is a send to 'super', the location of the /// 'super' keyword that indicates a send to the superclass. /// /// \param ReceiverType If this is a class message, the type of the /// class we are sending a message to. /// /// \param ReceiverExpr If this is an instance message, the expression /// used to compute the receiver object. /// /// objc-message-args: /// objc-selector /// objc-keywordarg-list /// /// objc-keywordarg-list: /// objc-keywordarg /// objc-keywordarg-list objc-keywordarg /// /// objc-keywordarg: /// selector-name[opt] ':' objc-keywordexpr /// /// objc-keywordexpr: /// nonempty-expr-list /// /// nonempty-expr-list: /// assignment-expression /// nonempty-expr-list , assignment-expression /// ExprResult Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc, SourceLocation SuperLoc, ParsedType ReceiverType, Expr *ReceiverExpr) { InMessageExpressionRAIIObject InMessage(*this, true); if (Tok.is(tok::code_completion)) { if (SuperLoc.isValid()) Actions.CodeCompleteObjCSuperMessage(getCurScope(), SuperLoc, None, false); else if (ReceiverType) Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType, None, false); else Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr, None, false); cutOffParsing(); return ExprError(); } // Parse objc-selector SourceLocation Loc; IdentifierInfo *selIdent = ParseObjCSelectorPiece(Loc); SmallVector KeyIdents; SmallVector KeyLocs; ExprVector KeyExprs; if (Tok.is(tok::colon)) { while (1) { // Each iteration parses a single keyword argument. KeyIdents.push_back(selIdent); KeyLocs.push_back(Loc); if (ExpectAndConsume(tok::colon)) { // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_square, StopAtSemi); return ExprError(); } /// Parse the expression after ':' if (Tok.is(tok::code_completion)) { if (SuperLoc.isValid()) Actions.CodeCompleteObjCSuperMessage(getCurScope(), SuperLoc, KeyIdents, /*AtArgumentEpression=*/true); else if (ReceiverType) Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType, KeyIdents, /*AtArgumentEpression=*/true); else Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr, KeyIdents, /*AtArgumentEpression=*/true); cutOffParsing(); return ExprError(); } ExprResult Expr; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); Expr = ParseBraceInitializer(); } else Expr = ParseAssignmentExpression(); ExprResult Res(Expr); if (Res.isInvalid()) { // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_square, StopAtSemi); return Res; } // We have a valid expression. KeyExprs.push_back(Res.get()); // Code completion after each argument. if (Tok.is(tok::code_completion)) { if (SuperLoc.isValid()) Actions.CodeCompleteObjCSuperMessage(getCurScope(), SuperLoc, KeyIdents, /*AtArgumentEpression=*/false); else if (ReceiverType) Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType, KeyIdents, /*AtArgumentEpression=*/false); else Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr, KeyIdents, /*AtArgumentEpression=*/false); cutOffParsing(); return ExprError(); } // Check for another keyword selector. selIdent = ParseObjCSelectorPiece(Loc); if (!selIdent && Tok.isNot(tok::colon)) break; // We have a selector or a colon, continue parsing. } // Parse the, optional, argument list, comma separated. while (Tok.is(tok::comma)) { SourceLocation commaLoc = ConsumeToken(); // Eat the ','. /// Parse the expression after ',' ExprResult Res(ParseAssignmentExpression()); if (Tok.is(tok::colon)) Res = Actions.CorrectDelayedTyposInExpr(Res); if (Res.isInvalid()) { if (Tok.is(tok::colon)) { Diag(commaLoc, diag::note_extra_comma_message_arg) << FixItHint::CreateRemoval(commaLoc); } // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_square, StopAtSemi); return Res; } // We have a valid expression. KeyExprs.push_back(Res.get()); } } else if (!selIdent) { Diag(Tok, diag::err_expected) << tok::identifier; // missing selector name. // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_square, StopAtSemi); return ExprError(); } if (Tok.isNot(tok::r_square)) { Diag(Tok, diag::err_expected) << (Tok.is(tok::identifier) ? tok::colon : tok::r_square); // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_square, StopAtSemi); return ExprError(); } SourceLocation RBracLoc = ConsumeBracket(); // consume ']' unsigned nKeys = KeyIdents.size(); if (nKeys == 0) { KeyIdents.push_back(selIdent); KeyLocs.push_back(Loc); } Selector Sel = PP.getSelectorTable().getSelector(nKeys, &KeyIdents[0]); if (SuperLoc.isValid()) return Actions.ActOnSuperMessage(getCurScope(), SuperLoc, Sel, LBracLoc, KeyLocs, RBracLoc, KeyExprs); else if (ReceiverType) return Actions.ActOnClassMessage(getCurScope(), ReceiverType, Sel, LBracLoc, KeyLocs, RBracLoc, KeyExprs); return Actions.ActOnInstanceMessage(getCurScope(), ReceiverExpr, Sel, LBracLoc, KeyLocs, RBracLoc, KeyExprs); } ExprResult Parser::ParseObjCStringLiteral(SourceLocation AtLoc) { ExprResult Res(ParseStringLiteralExpression()); if (Res.isInvalid()) return Res; // @"foo" @"bar" is a valid concatenated string. Eat any subsequent string // expressions. At this point, we know that the only valid thing that starts // with '@' is an @"". SmallVector AtLocs; ExprVector AtStrings; AtLocs.push_back(AtLoc); AtStrings.push_back(Res.get()); while (Tok.is(tok::at)) { AtLocs.push_back(ConsumeToken()); // eat the @. // Invalid unless there is a string literal. if (!isTokenStringLiteral()) return ExprError(Diag(Tok, diag::err_objc_concat_string)); ExprResult Lit(ParseStringLiteralExpression()); if (Lit.isInvalid()) return Lit; AtStrings.push_back(Lit.get()); } return Actions.ParseObjCStringLiteral(AtLocs.data(), AtStrings); } /// ParseObjCBooleanLiteral - /// objc-scalar-literal : '@' boolean-keyword /// ; /// boolean-keyword: 'true' | 'false' | '__objc_yes' | '__objc_no' /// ; ExprResult Parser::ParseObjCBooleanLiteral(SourceLocation AtLoc, bool ArgValue) { SourceLocation EndLoc = ConsumeToken(); // consume the keyword. return Actions.ActOnObjCBoolLiteral(AtLoc, EndLoc, ArgValue); } /// ParseObjCCharacterLiteral - /// objc-scalar-literal : '@' character-literal /// ; ExprResult Parser::ParseObjCCharacterLiteral(SourceLocation AtLoc) { ExprResult Lit(Actions.ActOnCharacterConstant(Tok)); if (Lit.isInvalid()) { return Lit; } ConsumeToken(); // Consume the literal token. return Actions.BuildObjCNumericLiteral(AtLoc, Lit.get()); } /// ParseObjCNumericLiteral - /// objc-scalar-literal : '@' scalar-literal /// ; /// scalar-literal : | numeric-constant /* any numeric constant. */ /// ; ExprResult Parser::ParseObjCNumericLiteral(SourceLocation AtLoc) { ExprResult Lit(Actions.ActOnNumericConstant(Tok)); if (Lit.isInvalid()) { return Lit; } ConsumeToken(); // Consume the literal token. return Actions.BuildObjCNumericLiteral(AtLoc, Lit.get()); } /// ParseObjCBoxedExpr - /// objc-box-expression: /// @( assignment-expression ) ExprResult Parser::ParseObjCBoxedExpr(SourceLocation AtLoc) { if (Tok.isNot(tok::l_paren)) return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@"); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); ExprResult ValueExpr(ParseAssignmentExpression()); if (T.consumeClose()) return ExprError(); if (ValueExpr.isInvalid()) return ExprError(); // Wrap the sub-expression in a parenthesized expression, to distinguish // a boxed expression from a literal. SourceLocation LPLoc = T.getOpenLocation(), RPLoc = T.getCloseLocation(); ValueExpr = Actions.ActOnParenExpr(LPLoc, RPLoc, ValueExpr.get()); return Actions.BuildObjCBoxedExpr(SourceRange(AtLoc, RPLoc), ValueExpr.get()); } ExprResult Parser::ParseObjCArrayLiteral(SourceLocation AtLoc) { ExprVector ElementExprs; // array elements. ConsumeBracket(); // consume the l_square. bool HasInvalidEltExpr = false; while (Tok.isNot(tok::r_square)) { // Parse list of array element expressions (all must be id types). ExprResult Res(ParseAssignmentExpression()); if (Res.isInvalid()) { // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_square, StopAtSemi); return Res; } Res = Actions.CorrectDelayedTyposInExpr(Res.get()); if (Res.isInvalid()) HasInvalidEltExpr = true; // Parse the ellipsis that indicates a pack expansion. if (Tok.is(tok::ellipsis)) Res = Actions.ActOnPackExpansion(Res.get(), ConsumeToken()); if (Res.isInvalid()) HasInvalidEltExpr = true; ElementExprs.push_back(Res.get()); if (Tok.is(tok::comma)) ConsumeToken(); // Eat the ','. else if (Tok.isNot(tok::r_square)) return ExprError(Diag(Tok, diag::err_expected_either) << tok::r_square << tok::comma); } SourceLocation EndLoc = ConsumeBracket(); // location of ']' if (HasInvalidEltExpr) return ExprError(); MultiExprArg Args(ElementExprs); return Actions.BuildObjCArrayLiteral(SourceRange(AtLoc, EndLoc), Args); } ExprResult Parser::ParseObjCDictionaryLiteral(SourceLocation AtLoc) { SmallVector Elements; // dictionary elements. ConsumeBrace(); // consume the l_square. bool HasInvalidEltExpr = false; while (Tok.isNot(tok::r_brace)) { // Parse the comma separated key : value expressions. ExprResult KeyExpr; { ColonProtectionRAIIObject X(*this); KeyExpr = ParseAssignmentExpression(); if (KeyExpr.isInvalid()) { // We must manually skip to a '}', otherwise the expression skipper will // stop at the '}' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_brace, StopAtSemi); return KeyExpr; } } if (ExpectAndConsume(tok::colon)) { SkipUntil(tok::r_brace, StopAtSemi); return ExprError(); } ExprResult ValueExpr(ParseAssignmentExpression()); if (ValueExpr.isInvalid()) { // We must manually skip to a '}', otherwise the expression skipper will // stop at the '}' when it skips to the ';'. We want it to skip beyond // the enclosing expression. SkipUntil(tok::r_brace, StopAtSemi); return ValueExpr; } // Check the key and value for possible typos KeyExpr = Actions.CorrectDelayedTyposInExpr(KeyExpr.get()); ValueExpr = Actions.CorrectDelayedTyposInExpr(ValueExpr.get()); if (KeyExpr.isInvalid() || ValueExpr.isInvalid()) HasInvalidEltExpr = true; // Parse the ellipsis that designates this as a pack expansion. Do not // ActOnPackExpansion here, leave it to template instantiation time where // we can get better diagnostics. SourceLocation EllipsisLoc; if (getLangOpts().CPlusPlus) TryConsumeToken(tok::ellipsis, EllipsisLoc); // We have a valid expression. Collect it in a vector so we can // build the argument list. ObjCDictionaryElement Element = { KeyExpr.get(), ValueExpr.get(), EllipsisLoc, None }; Elements.push_back(Element); if (!TryConsumeToken(tok::comma) && Tok.isNot(tok::r_brace)) return ExprError(Diag(Tok, diag::err_expected_either) << tok::r_brace << tok::comma); } SourceLocation EndLoc = ConsumeBrace(); if (HasInvalidEltExpr) return ExprError(); // Create the ObjCDictionaryLiteral. return Actions.BuildObjCDictionaryLiteral(SourceRange(AtLoc, EndLoc), Elements); } /// objc-encode-expression: /// \@encode ( type-name ) ExprResult Parser::ParseObjCEncodeExpression(SourceLocation AtLoc) { assert(Tok.isObjCAtKeyword(tok::objc_encode) && "Not an @encode expression!"); SourceLocation EncLoc = ConsumeToken(); if (Tok.isNot(tok::l_paren)) return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@encode"); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); TypeResult Ty = ParseTypeName(); T.consumeClose(); if (Ty.isInvalid()) return ExprError(); return Actions.ParseObjCEncodeExpression(AtLoc, EncLoc, T.getOpenLocation(), Ty.get(), T.getCloseLocation()); } /// objc-protocol-expression /// \@protocol ( protocol-name ) ExprResult Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) { SourceLocation ProtoLoc = ConsumeToken(); if (Tok.isNot(tok::l_paren)) return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@protocol"); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); if (expectIdentifier()) return ExprError(); IdentifierInfo *protocolId = Tok.getIdentifierInfo(); SourceLocation ProtoIdLoc = ConsumeToken(); T.consumeClose(); return Actions.ParseObjCProtocolExpression(protocolId, AtLoc, ProtoLoc, T.getOpenLocation(), ProtoIdLoc, T.getCloseLocation()); } /// objc-selector-expression /// @selector '(' '('[opt] objc-keyword-selector ')'[opt] ')' ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) { SourceLocation SelectorLoc = ConsumeToken(); if (Tok.isNot(tok::l_paren)) return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@selector"); SmallVector KeyIdents; SourceLocation sLoc; BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); bool HasOptionalParen = Tok.is(tok::l_paren); if (HasOptionalParen) ConsumeParen(); if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCSelector(getCurScope(), KeyIdents); cutOffParsing(); return ExprError(); } IdentifierInfo *SelIdent = ParseObjCSelectorPiece(sLoc); if (!SelIdent && // missing selector name. Tok.isNot(tok::colon) && Tok.isNot(tok::coloncolon)) return ExprError(Diag(Tok, diag::err_expected) << tok::identifier); KeyIdents.push_back(SelIdent); unsigned nColons = 0; if (Tok.isNot(tok::r_paren)) { while (1) { if (TryConsumeToken(tok::coloncolon)) { // Handle :: in C++. ++nColons; KeyIdents.push_back(nullptr); } else if (ExpectAndConsume(tok::colon)) // Otherwise expect ':'. return ExprError(); ++nColons; if (Tok.is(tok::r_paren)) break; if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCSelector(getCurScope(), KeyIdents); cutOffParsing(); return ExprError(); } // Check for another keyword selector. SourceLocation Loc; SelIdent = ParseObjCSelectorPiece(Loc); KeyIdents.push_back(SelIdent); if (!SelIdent && Tok.isNot(tok::colon) && Tok.isNot(tok::coloncolon)) break; } } if (HasOptionalParen && Tok.is(tok::r_paren)) ConsumeParen(); // ')' T.consumeClose(); Selector Sel = PP.getSelectorTable().getSelector(nColons, &KeyIdents[0]); return Actions.ParseObjCSelectorExpression(Sel, AtLoc, SelectorLoc, T.getOpenLocation(), T.getCloseLocation(), !HasOptionalParen); } void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) { // MCDecl might be null due to error in method or c-function prototype, etc. Decl *MCDecl = LM.D; bool skip = MCDecl && ((parseMethod && !Actions.isObjCMethodDecl(MCDecl)) || (!parseMethod && Actions.isObjCMethodDecl(MCDecl))); if (skip) return; // Save the current token position. SourceLocation OrigLoc = Tok.getLocation(); assert(!LM.Toks.empty() && "ParseLexedObjCMethodDef - Empty body!"); // Store an artificial EOF token to ensure that we don't run off the end of // the method's body when we come to parse it. Token Eof; Eof.startToken(); Eof.setKind(tok::eof); Eof.setEofData(MCDecl); Eof.setLocation(OrigLoc); LM.Toks.push_back(Eof); // Append the current token at the end of the new token stream so that it // doesn't get lost. LM.Toks.push_back(Tok); PP.EnterTokenStream(LM.Toks, true); // Consume the previously pushed token. ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); assert(Tok.isOneOf(tok::l_brace, tok::kw_try, tok::colon) && "Inline objective-c method not starting with '{' or 'try' or ':'"); // Enter a scope for the method or c-function body. ParseScope BodyScope(this, parseMethod ? Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope : Scope::FnScope|Scope::DeclScope); // Tell the actions module that we have entered a method or c-function definition // with the specified Declarator for the method/function. if (parseMethod) Actions.ActOnStartOfObjCMethodDef(getCurScope(), MCDecl); else Actions.ActOnStartOfFunctionDef(getCurScope(), MCDecl); if (Tok.is(tok::kw_try)) ParseFunctionTryBlock(MCDecl, BodyScope); else { if (Tok.is(tok::colon)) ParseConstructorInitializer(MCDecl); else Actions.ActOnDefaultCtorInitializers(MCDecl); ParseFunctionStatementBody(MCDecl, BodyScope); } if (Tok.getLocation() != OrigLoc) { // Due to parsing error, we either went over the cached tokens or // there are still cached tokens left. If it's the latter case skip the // leftover tokens. // Since this is an uncommon situation that should be avoided, use the // expensive isBeforeInTranslationUnit call. if (PP.getSourceManager().isBeforeInTranslationUnit(Tok.getLocation(), OrigLoc)) while (Tok.getLocation() != OrigLoc && Tok.isNot(tok::eof)) ConsumeAnyToken(); } // Clean up the remaining EOF token. ConsumeAnyToken(); }