3699 lines
128 KiB
C++
3699 lines
128 KiB
C++
//===--- ParseObjC.cpp - Objective C Parsing ------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the Objective-C portions of the Parser interface.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/PrettyDeclStackTrace.h"
|
|
#include "clang/Basic/CharInfo.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "clang/Parse/ParseDiagnostic.h"
|
|
#include "clang/Parse/Parser.h"
|
|
#include "clang/Parse/RAIIObjectsForParser.h"
|
|
#include "clang/Sema/DeclSpec.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(ParsedAttributesWithRange &Attrs) {
|
|
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:
|
|
SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, Attrs);
|
|
break;
|
|
case tok::objc_protocol:
|
|
return ParseObjCAtProtocolDeclaration(AtLoc, Attrs);
|
|
case tok::objc_implementation:
|
|
return ParseObjCAtImplementationDeclaration(AtLoc, Attrs);
|
|
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) {
|
|
SingleDecl = ParseModuleImport(AtLoc);
|
|
break;
|
|
}
|
|
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<IdentifierInfo *, 8> ClassNames;
|
|
SmallVector<SourceLocation, 8> ClassLocs;
|
|
SmallVector<ObjCTypeParamList *, 8> 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->getBeginLoc(), 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<IdentifierLocPair, 8> 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().ObjC) {
|
|
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<Decl *, 8> ProtocolRefs;
|
|
SmallVector<SourceLocation, 8> 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);
|
|
|
|
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<ParsedType, 4> typeArgs;
|
|
SourceLocation typeArgsRAngleLoc;
|
|
SmallVector<Decl *, 4> protocols;
|
|
SmallVector<SourceLocation, 4> 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);
|
|
|
|
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 = [&](AttributePool &Pool) -> ParsedAttr * {
|
|
return Pool.create(P.getNullabilityKeyword(nullability),
|
|
SourceRange(nullabilityLoc), nullptr, SourceLocation(),
|
|
nullptr, 0, ParsedAttr::AS_ContextSensitiveKeyword);
|
|
};
|
|
|
|
if (D.getNumTypeObjects() > 0) {
|
|
// Add the attribute to the declarator chunk nearest the declarator.
|
|
D.getTypeObject(0).getAttrs().addAtEnd(
|
|
getNullabilityAttr(D.getAttributePool()));
|
|
} else if (!addedToDeclSpec) {
|
|
// Otherwise, just put it on the declaration specifiers (if one
|
|
// isn't there already).
|
|
D.getMutableDeclSpec().getAttributes().addAtEnd(
|
|
getNullabilityAttr(D.getMutableDeclSpec().getAttributes().getPool()));
|
|
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<IdentifierLocPair> &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<Decl *, 4> 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(lAngleLoc, rAngleLoc,
|
|
/*ConsumeLastToken=*/true,
|
|
/*ObjCGenericList=*/true)) {
|
|
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<IdentifierLocPair, 1> 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<Decl *, 32> allMethods;
|
|
SmallVector<DeclGroupPtrTy, 8> 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)) {
|
|
// FIXME: This should use ConsumeExtraSemi() for extraneous semicolons,
|
|
// to make -Wextra-semi diagnose them.
|
|
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);
|
|
|
|
// Since we call ParseDeclarationOrFunctionDefinition() instead of
|
|
// ParseExternalDeclaration() below (so that this doesn't parse nested
|
|
// @interfaces), this needs to duplicate some code from the latter.
|
|
if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
|
|
SourceLocation DeclEnd;
|
|
allTUVariables.push_back(
|
|
ParseDeclaration(DeclaratorContext::File, DeclEnd, attrs));
|
|
continue;
|
|
}
|
|
|
|
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->getBeginLoc(), diag::note_objc_container_start)
|
|
<< (int)Actions.getObjCContainerKind();
|
|
ConsumeToken();
|
|
break;
|
|
|
|
case tok::objc_required:
|
|
case tok::objc_optional:
|
|
// This is only valid on protocols.
|
|
if (contextKey != tok::objc_protocol)
|
|
Diag(AtLoc, diag::err_objc_directive_only_in_protocol);
|
|
else
|
|
MethodImplKind = DirectiveKind;
|
|
break;
|
|
|
|
case tok::objc_property:
|
|
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() &
|
|
ObjCPropertyAttribute::kind_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->getBeginLoc(), 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 ':'
|
|
/// direct
|
|
/// 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(ObjCPropertyAttribute::kind_readonly);
|
|
else if (II->isStr("assign"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_assign);
|
|
else if (II->isStr("unsafe_unretained"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_unsafe_unretained);
|
|
else if (II->isStr("readwrite"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_readwrite);
|
|
else if (II->isStr("retain"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_retain);
|
|
else if (II->isStr("strong"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_strong);
|
|
else if (II->isStr("copy"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_copy);
|
|
else if (II->isStr("nonatomic"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nonatomic);
|
|
else if (II->isStr("atomic"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_atomic);
|
|
else if (II->isStr("weak"))
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_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(ObjCPropertyAttribute::kind_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(ObjCPropertyAttribute::kind_getter);
|
|
DS.setGetterName(SelIdent, SelLoc);
|
|
}
|
|
} else if (II->isStr("nonnull")) {
|
|
if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
|
|
diagnoseRedundantPropertyNullability(*this, DS,
|
|
NullabilityKind::NonNull,
|
|
Tok.getLocation());
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
|
|
DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull);
|
|
} else if (II->isStr("nullable")) {
|
|
if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
|
|
diagnoseRedundantPropertyNullability(*this, DS,
|
|
NullabilityKind::Nullable,
|
|
Tok.getLocation());
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
|
|
DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable);
|
|
} else if (II->isStr("null_unspecified")) {
|
|
if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
|
|
diagnoseRedundantPropertyNullability(*this, DS,
|
|
NullabilityKind::Unspecified,
|
|
Tok.getLocation());
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
|
|
DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
|
|
} else if (II->isStr("null_resettable")) {
|
|
if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
|
|
diagnoseRedundantPropertyNullability(*this, DS,
|
|
NullabilityKind::Unspecified,
|
|
Tok.getLocation());
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
|
|
DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
|
|
|
|
// Also set the null_resettable bit.
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_null_resettable);
|
|
} else if (II->isStr("class")) {
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_class);
|
|
} else if (II->isStr("direct")) {
|
|
DS.setPropertyAttributes(ObjCPropertyAttribute::kind_direct);
|
|
} 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().ObjC && 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,
|
|
DeclaratorContext Context) {
|
|
assert(Context == DeclaratorContext::ObjCParameter ||
|
|
Context == DeclaratorContext::ObjCResult);
|
|
|
|
while (1) {
|
|
if (Tok.is(tok::code_completion)) {
|
|
Actions.CodeCompleteObjCPassingType(
|
|
getCurScope(), DS, Context == DeclaratorContext::ObjCParameter);
|
|
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(ParsedAttributesView &attrs,
|
|
ParsedAttributesView &from) {
|
|
for (auto &AL : llvm::reverse(from)) {
|
|
if (!AL.isUsedAsTypeAttr()) {
|
|
from.remove(&AL);
|
|
attrs.addAtEnd(&AL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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.getMutableDeclSpec().getAttributes());
|
|
takeDeclAttributes(attrs, D.getAttributes());
|
|
for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i)
|
|
takeDeclAttributes(attrs, D.getTypeObject(i).getAttrs());
|
|
}
|
|
|
|
/// objc-type-name:
|
|
/// '(' objc-type-qualifiers[opt] type-name ')'
|
|
/// '(' objc-type-qualifiers[opt] ')'
|
|
///
|
|
ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
|
|
DeclaratorContext context,
|
|
ParsedAttributes *paramAttrs) {
|
|
assert(context == DeclaratorContext::ObjCParameter ||
|
|
context == DeclaratorContext::ObjCResult);
|
|
assert((paramAttrs != nullptr) ==
|
|
(context == DeclaratorContext::ObjCParameter));
|
|
|
|
assert(Tok.is(tok::l_paren) && "expected (");
|
|
|
|
BalancedDelimiterTracker T(*this, tok::l_paren);
|
|
T.consumeOpen();
|
|
|
|
ObjCDeclContextSwitch ObjCDC(*this);
|
|
|
|
// Parse type qualifiers, in, inout, etc.
|
|
ParseObjCTypeQualifierList(DS, context);
|
|
SourceLocation TypeStartLoc = Tok.getLocation();
|
|
|
|
ParsedType Ty;
|
|
if (isTypeSpecifierQualifier() || isObjCInstancetype()) {
|
|
// Parse an abstract declarator.
|
|
DeclSpec declSpec(AttrFactory);
|
|
declSpec.setObjCQualifiers(&DS);
|
|
DeclSpecContext dsContext = DeclSpecContext::DSC_normal;
|
|
if (context == DeclaratorContext::ObjCResult)
|
|
dsContext = DeclSpecContext::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 == DeclaratorContext::ObjCParameter)
|
|
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, DeclaratorContext::ObjCResult, nullptr);
|
|
|
|
// If attributes exist before the method, parse them.
|
|
ParsedAttributes methodAttrs(AttrFactory);
|
|
if (getLangOpts().ObjC)
|
|
MaybeParseGNUAttributes(methodAttrs);
|
|
MaybeParseCXX11Attributes(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<DeclaratorChunk::ParamInfo, 8> CParamInfo;
|
|
if (Tok.isNot(tok::colon)) {
|
|
// If attributes exist after the method, parse them.
|
|
if (getLangOpts().ObjC)
|
|
MaybeParseGNUAttributes(methodAttrs);
|
|
MaybeParseCXX11Attributes(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,
|
|
MethodImplKind, false, MethodDefinition);
|
|
PD.complete(Result);
|
|
return Result;
|
|
}
|
|
|
|
SmallVector<IdentifierInfo *, 12> KeyIdents;
|
|
SmallVector<SourceLocation, 12> KeyLocs;
|
|
SmallVector<Sema::ObjCArgInfo, 12> 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, DeclaratorContext::ObjCParameter, ¶mAttrs);
|
|
|
|
// If attributes exist before the argument name, parse them.
|
|
// Regardless, collect all the attributes we've parsed so far.
|
|
if (getLangOpts().ObjC)
|
|
MaybeParseGNUAttributes(paramAttrs);
|
|
MaybeParseCXX11Attributes(paramAttrs);
|
|
ArgInfo.ArgAttrs = paramAttrs;
|
|
|
|
// 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, DeclaratorContext::Prototype);
|
|
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().ObjC)
|
|
MaybeParseGNUAttributes(methodAttrs);
|
|
MaybeParseCXX11Attributes(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,
|
|
MethodImplKind, isVariadic, MethodDefinition);
|
|
|
|
PD.complete(Result);
|
|
return Result;
|
|
}
|
|
|
|
/// objc-protocol-refs:
|
|
/// '<' identifier-list '>'
|
|
///
|
|
bool Parser::
|
|
ParseObjCProtocolReferences(SmallVectorImpl<Decl *> &Protocols,
|
|
SmallVectorImpl<SourceLocation> &ProtocolLocs,
|
|
bool WarnOnDeclarations, bool ForObjCContainer,
|
|
SourceLocation &LAngleLoc, SourceLocation &EndLoc,
|
|
bool consumeLastToken) {
|
|
assert(Tok.is(tok::less) && "expected <");
|
|
|
|
LAngleLoc = ConsumeToken(); // the "<"
|
|
|
|
SmallVector<IdentifierLocPair, 8> 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(LAngleLoc, 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().ObjC && "Protocol qualifiers only exist in Objective-C");
|
|
|
|
SourceLocation lAngleLoc;
|
|
SmallVector<Decl *, 8> protocols;
|
|
SmallVector<SourceLocation, 8> 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<ParsedType> &typeArgs,
|
|
SourceLocation &typeArgsRAngleLoc,
|
|
SourceLocation &protocolLAngleLoc,
|
|
SmallVectorImpl<Decl *> &protocols,
|
|
SmallVectorImpl<SourceLocation> &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<IdentifierInfo *, 4> identifiers;
|
|
SmallVectorImpl<SourceLocation> &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<IdentifierLocPair, 4> 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(lAngleLoc, 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<IdentifierInfo *, 2> unknownTypeArgs;
|
|
SmallVector<SourceLocation, 2> 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, DeclaratorContext::TypeName);
|
|
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(lAngleLoc, 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<ParsedType> &typeArgs,
|
|
SourceLocation &typeArgsRAngleLoc,
|
|
SourceLocation &protocolLAngleLoc,
|
|
SmallVectorImpl<Decl *> &protocols,
|
|
SmallVectorImpl<SourceLocation> &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<NSView><NSTextDelegate>
|
|
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<ParsedType, 4> typeArgs;
|
|
SourceLocation typeArgsRAngleLoc;
|
|
SourceLocation protocolLAngleLoc;
|
|
SmallVector<Decl *, 4> protocols;
|
|
SmallVector<SourceLocation, 4> 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<Decl *> &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(),
|
|
ParsedAttributesView());
|
|
}
|
|
|
|
/// 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 static_assert-declaration
|
|
/// 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<Decl *, 32> 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, /*IsReinject*/true);
|
|
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();
|
|
}
|
|
|
|
// This needs to duplicate a small amount of code from
|
|
// ParseStructUnionBody() for things that should work in both
|
|
// C struct and in Objective-C class instance variables.
|
|
if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
|
|
SourceLocation DeclEnd;
|
|
ParseStaticAssertDeclaration(DeclEnd);
|
|
continue;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
CheckNestedObjCContexts(AtLoc);
|
|
|
|
if (Tok.is(tok::comma)) { // list of forward declarations.
|
|
SmallVector<IdentifierLocPair, 8> 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);
|
|
}
|
|
|
|
// Last, and definitely not least, parse a protocol declaration.
|
|
SourceLocation LAngleLoc, EndProtoLoc;
|
|
|
|
SmallVector<Decl *, 8> ProtocolRefs;
|
|
SmallVector<SourceLocation, 8> 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);
|
|
|
|
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,
|
|
ParsedAttributes &Attrs) {
|
|
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<IdentifierLocPair, 8> 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<Decl *, 4> protocols;
|
|
SmallVector<SourceLocation, 4> protocolLocs;
|
|
(void)ParseObjCProtocolReferences(protocols, protocolLocs,
|
|
/*warnOnIncompleteProtocols=*/false,
|
|
/*ForObjCContainer=*/false,
|
|
protocolLAngleLoc, protocolRAngleLoc,
|
|
/*consumeLastToken=*/true);
|
|
}
|
|
ObjCImpDecl = Actions.ActOnStartCategoryImplementation(
|
|
AtLoc, nameId, nameLoc, categoryId, categoryLoc, Attrs);
|
|
|
|
} 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, Attrs);
|
|
|
|
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<Decl *, 4> protocols;
|
|
SmallVector<SourceLocation, 4> protocolLocs;
|
|
(void)ParseObjCProtocolReferences(protocols, protocolLocs,
|
|
/*warnOnIncompleteProtocols=*/false,
|
|
/*ForObjCContainer=*/false,
|
|
protocolLAngleLoc, protocolRAngleLoc,
|
|
/*consumeLastToken=*/true);
|
|
}
|
|
}
|
|
assert(ObjCImpDecl);
|
|
|
|
SmallVector<Decl *, 8> 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->getBeginLoc(), 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*/);
|
|
|
|
/// 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 | Scope::CompoundStmtScope);
|
|
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 | Scope::CompoundStmtScope);
|
|
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::CompoundStmtScope |
|
|
Scope::AtCatchScope);
|
|
if (Tok.isNot(tok::ellipsis)) {
|
|
DeclSpec DS(AttrFactory);
|
|
ParseDeclarationSpecifiers(DS);
|
|
Declarator ParmDecl(DS, DeclaratorContext::ObjCCatch);
|
|
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 | Scope::CompoundStmtScope);
|
|
|
|
bool ShouldCapture =
|
|
getTargetInfo().getTriple().isWindowsMSVCEnvironment();
|
|
if (ShouldCapture)
|
|
Actions.ActOnCapturedRegionStart(Tok.getLocation(), getCurScope(),
|
|
CR_ObjCAtFinally, 1);
|
|
|
|
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());
|
|
if (ShouldCapture)
|
|
Actions.ActOnCapturedRegionError();
|
|
} else if (ShouldCapture) {
|
|
FinallyBody = Actions.ActOnCapturedRegionEnd(FinallyBody.get());
|
|
}
|
|
|
|
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 | Scope::CompoundStmtScope);
|
|
|
|
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.Context, 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,
|
|
ParsedStmtContext StmtCtx) {
|
|
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 handleExprStmt(Res, StmtCtx);
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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, DeclaratorContext::TypeName);
|
|
TypeResult Type = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
|
|
if (Type.isInvalid())
|
|
return true;
|
|
|
|
IsExpr = false;
|
|
TypeOrExpr = Type.get().getAsOpaquePtr();
|
|
return false;
|
|
}
|
|
|
|
/// 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().ObjC &&
|
|
"Incorrect start for isSimpleObjCMessageExpression");
|
|
return GetLookAheadToken(1).is(tok::identifier) &&
|
|
GetLookAheadToken(2).is(tok::identifier);
|
|
}
|
|
|
|
bool Parser::isStartOfObjCClassMessageMissingOpenBracket() {
|
|
if (!getLangOpts().ObjC || !NextToken().is(tok::identifier) ||
|
|
InMessageExpression)
|
|
return false;
|
|
|
|
TypeResult 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;
|
|
|
|
// FIXME: Should not be querying properties of types from the parser.
|
|
if (Type.isUsable() && Type.get().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<Expr *>(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());
|
|
}
|
|
|
|
/// 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<IdentifierInfo *, 12> KeyIdents;
|
|
SmallVector<SourceLocation, 12> 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,
|
|
/*AtArgumentExpression=*/true);
|
|
else if (ReceiverType)
|
|
Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType,
|
|
KeyIdents,
|
|
/*AtArgumentExpression=*/true);
|
|
else
|
|
Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr,
|
|
KeyIdents,
|
|
/*AtArgumentExpression=*/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,
|
|
/*AtArgumentExpression=*/false);
|
|
else if (ReceiverType)
|
|
Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType,
|
|
KeyIdents,
|
|
/*AtArgumentExpression=*/false);
|
|
else
|
|
Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr,
|
|
KeyIdents,
|
|
/*AtArgumentExpression=*/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<SourceLocation, 4> 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<ObjCDictionaryElement, 4> 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<IdentifierInfo *, 12> 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, /*IsReinject*/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 : 0) |
|
|
Scope::FnScope | Scope::DeclScope |
|
|
Scope::CompoundStmtScope);
|
|
|
|
// 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();
|
|
}
|