blob: 4ae2afe068fedc60bed9322ab98e36f2b5070661 [file] [log] [blame]
//===--- ParseObjC.cpp - Objective C Parsing ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Objective-C portions of the Parser interface.
//
//===----------------------------------------------------------------------===//
#include "clang/Parse/Parser.h"
#include "RAIIObjectsForParser.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/PrettyDeclStackTrace.h"
#include "clang/Sema/Scope.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
using namespace clang;
/// Skips attributes after an Objective-C @ directive. Emits a diagnostic.
void Parser::MaybeSkipAttributes(tok::ObjCKeywordKind Kind) {
ParsedAttributes attrs(AttrFactory);
if (Tok.is(tok::kw___attribute)) {
if (Kind == tok::objc_interface || Kind == tok::objc_protocol)
Diag(Tok, diag::err_objc_postfix_attribute_hint)
<< (Kind == tok::objc_protocol);
else
Diag(Tok, diag::err_objc_postfix_attribute);
ParseGNUAttributes(attrs);
}
}
/// ParseObjCAtDirectives - Handle parts of the external-declaration production:
/// external-declaration: [C99 6.9]
/// [OBJC] objc-class-definition
/// [OBJC] objc-class-declaration
/// [OBJC] objc-alias-declaration
/// [OBJC] objc-protocol-definition
/// [OBJC] objc-method-definition
/// [OBJC] '@' 'end'
Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives() {
SourceLocation AtLoc = ConsumeToken(); // the "@"
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCAtDirective(getCurScope());
cutOffParsing();
return nullptr;
}
Decl *SingleDecl = nullptr;
switch (Tok.getObjCKeywordID()) {
case tok::objc_class:
return ParseObjCAtClassDeclaration(AtLoc);
case tok::objc_interface: {
ParsedAttributes attrs(AttrFactory);
SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, attrs);
break;
}
case tok::objc_protocol: {
ParsedAttributes attrs(AttrFactory);
return ParseObjCAtProtocolDeclaration(AtLoc, attrs);
}
case tok::objc_implementation:
return ParseObjCAtImplementationDeclaration(AtLoc);
case tok::objc_end:
return ParseObjCAtEndDeclaration(AtLoc);
case tok::objc_compatibility_alias:
SingleDecl = ParseObjCAtAliasDeclaration(AtLoc);
break;
case tok::objc_synthesize:
SingleDecl = ParseObjCPropertySynthesize(AtLoc);
break;
case tok::objc_dynamic:
SingleDecl = ParseObjCPropertyDynamic(AtLoc);
break;
case tok::objc_import:
if (getLangOpts().Modules || getLangOpts().DebuggerSupport)
return ParseModuleImport(AtLoc);
Diag(AtLoc, diag::err_atimport);
SkipUntil(tok::semi);
return Actions.ConvertDeclToDeclGroup(nullptr);
default:
Diag(AtLoc, diag::err_unexpected_at);
SkipUntil(tok::semi);
SingleDecl = nullptr;
break;
}
return Actions.ConvertDeclToDeclGroup(SingleDecl);
}
/// Class to handle popping type parameters when leaving the scope.
class Parser::ObjCTypeParamListScope {
Sema &Actions;
Scope *S;
ObjCTypeParamList *Params;
public:
ObjCTypeParamListScope(Sema &Actions, Scope *S)
: Actions(Actions), S(S), Params(nullptr) {}
~ObjCTypeParamListScope() {
leave();
}
void enter(ObjCTypeParamList *P) {
assert(!Params);
Params = P;
}
void leave() {
if (Params)
Actions.popObjCTypeParamList(S, Params);
Params = nullptr;
}
};
///
/// objc-class-declaration:
/// '@' 'class' objc-class-forward-decl (',' objc-class-forward-decl)* ';'
///
/// objc-class-forward-decl:
/// identifier objc-type-parameter-list[opt]
///
Parser::DeclGroupPtrTy
Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) {
ConsumeToken(); // the identifier "class"
SmallVector<IdentifierInfo *, 8> ClassNames;
SmallVector<SourceLocation, 8> ClassLocs;
SmallVector<ObjCTypeParamList *, 8> ClassTypeParams;
while (1) {
MaybeSkipAttributes(tok::objc_class);
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
SkipUntil(tok::semi);
return Actions.ConvertDeclToDeclGroup(nullptr);
}
ClassNames.push_back(Tok.getIdentifierInfo());
ClassLocs.push_back(Tok.getLocation());
ConsumeToken();
// Parse the optional objc-type-parameter-list.
ObjCTypeParamList *TypeParams = nullptr;
if (Tok.is(tok::less))
TypeParams = parseObjCTypeParamList();
ClassTypeParams.push_back(TypeParams);
if (!TryConsumeToken(tok::comma))
break;
}
// Consume the ';'.
if (ExpectAndConsume(tok::semi, diag::err_expected_after, "@class"))
return Actions.ConvertDeclToDeclGroup(nullptr);
return Actions.ActOnForwardClassDeclaration(atLoc, ClassNames.data(),
ClassLocs.data(),
ClassTypeParams,
ClassNames.size());
}
void Parser::CheckNestedObjCContexts(SourceLocation AtLoc)
{
Sema::ObjCContainerKind ock = Actions.getObjCContainerKind();
if (ock == Sema::OCK_None)
return;
Decl *Decl = Actions.getObjCDeclContext();
if (CurParsedObjCImpl) {
CurParsedObjCImpl->finish(AtLoc);
} else {
Actions.ActOnAtEnd(getCurScope(), AtLoc);
}
Diag(AtLoc, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(AtLoc, "@end\n");
if (Decl)
Diag(Decl->getLocStart(), diag::note_objc_container_start)
<< (int) ock;
}
///
/// objc-interface:
/// objc-class-interface-attributes[opt] objc-class-interface
/// objc-category-interface
///
/// objc-class-interface:
/// '@' 'interface' identifier objc-type-parameter-list[opt]
/// objc-superclass[opt] objc-protocol-refs[opt]
/// objc-class-instance-variables[opt]
/// objc-interface-decl-list
/// @end
///
/// objc-category-interface:
/// '@' 'interface' identifier objc-type-parameter-list[opt]
/// '(' identifier[opt] ')' objc-protocol-refs[opt]
/// objc-interface-decl-list
/// @end
///
/// objc-superclass:
/// ':' identifier objc-type-arguments[opt]
///
/// objc-class-interface-attributes:
/// __attribute__((visibility("default")))
/// __attribute__((visibility("hidden")))
/// __attribute__((deprecated))
/// __attribute__((unavailable))
/// __attribute__((objc_exception)) - used by NSException on 64-bit
/// __attribute__((objc_root_class))
/// __attribute__((objc_subclassing_restricted))
/// __attribute__((objc_complete_definition))
///
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 (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected)
<< tok::identifier; // missing class or category name.
return nullptr;
}
// 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().ObjC2) {
Diag(Tok, diag::err_expected)
<< tok::identifier; // missing category name.
return nullptr;
}
T.consumeClose();
if (T.getCloseLocation().isInvalid())
return nullptr;
if (!attrs.empty()) { // categories don't support attributes.
Diag(nameLoc, diag::err_objc_no_attributes_on_category);
attrs.clear();
}
// 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);
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 (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected)
<< tok::identifier; // missing super class name.
return nullptr;
}
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);
}
}
// 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, superClassId, superClassLoc);
Decl *ClsType =
Actions.ActOnStartClassInterface(getCurScope(), AtLoc, nameId, nameLoc,
typeParameterList, superClassId,
superClassLoc,
typeArgs,
SourceRange(typeArgsLAngleLoc,
typeArgsRAngleLoc),
protocols.data(), protocols.size(),
protocolLocs.data(),
EndProtoLoc, attrs.getList());
if (Tok.is(tok::l_brace))
ParseObjCClassInstanceVariables(ClsType, tok::objc_protected, AtLoc);
ParseObjCInterfaceDeclList(tok::objc_interface, ClsType);
return ClsType;
}
/// Add an attribute for a context-sensitive type nullability to the given
/// declarator.
static void addContextSensitiveTypeNullability(Parser &P,
Declarator &D,
NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool &addedToDeclSpec) {
// Create the attribute.
auto getNullabilityAttr = [&]() -> AttributeList * {
return D.getAttributePool().create(
P.getNullabilityKeyword(nullability),
SourceRange(nullabilityLoc),
nullptr, SourceLocation(),
nullptr, 0,
AttributeList::AS_ContextSensitiveKeyword);
};
if (D.getNumTypeObjects() > 0) {
// Add the attribute to the declarator chunk nearest the declarator.
auto nullabilityAttr = getNullabilityAttr();
DeclaratorChunk &chunk = D.getTypeObject(0);
nullabilityAttr->setNext(chunk.getAttrListRef());
chunk.getAttrListRef() = nullabilityAttr;
} else if (!addedToDeclSpec) {
// Otherwise, just put it on the declaration specifiers (if one
// isn't there already).
D.getMutableDeclSpec().addAttributes(getNullabilityAttr());
addedToDeclSpec = true;
}
}
/// Parse an Objective-C type parameter list, if present, or capture
/// the locations of the protocol identifiers for a list of protocol
/// references.
///
/// objc-type-parameter-list:
/// '<' objc-type-parameter (',' objc-type-parameter)* '>'
///
/// objc-type-parameter:
/// objc-type-parameter-variance? identifier objc-type-parameter-bound[opt]
///
/// objc-type-parameter-bound:
/// ':' type-name
///
/// objc-type-parameter-variance:
/// '__covariant'
/// '__contravariant'
///
/// \param lAngleLoc The location of the starting '<'.
///
/// \param protocolIdents Will capture the list of identifiers, if the
/// angle brackets contain a list of protocol references rather than a
/// type parameter list.
///
/// \param rAngleLoc The location of the ending '>'.
ObjCTypeParamList *Parser::parseObjCTypeParamListOrProtocolRefs(
ObjCTypeParamListScope &Scope, SourceLocation &lAngleLoc,
SmallVectorImpl<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(rAngleLoc,
/*ConsumeLastToken=*/true,
/*ObjCGenericList=*/true)) {
Diag(lAngleLoc, diag::note_matching) << "'<'";
SkipUntil({tok::greater, tok::greaterequal, tok::at, tok::minus,
tok::minus, tok::plus, tok::colon, tok::l_paren, tok::l_brace,
tok::comma, tok::semi },
StopBeforeMatch);
if (Tok.is(tok::greater))
ConsumeToken();
}
if (mayBeProtocolList) {
// A type parameter list must be followed by either a ':' (indicating the
// presence of a superclass) or a '(' (indicating that this is a category
// or extension). This disambiguates between an objc-type-parameter-list
// and a objc-protocol-refs.
if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_paren)) {
// Returning null indicates that we don't have a type parameter list.
// The results the caller needs to handle the protocol references are
// captured in the reference parameters already.
return nullptr;
}
// We have a type parameter list that looks like a list of protocol
// references. Turn that parameter list into type parameters.
makeProtocolIdentsIntoTypeParameters();
}
// Form the type parameter list and enter its scope.
ObjCTypeParamList *list = Actions.actOnObjCTypeParamList(
getCurScope(),
lAngleLoc,
typeParams,
rAngleLoc);
Scope.enter(list);
// Clear out the angle locations; they're used by the caller to indicate
// whether there are any protocol references.
lAngleLoc = SourceLocation();
rAngleLoc = SourceLocation();
return invalid ? nullptr : list;
}
/// Parse an objc-type-parameter-list.
ObjCTypeParamList *Parser::parseObjCTypeParamList() {
SourceLocation lAngleLoc;
SmallVector<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)) {
ConsumeToken();
continue;
}
// If we got to the end of the file, exit the loop.
if (isEofOrEom())
break;
// Code completion within an Objective-C interface.
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(),
CurParsedObjCImpl? Sema::PCC_ObjCImplementation
: Sema::PCC_ObjCInterface);
return cutOffParsing();
}
// If we don't have an @ directive, parse it as a function definition.
if (Tok.isNot(tok::at)) {
// The code below does not consume '}'s because it is afraid of eating the
// end of a namespace. Because of the way this code is structured, an
// erroneous r_brace would cause an infinite loop if not handled here.
if (Tok.is(tok::r_brace))
break;
ParsedAttributesWithRange attrs(AttrFactory);
allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs));
continue;
}
// Otherwise, we have an @ directive, eat the @.
SourceLocation AtLoc = ConsumeToken(); // the "@"
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCAtDirective(getCurScope());
return cutOffParsing();
}
tok::ObjCKeywordKind DirectiveKind = Tok.getObjCKeywordID();
if (DirectiveKind == tok::objc_end) { // @end -> terminate list
AtEnd.setBegin(AtLoc);
AtEnd.setEnd(Tok.getLocation());
break;
} else if (DirectiveKind == tok::objc_not_keyword) {
Diag(Tok, diag::err_objc_unknown_at);
SkipUntil(tok::semi);
continue;
}
// Eat the identifier.
ConsumeToken();
switch (DirectiveKind) {
default:
// FIXME: If someone forgets an @end on a protocol, this loop will
// continue to eat up tons of stuff and spew lots of nonsense errors. It
// would probably be better to bail out if we saw an @class or @interface
// or something like that.
Diag(AtLoc, diag::err_objc_illegal_interface_qual);
// Skip until we see an '@' or '}' or ';'.
SkipUntil(tok::r_brace, tok::at, StopAtSemi);
break;
case tok::objc_implementation:
case tok::objc_interface:
Diag(AtLoc, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(AtLoc, "@end\n");
Diag(CDecl->getLocStart(), diag::note_objc_container_start)
<< (int) Actions.getObjCContainerKind();
ConsumeToken();
break;
case tok::objc_required:
case tok::objc_optional:
// This is only valid on protocols.
// FIXME: Should this check for ObjC2 being enabled?
if (contextKey != tok::objc_protocol)
Diag(AtLoc, diag::err_objc_directive_only_in_protocol);
else
MethodImplKind = DirectiveKind;
break;
case tok::objc_property:
if (!getLangOpts().ObjC2)
Diag(AtLoc, diag::err_objc_properties_require_objc2);
ObjCDeclSpec OCDS;
SourceLocation LParenLoc;
// Parse property attribute list, if any.
if (Tok.is(tok::l_paren)) {
LParenLoc = Tok.getLocation();
ParseObjCPropertyAttribute(OCDS);
}
bool addedToDeclSpec = false;
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) {
if (FD.D.getIdentifier() == nullptr) {
Diag(AtLoc, diag::err_objc_property_requires_field_name)
<< FD.D.getSourceRange();
return;
}
if (FD.BitfieldSize) {
Diag(AtLoc, diag::err_objc_property_bitfield)
<< FD.D.getSourceRange();
return;
}
// Map a nullability property attribute to a context-sensitive keyword
// attribute.
if (OCDS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
addContextSensitiveTypeNullability(*this, FD.D, OCDS.getNullability(),
OCDS.getNullabilityLoc(),
addedToDeclSpec);
// Install the property declarator into interfaceDecl.
IdentifierInfo *SelName =
OCDS.getGetterName() ? OCDS.getGetterName() : FD.D.getIdentifier();
Selector GetterSel = PP.getSelectorTable().getNullarySelector(SelName);
IdentifierInfo *SetterName = OCDS.getSetterName();
Selector SetterSel;
if (SetterName)
SetterSel = PP.getSelectorTable().getSelector(1, &SetterName);
else
SetterSel = SelectorTable::constructSetterSelector(
PP.getIdentifierTable(), PP.getSelectorTable(),
FD.D.getIdentifier());
Decl *Property = Actions.ActOnProperty(
getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel,
MethodImplKind);
FD.complete(Property);
};
// Parse all the comma separated declarators.
ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, ObjCPropertyCallback);
ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list);
break;
}
}
// We break out of the big loop in two cases: when we see @end or when we see
// EOF. In the former case, eat the @end. In the later case, emit an error.
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCAtDirective(getCurScope());
return cutOffParsing();
} else if (Tok.isObjCAtKeyword(tok::objc_end)) {
ConsumeToken(); // the "end" identifier
} else {
Diag(Tok, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(Tok.getLocation(), "\n@end\n");
Diag(CDecl->getLocStart(), diag::note_objc_container_start)
<< (int) Actions.getObjCContainerKind();
AtEnd.setBegin(Tok.getLocation());
AtEnd.setEnd(Tok.getLocation());
}
// Insert collected methods declarations into the @interface object.
// This passes in an invalid SourceLocation for AtEndLoc when EOF is hit.
Actions.ActOnAtEnd(getCurScope(), AtEnd, allMethods, allTUVariables);
}
/// Diagnose redundant or conflicting nullability information.
static void diagnoseRedundantPropertyNullability(Parser &P,
ObjCDeclSpec &DS,
NullabilityKind nullability,
SourceLocation nullabilityLoc){
if (DS.getNullability() == nullability) {
P.Diag(nullabilityLoc, diag::warn_nullability_duplicate)
<< DiagNullabilityKind(nullability, true)
<< SourceRange(DS.getNullabilityLoc());
return;
}
P.Diag(nullabilityLoc, diag::err_nullability_conflicting)
<< DiagNullabilityKind(nullability, true)
<< DiagNullabilityKind(DS.getNullability(), true)
<< SourceRange(DS.getNullabilityLoc());
}
/// Parse property attribute declarations.
///
/// property-attr-decl: '(' property-attrlist ')'
/// property-attrlist:
/// property-attribute
/// property-attrlist ',' property-attribute
/// property-attribute:
/// getter '=' identifier
/// setter '=' identifier ':'
/// readonly
/// readwrite
/// assign
/// retain
/// copy
/// nonatomic
/// atomic
/// strong
/// weak
/// unsafe_unretained
/// nonnull
/// nullable
/// null_unspecified
/// null_resettable
/// class
///
void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
assert(Tok.getKind() == tok::l_paren);
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
while (1) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCPropertyFlags(getCurScope(), DS);
return cutOffParsing();
}
const IdentifierInfo *II = Tok.getIdentifierInfo();
// If this is not an identifier at all, bail out early.
if (!II) {
T.consumeClose();
return;
}
SourceLocation AttrName = ConsumeToken(); // consume last attribute name
if (II->isStr("readonly"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly);
else if (II->isStr("assign"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign);
else if (II->isStr("unsafe_unretained"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_unsafe_unretained);
else if (II->isStr("readwrite"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite);
else if (II->isStr("retain"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain);
else if (II->isStr("strong"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_strong);
else if (II->isStr("copy"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy);
else if (II->isStr("nonatomic"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic);
else if (II->isStr("atomic"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_atomic);
else if (II->isStr("weak"))
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_weak);
else if (II->isStr("getter") || II->isStr("setter")) {
bool IsSetter = II->getNameStart()[0] == 's';
// getter/setter require extra treatment.
unsigned DiagID = IsSetter ? diag::err_objc_expected_equal_for_setter :
diag::err_objc_expected_equal_for_getter;
if (ExpectAndConsume(tok::equal, DiagID)) {
SkipUntil(tok::r_paren, StopAtSemi);
return;
}
if (Tok.is(tok::code_completion)) {
if (IsSetter)
Actions.CodeCompleteObjCPropertySetter(getCurScope());
else
Actions.CodeCompleteObjCPropertyGetter(getCurScope());
return cutOffParsing();
}
SourceLocation SelLoc;
IdentifierInfo *SelIdent = ParseObjCSelectorPiece(SelLoc);
if (!SelIdent) {
Diag(Tok, diag::err_objc_expected_selector_for_getter_setter)
<< IsSetter;
SkipUntil(tok::r_paren, StopAtSemi);
return;
}
if (IsSetter) {
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_setter);
DS.setSetterName(SelIdent);
if (ExpectAndConsume(tok::colon,
diag::err_expected_colon_after_setter_name)) {
SkipUntil(tok::r_paren, StopAtSemi);
return;
}
} else {
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter);
DS.setGetterName(SelIdent);
}
} else if (II->isStr("nonnull")) {
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
diagnoseRedundantPropertyNullability(*this, DS,
NullabilityKind::NonNull,
Tok.getLocation());
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull);
} else if (II->isStr("nullable")) {
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
diagnoseRedundantPropertyNullability(*this, DS,
NullabilityKind::Nullable,
Tok.getLocation());
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable);
} else if (II->isStr("null_unspecified")) {
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
diagnoseRedundantPropertyNullability(*this, DS,
NullabilityKind::Unspecified,
Tok.getLocation());
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
} else if (II->isStr("null_resettable")) {
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
diagnoseRedundantPropertyNullability(*this, DS,
NullabilityKind::Unspecified,
Tok.getLocation());
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
// Also set the null_resettable bit.
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable);
} else if (II->isStr("class")) {
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class);
} else {
Diag(AttrName, diag::err_objc_expected_property_attr) << II;
SkipUntil(tok::r_paren, StopAtSemi);
return;
}
if (Tok.isNot(tok::comma))
break;
ConsumeToken();
}
T.consumeClose();
}
/// objc-method-proto:
/// objc-instance-method objc-method-decl objc-method-attributes[opt]
/// objc-class-method objc-method-decl objc-method-attributes[opt]
///
/// objc-instance-method: '-'
/// objc-class-method: '+'
///
/// objc-method-attributes: [OBJC2]
/// __attribute__((deprecated))
///
Decl *Parser::ParseObjCMethodPrototype(tok::ObjCKeywordKind MethodImplKind,
bool MethodDefinition) {
assert(Tok.isOneOf(tok::minus, tok::plus) && "expected +/-");
tok::TokenKind methodType = Tok.getKind();
SourceLocation mLoc = ConsumeToken();
Decl *MDecl = ParseObjCMethodDecl(mLoc, methodType, MethodImplKind,
MethodDefinition);
// Since this rule is used for both method declarations and definitions,
// the caller is (optionally) responsible for consuming the ';'.
return MDecl;
}
/// objc-selector:
/// identifier
/// one of
/// enum struct union if else while do for switch case default
/// break continue return goto asm sizeof typeof __alignof
/// unsigned long const short volatile signed restrict _Complex
/// in out inout bycopy byref oneway int char float double void _Bool
///
IdentifierInfo *Parser::ParseObjCSelectorPiece(SourceLocation &SelectorLoc) {
switch (Tok.getKind()) {
default:
return nullptr;
case tok::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.data());
Tok.setKind(tok::identifier);
SelectorLoc = ConsumeToken();
return II;
}
return nullptr;
}
case tok::identifier:
case tok::kw_asm:
case tok::kw_auto:
case tok::kw_bool:
case tok::kw_break:
case tok::kw_case:
case tok::kw_catch:
case tok::kw_char:
case tok::kw_class:
case tok::kw_const:
case tok::kw_const_cast:
case tok::kw_continue:
case tok::kw_default:
case tok::kw_delete:
case tok::kw_do:
case tok::kw_double:
case tok::kw_dynamic_cast:
case tok::kw_else:
case tok::kw_enum:
case tok::kw_explicit:
case tok::kw_export:
case tok::kw_extern:
case tok::kw_false:
case tok::kw_float:
case tok::kw_for:
case tok::kw_friend:
case tok::kw_goto:
case tok::kw_if:
case tok::kw_inline:
case tok::kw_int:
case tok::kw_long:
case tok::kw_mutable:
case tok::kw_namespace:
case tok::kw_new:
case tok::kw_operator:
case tok::kw_private:
case tok::kw_protected:
case tok::kw_public:
case tok::kw_register:
case tok::kw_reinterpret_cast:
case tok::kw_restrict:
case tok::kw_return:
case tok::kw_short:
case tok::kw_signed:
case tok::kw_sizeof:
case tok::kw_static:
case tok::kw_static_cast:
case tok::kw_struct:
case tok::kw_switch:
case tok::kw_template:
case tok::kw_this:
case tok::kw_throw:
case tok::kw_true:
case tok::kw_try:
case tok::kw_typedef:
case tok::kw_typeid:
case tok::kw_typename:
case tok::kw_typeof:
case tok::kw_union:
case tok::kw_unsigned:
case tok::kw_using:
case tok::kw_virtual:
case tok::kw_void:
case tok::kw_volatile:
case tok::kw_wchar_t:
case tok::kw_while:
case tok::kw__Bool:
case tok::kw__Complex:
case tok::kw___alignof:
case tok::kw___auto_type:
IdentifierInfo *II = Tok.getIdentifierInfo();
SelectorLoc = ConsumeToken();
return II;
}
}
/// objc-for-collection-in: 'in'
///
bool Parser::isTokIdentifier_in() const {
// FIXME: May have to do additional look-ahead to only allow for
// valid tokens following an 'in'; such as an identifier, unary operators,
// '[' etc.
return (getLangOpts().ObjC2 && Tok.is(tok::identifier) &&
Tok.getIdentifierInfo() == ObjCTypeQuals[objc_in]);
}
/// ParseObjCTypeQualifierList - This routine parses the objective-c's type
/// qualifier list and builds their bitmask representation in the input
/// argument.
///
/// objc-type-qualifiers:
/// objc-type-qualifier
/// objc-type-qualifiers objc-type-qualifier
///
/// objc-type-qualifier:
/// 'in'
/// 'out'
/// 'inout'
/// 'oneway'
/// 'bycopy'
/// 'byref'
/// 'nonnull'
/// 'nullable'
/// 'null_unspecified'
///
void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
Declarator::TheContext Context) {
assert(Context == Declarator::ObjCParameterContext ||
Context == Declarator::ObjCResultContext);
while (1) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCPassingType(getCurScope(), DS,
Context == Declarator::ObjCParameterContext);
return cutOffParsing();
}
if (Tok.isNot(tok::identifier))
return;
const IdentifierInfo *II = Tok.getIdentifierInfo();
for (unsigned i = 0; i != objc_NumQuals; ++i) {
if (II != ObjCTypeQuals[i] ||
NextToken().is(tok::less) ||
NextToken().is(tok::coloncolon))
continue;
ObjCDeclSpec::ObjCDeclQualifier Qual;
NullabilityKind Nullability;
switch (i) {
default: llvm_unreachable("Unknown decl qualifier");
case objc_in: Qual = ObjCDeclSpec::DQ_In; break;
case objc_out: Qual = ObjCDeclSpec::DQ_Out; break;
case objc_inout: Qual = ObjCDeclSpec::DQ_Inout; break;
case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break;
case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break;
case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break;
case objc_nonnull:
Qual = ObjCDeclSpec::DQ_CSNullability;
Nullability = NullabilityKind::NonNull;
break;
case objc_nullable:
Qual = ObjCDeclSpec::DQ_CSNullability;
Nullability = NullabilityKind::Nullable;
break;
case objc_null_unspecified:
Qual = ObjCDeclSpec::DQ_CSNullability;
Nullability = NullabilityKind::Unspecified;
break;
}
// FIXME: Diagnose redundant specifiers.
DS.setObjCDeclQualifier(Qual);
if (Qual == ObjCDeclSpec::DQ_CSNullability)
DS.setNullability(Tok.getLocation(), Nullability);
ConsumeToken();
II = nullptr;
break;
}
// If this wasn't a recognized qualifier, bail out.
if (II) return;
}
}
/// Take all the decl attributes out of the given list and add
/// them to the given attribute set.
static void takeDeclAttributes(ParsedAttributes &attrs,
AttributeList *list) {
while (list) {
AttributeList *cur = list;
list = cur->getNext();
if (!cur->isUsedAsTypeAttr()) {
// Clear out the next pointer. We're really completely
// destroying the internal invariants of the declarator here,
// but it doesn't matter because we're done with it.
cur->setNext(nullptr);
attrs.add(cur);
}
}
}
/// takeDeclAttributes - Take all the decl attributes from the given
/// declarator and add them to the given list.
static void takeDeclAttributes(ParsedAttributes &attrs,
Declarator &D) {
// First, take ownership of all attributes.
attrs.getPool().takeAllFrom(D.getAttributePool());
attrs.getPool().takeAllFrom(D.getDeclSpec().getAttributePool());
// Now actually move the attributes over.
takeDeclAttributes(attrs, D.getDeclSpec().getAttributes().getList());
takeDeclAttributes(attrs, D.getAttributes());
for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i)
takeDeclAttributes(attrs,
const_cast<AttributeList*>(D.getTypeObject(i).getAttrs()));
}
/// objc-type-name:
/// '(' objc-type-qualifiers[opt] type-name ')'
/// '(' objc-type-qualifiers[opt] ')'
///
ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
Declarator::TheContext context,
ParsedAttributes *paramAttrs) {
assert(context == Declarator::ObjCParameterContext ||
context == Declarator::ObjCResultContext);
assert((paramAttrs != nullptr) ==
(context == Declarator::ObjCParameterContext));
assert(Tok.is(tok::l_paren) && "expected (");
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
SourceLocation TypeStartLoc = Tok.getLocation();
ObjCDeclContextSwitch ObjCDC(*this);
// Parse type qualifiers, in, inout, etc.
ParseObjCTypeQualifierList(DS, context);
ParsedType Ty;
if (isTypeSpecifierQualifier() || isObjCInstancetype()) {
// Parse an abstract declarator.
DeclSpec declSpec(AttrFactory);
declSpec.setObjCQualifiers(&DS);
DeclSpecContext dsContext = DSC_normal;
if (context == Declarator::ObjCResultContext)
dsContext = DSC_objc_method_result;
ParseSpecifierQualifierList(declSpec, AS_none, dsContext);
declSpec.SetRangeEnd(Tok.getLocation());
Declarator declarator(declSpec, context);
ParseDeclarator(declarator);
// If that's not invalid, extract a type.
if (!declarator.isInvalidType()) {
// Map a nullability specifier to a context-sensitive keyword attribute.
bool addedToDeclSpec = false;
if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability)
addContextSensitiveTypeNullability(*this, declarator,
DS.getNullability(),
DS.getNullabilityLoc(),
addedToDeclSpec);
TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
if (!type.isInvalid())
Ty = type.get();
// If we're parsing a parameter, steal all the decl attributes
// and add them to the decl spec.
if (context == Declarator::ObjCParameterContext)
takeDeclAttributes(*paramAttrs, declarator);
}
}
if (Tok.is(tok::r_paren))
T.consumeClose();
else if (Tok.getLocation() == TypeStartLoc) {
// If we didn't eat any tokens, then this isn't a type.
Diag(Tok, diag::err_expected_type);
SkipUntil(tok::r_paren, StopAtSemi);
} else {
// Otherwise, we found *something*, but didn't get a ')' in the right
// place. Emit an error then return what we have as the type.
T.consumeClose();
}
return Ty;
}
/// objc-method-decl:
/// objc-selector
/// objc-keyword-selector objc-parmlist[opt]
/// objc-type-name objc-selector
/// objc-type-name objc-keyword-selector objc-parmlist[opt]
///
/// objc-keyword-selector:
/// objc-keyword-decl
/// objc-keyword-selector objc-keyword-decl
///
/// objc-keyword-decl:
/// objc-selector ':' objc-type-name objc-keyword-attributes[opt] identifier
/// objc-selector ':' objc-keyword-attributes[opt] identifier
/// ':' objc-type-name objc-keyword-attributes[opt] identifier
/// ':' objc-keyword-attributes[opt] identifier
///
/// objc-parmlist:
/// objc-parms objc-ellipsis[opt]
///
/// objc-parms:
/// objc-parms , parameter-declaration
///
/// objc-ellipsis:
/// , ...
///
/// objc-keyword-attributes: [OBJC2]
/// __attribute__((unused))
///
Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
tok::TokenKind mType,
tok::ObjCKeywordKind MethodImplKind,
bool MethodDefinition) {
ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent);
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus,
/*ReturnType=*/nullptr);
cutOffParsing();
return nullptr;
}
// Parse the return type if present.
ParsedType ReturnType;
ObjCDeclSpec DSRet;
if (Tok.is(tok::l_paren))
ReturnType = ParseObjCTypeName(DSRet, Declarator::ObjCResultContext,
nullptr);
// If attributes exist before the method, parse them.
ParsedAttributes methodAttrs(AttrFactory);
if (getLangOpts().ObjC2)
MaybeParseGNUAttributes(methodAttrs);
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus,
ReturnType);
cutOffParsing();
return nullptr;
}
// Now parse the selector.
SourceLocation selLoc;
IdentifierInfo *SelIdent = ParseObjCSelectorPiece(selLoc);
// An unnamed colon is valid.
if (!SelIdent && Tok.isNot(tok::colon)) { // missing selector name.
Diag(Tok, diag::err_expected_selector_for_method)
<< SourceRange(mLoc, Tok.getLocation());
// Skip until we get a ; or @.
SkipUntil(tok::at, StopAtSemi | StopBeforeMatch);
return nullptr;
}
SmallVector<DeclaratorChunk::ParamInfo, 8> CParamInfo;
if (Tok.isNot(tok::colon)) {
// If attributes exist after the method, parse them.
if (getLangOpts().ObjC2)
MaybeParseGNUAttributes(methodAttrs);
Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent);
Decl *Result
= Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(),
mType, DSRet, ReturnType,
selLoc, Sel, nullptr,
CParamInfo.data(), CParamInfo.size(),
methodAttrs.getList(), MethodImplKind,
false, MethodDefinition);
PD.complete(Result);
return Result;
}
SmallVector<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,
Declarator::ObjCParameterContext,
&paramAttrs);
// If attributes exist before the argument name, parse them.
// Regardless, collect all the attributes we've parsed so far.
ArgInfo.ArgAttrs = nullptr;
if (getLangOpts().ObjC2) {
MaybeParseGNUAttributes(paramAttrs);
ArgInfo.ArgAttrs = paramAttrs.getList();
}
// Code completion for the next piece of the selector.
if (Tok.is(tok::code_completion)) {
KeyIdents.push_back(SelIdent);
Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(),
mType == tok::minus,
/*AtParameterName=*/true,
ReturnType, KeyIdents);
cutOffParsing();
return nullptr;
}
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected)
<< tok::identifier; // missing argument name.
break;
}
ArgInfo.Name = Tok.getIdentifierInfo();
ArgInfo.NameLoc = Tok.getLocation();
ConsumeToken(); // Eat the identifier.
ArgInfos.push_back(ArgInfo);
KeyIdents.push_back(SelIdent);
KeyLocs.push_back(selLoc);
// Make sure the attributes persist.
allParamAttrs.takeAllFrom(paramAttrs.getPool());
// Code completion for the next piece of the selector.
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(),
mType == tok::minus,
/*AtParameterName=*/false,
ReturnType, KeyIdents);
cutOffParsing();
return nullptr;
}
// Check for another keyword selector.
SelIdent = ParseObjCSelectorPiece(selLoc);
if (!SelIdent && Tok.isNot(tok::colon))
break;
if (!SelIdent) {
SourceLocation ColonLoc = Tok.getLocation();
if (PP.getLocForEndOfToken(ArgInfo.NameLoc) == ColonLoc) {
Diag(ArgInfo.NameLoc, diag::warn_missing_selector_name) << ArgInfo.Name;
Diag(ArgInfo.NameLoc, diag::note_missing_selector_name) << ArgInfo.Name;
Diag(ColonLoc, diag::note_force_empty_selector_name) << ArgInfo.Name;
}
}
// We have a selector or a colon, continue parsing.
}
bool isVariadic = false;
bool cStyleParamWarned = false;
// Parse the (optional) parameter list.
while (Tok.is(tok::comma)) {
ConsumeToken();
if (Tok.is(tok::ellipsis)) {
isVariadic = true;
ConsumeToken();
break;
}
if (!cStyleParamWarned) {
Diag(Tok, diag::warn_cstyle_param);
cStyleParamWarned = true;
}
DeclSpec DS(AttrFactory);
ParseDeclarationSpecifiers(DS);
// Parse the declarator.
Declarator ParmDecl(DS, Declarator::PrototypeContext);
ParseDeclarator(ParmDecl);
IdentifierInfo *ParmII = ParmDecl.getIdentifier();
Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDecl);
CParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII,
ParmDecl.getIdentifierLoc(),
Param,
nullptr));
}
// FIXME: Add support for optional parameter list...
// If attributes exist after the method, parse them.
if (getLangOpts().ObjC2)
MaybeParseGNUAttributes(methodAttrs);
if (KeyIdents.size() == 0)
return nullptr;
Selector Sel = PP.getSelectorTable().getSelector(KeyIdents.size(),
&KeyIdents[0]);
Decl *Result
= Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(),
mType, DSRet, ReturnType,
KeyLocs, Sel, &ArgInfos[0],
CParamInfo.data(), CParamInfo.size(),
methodAttrs.getList(),
MethodImplKind, isVariadic, MethodDefinition);
PD.complete(Result);
return Result;
}
/// objc-protocol-refs:
/// '<' identifier-list '>'
///
bool Parser::
ParseObjCProtocolReferences(SmallVectorImpl<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 (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
SkipUntil(tok::greater, StopAtSemi);
return true;
}
ProtocolIdents.push_back(std::make_pair(Tok.getIdentifierInfo(),
Tok.getLocation()));
ProtocolLocs.push_back(Tok.getLocation());
ConsumeToken();
if (!TryConsumeToken(tok::comma))
break;
}
// Consume the '>'.
if (ParseGreaterThanInTemplateList(EndLoc, consumeLastToken,
/*ObjCGenericList=*/false))
return true;
// Convert the list of protocols identifiers into a list of protocol decls.
Actions.FindProtocolDeclaration(WarnOnDeclarations, ForObjCContainer,
ProtocolIdents, Protocols);
return false;
}
TypeResult Parser::parseObjCProtocolQualifierType(SourceLocation &rAngleLoc) {
assert(Tok.is(tok::less) && "Protocol qualifiers start with '<'");
assert(getLangOpts().ObjC1 && "Protocol qualifiers only exist in Objective-C");
SourceLocation lAngleLoc;
SmallVector<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(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 syntactically matched a type argument, so commit to parsing
// type arguments.
// Convert the identifiers into type arguments.
bool invalid = false;
for (unsigned i = 0, n = identifiers.size(); i != n; ++i) {
ParsedType typeArg
= Actions.getTypeName(*identifiers[i], identifierLocs[i], getCurScope());
if (typeArg) {
DeclSpec DS(AttrFactory);
const char *prevSpec = nullptr;
unsigned diagID;
DS.SetTypeSpecType(TST_typename, identifierLocs[i], prevSpec, diagID,
typeArg, Actions.getASTContext().getPrintingPolicy());
// Form a declarator to turn this into a type.
Declarator D(DS, Declarator::TypeNameContext);
TypeResult fullTypeArg = Actions.ActOnTypeName(getCurScope(), D);
if (fullTypeArg.isUsable())
typeArgs.push_back(fullTypeArg.get());
else
invalid = true;
} else {
invalid = true;
}
}
// Continue parsing type-names.
do {
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());
} else {
invalid = true;
}
} while (TryConsumeToken(tok::comma));
// Parse the closing '>'.
SourceLocation rAngleLoc;
(void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken,
/*ObjCGenericList=*/true);
if (invalid) {
typeArgs.clear();
return;
}
// Record left/right angle locations.
typeArgsLAngleLoc = lAngleLoc;
typeArgsRAngleLoc = rAngleLoc;
}
void Parser::parseObjCTypeArgsAndProtocolQualifiers(
ParsedType baseType,
SourceLocation &typeArgsLAngleLoc,
SmallVectorImpl<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);
// 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);
// 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(), nullptr);
}
/// objc-class-instance-variables:
/// '{' objc-instance-variable-decl-list[opt] '}'
///
/// objc-instance-variable-decl-list:
/// objc-visibility-spec
/// objc-instance-variable-decl ';'
/// ';'
/// objc-instance-variable-decl-list objc-visibility-spec
/// objc-instance-variable-decl-list objc-instance-variable-decl ';'
/// objc-instance-variable-decl-list ';'
///
/// objc-visibility-spec:
/// @private
/// @protected
/// @public
/// @package [OBJC2]
///
/// objc-instance-variable-decl:
/// struct-declaration
///
void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
tok::ObjCKeywordKind visibility,
SourceLocation atLoc) {
assert(Tok.is(tok::l_brace) && "expected {");
SmallVector<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);
HelperActionsForIvarDeclarations(interfaceDecl, atLoc,
T, AllIvarDecls, true);
return;
default:
Diag(Tok, diag::err_objc_illegal_visibility_spec);
continue;
}
}
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(),
Sema::PCC_ObjCInstanceVariableList);
return cutOffParsing();
}
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
// Install the declarator into the interface decl.
FD.D.setObjCIvar(true);
Decl *Field = Actions.ActOnIvar(
getCurScope(), FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D,
FD.BitfieldSize, visibility);
Actions.ActOnObjCContainerFinishDefinition();
if (Field)
AllIvarDecls.push_back(Field);
FD.complete(Field);
};
// Parse all the comma separated declarators.
ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, ObjCIvarCallback);
if (Tok.is(tok::semi)) {
ConsumeToken();
} else {
Diag(Tok, diag::err_expected_semi_decl_list);
// Skip to end of block or statement
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
}
}
HelperActionsForIvarDeclarations(interfaceDecl, atLoc,
T, AllIvarDecls, false);
return;
}
/// 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 (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier; // missing protocol name.
return nullptr;
}
// Save the protocol name, then consume it.
IdentifierInfo *protocolName = Tok.getIdentifierInfo();
SourceLocation nameLoc = ConsumeToken();
if (TryConsumeToken(tok::semi)) { // forward declaration of one protocol.
IdentifierLocPair ProtoInfo(protocolName, nameLoc);
return Actions.ActOnForwardProtocolDeclaration(AtLoc, ProtoInfo,
attrs.getList());
}
CheckNestedObjCContexts(AtLoc);
if (Tok.is(tok::comma)) { // list of forward declarations.
SmallVector<IdentifierLocPair, 8> ProtocolRefs;
ProtocolRefs.push_back(std::make_pair(protocolName, nameLoc));
// Parse the list of forward declarations.
while (1) {
ConsumeToken(); // the ','
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
SkipUntil(tok::semi);
return nullptr;
}
ProtocolRefs.push_back(IdentifierLocPair(Tok.getIdentifierInfo(),
Tok.getLocation()));
ConsumeToken(); // the identifier
if (Tok.isNot(tok::comma))
break;
}
// Consume the ';'.
if (ExpectAndConsume(tok::semi, diag::err_expected_after, "@protocol"))
return nullptr;
return Actions.ActOnForwardProtocolDeclaration(AtLoc, ProtocolRefs,
attrs.getList());
}
// Last, and definitely not least, parse a protocol declaration.
SourceLocation LAngleLoc, EndProtoLoc;
SmallVector<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.getList());
ParseObjCInterfaceDeclList(tok::objc_protocol, ProtoType);
return Actions.ConvertDeclToDeclGroup(ProtoType);
}
/// objc-implementation:
/// objc-class-implementation-prologue
/// objc-category-implementation-prologue
///
/// objc-class-implementation-prologue:
/// @implementation identifier objc-superclass[opt]
/// objc-class-instance-variables[opt]
///
/// objc-category-implementation-prologue:
/// @implementation identifier ( identifier )
Parser::DeclGroupPtrTy
Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
assert(Tok.isObjCAtKeyword(tok::objc_implementation) &&
"ParseObjCAtImplementationDeclaration(): Expected @implementation");
CheckNestedObjCContexts(AtLoc);
ConsumeToken(); // the "implementation" identifier
// Code completion after '@implementation'.
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCImplementationDecl(getCurScope());
cutOffParsing();
return nullptr;
}
MaybeSkipAttributes(tok::objc_implementation);
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected)
<< tok::identifier; // missing class or category name.
return nullptr;
}
// 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);
} else {
// We have a class implementation
SourceLocation superClassLoc;
IdentifierInfo *superClassId = nullptr;
if (TryConsumeToken(tok::colon)) {
// We have a super class
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected)
<< tok::identifier; // missing super class name.
return nullptr;
}
superClassId = Tok.getIdentifierInfo();
superClassLoc = ConsumeToken(); // Consume super class name
}
ObjCImpDecl = Actions.ActOnStartClassImplementation(
AtLoc, nameId, nameLoc,
superClassId, superClassLoc);
if (Tok.is(tok::l_brace)) // we have ivars
ParseObjCClassInstanceVariables(ObjCImpDecl, tok::objc_private, AtLoc);
else if (Tok.is(tok::less)) { // we have illegal '<' try to recover
Diag(Tok, diag::err_unexpected_protocol_qualifier);
SourceLocation protocolLAngleLoc, protocolRAngleLoc;
SmallVector<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);
MaybeParseMicrosoftAttributes(attrs);
if (DeclGroupPtrTy DGP = ParseExternalDeclaration(attrs)) {
DeclGroupRef DG = DGP.get();
DeclsInGroup.append(DG.begin(), DG.end());
}
}
}
return Actions.ActOnFinishObjCImplementation(ObjCImpDecl, DeclsInGroup);
}
Parser::DeclGroupPtrTy
Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) {
assert(Tok.isObjCAtKeyword(tok::objc_end) &&
"ParseObjCAtEndDeclaration(): Expected @end");
ConsumeToken(); // the "end" identifier
if (CurParsedObjCImpl)
CurParsedObjCImpl->finish(atEnd);
else
// missing @implementation
Diag(atEnd.getBegin(), diag::err_expected_objc_container);
return nullptr;
}
Parser::ObjCImplParsingDataRAII::~ObjCImplParsingDataRAII() {
if (!Finished) {
finish(P.Tok.getLocation());
if (P.isEofOrEom()) {
P.Diag(P.Tok, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(P.Tok.getLocation(), "\n@end\n");
P.Diag(Dcl->getLocStart(), diag::note_objc_container_start)
<< Sema::OCK_Implementation;
}
}
P.CurParsedObjCImpl = nullptr;
assert(LateParsedObjCMethods.empty());
}
void Parser::ObjCImplParsingDataRAII::finish(SourceRange AtEnd) {
assert(!Finished);
P.Actions.DefaultSynthesizeProperties(P.getCurScope(), Dcl);
for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i)
P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i],
true/*Methods*/);
P.Actions.ActOnAtEnd(P.getCurScope(), AtEnd);
if (HasCFunction)
for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i)
P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i],
false/*c-functions*/);
/// \brief Clear and free the cached objc methods.
for (LateParsedObjCMethodContainer::iterator
I = LateParsedObjCMethods.begin(),
E = LateParsedObjCMethods.end(); I != E; ++I)
delete *I;
LateParsedObjCMethods.clear();
Finished = true;
}
/// compatibility-alias-decl:
/// @compatibility_alias alias-name class-name ';'
///
Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) {
assert(Tok.isObjCAtKeyword(tok::objc_compatibility_alias) &&
"ParseObjCAtAliasDeclaration(): Expected @compatibility_alias");
ConsumeToken(); // consume compatibility_alias
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
return nullptr;
}
IdentifierInfo *aliasId = Tok.getIdentifierInfo();
SourceLocation aliasLoc = ConsumeToken(); // consume alias-name
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
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 (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
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 (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
SkipUntil(tok::semi);
return nullptr;
}
IdentifierInfo *propertyId = Tok.getIdentifierInfo();
SourceLocation propertyLoc = ConsumeToken(); // consume property name
Actions.ActOnPropertyImplDecl(
getCurScope(), atLoc, propertyLoc, false,
propertyId, nullptr, SourceLocation(),
isClassProperty ? ObjCPropertyQueryKind::OBJC_PR_query_class :
ObjCPropertyQueryKind::OBJC_PR_query_unknown);
if (Tok.isNot(tok::comma))
break;
ConsumeToken(); // consume ','
}
ExpectAndConsume(tok::semi, diag::err_expected_after, "@dynamic");
return nullptr;
}
/// objc-throw-statement:
/// throw expression[opt];
///
StmtResult Parser::ParseObjCThrowStmt(SourceLocation atLoc) {
ExprResult Res;
ConsumeToken(); // consume throw
if (Tok.isNot(tok::semi)) {
Res = ParseExpression();
if (Res.isInvalid()) {
SkipUntil(tok::semi);
return StmtError();
}
}
// consume ';'
ExpectAndConsume(tok::semi, diag::err_expected_after, "@throw");
return Actions.ActOnObjCAtThrowStmt(atLoc, Res.get(), getCurScope());
}
/// objc-synchronized-statement:
/// @synchronized '(' expression ')' compound-statement
///
StmtResult
Parser::ParseObjCSynchronizedStmt(SourceLocation atLoc) {
ConsumeToken(); // consume synchronized
if (Tok.isNot(tok::l_paren)) {
Diag(Tok, diag::err_expected_lparen_after) << "@synchronized";
return StmtError();
}
// The operand is surrounded with parentheses.
ConsumeParen(); // '('
ExprResult operand(ParseExpression());
if (Tok.is(tok::r_paren)) {
ConsumeParen(); // ')'
} else {
if (!operand.isInvalid())
Diag(Tok, diag::err_expected) << tok::r_paren;
// Skip forward until we see a left brace, but don't consume it.
SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch);
}
// Require a compound statement.
if (Tok.isNot(tok::l_brace)) {
if (!operand.isInvalid())
Diag(Tok, diag::err_expected) << tok::l_brace;
return StmtError();
}
// Check the @synchronized operand now.
if (!operand.isInvalid())
operand = Actions.ActOnObjCAtSynchronizedOperand(atLoc, operand.get());
// Parse the compound statement within a new scope.
ParseScope bodyScope(this, Scope::DeclScope);
StmtResult body(ParseCompoundStatementBody());
bodyScope.Exit();
// If there was a semantic or parse error earlier with the
// operand, fail now.
if (operand.isInvalid())
return StmtError();
if (body.isInvalid())
body = Actions.ActOnNullStmt(Tok.getLocation());
return Actions.ActOnObjCAtSynchronizedStmt(atLoc, operand.get(), body.get());
}
/// objc-try-catch-statement:
/// @try compound-statement objc-catch-list[opt]
/// @try compound-statement objc-catch-list[opt] @finally compound-statement
///
/// objc-catch-list:
/// @catch ( parameter-declaration ) compound-statement
/// objc-catch-list @catch ( catch-parameter-declaration ) compound-statement
/// catch-parameter-declaration:
/// parameter-declaration
/// '...' [OBJC2]
///
StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) {
bool catch_or_finally_seen = false;
ConsumeToken(); // consume try
if (Tok.isNot(tok::l_brace)) {
Diag(Tok, diag::err_expected) << tok::l_brace;
return StmtError();
}
StmtVector CatchStmts;
StmtResult FinallyStmt;
ParseScope TryScope(this, Scope::DeclScope);
StmtResult TryBody(ParseCompoundStatementBody());
TryScope.Exit();
if (TryBody.isInvalid())
TryBody = Actions.ActOnNullStmt(Tok.getLocation());
while (Tok.is(tok::at)) {
// At this point, we need to lookahead to determine if this @ is the start
// of an @catch or @finally. We don't want to consume the @ token if this
// is an @try or @encode or something else.
Token AfterAt = GetLookAheadToken(1);
if (!AfterAt.isObjCAtKeyword(tok::objc_catch) &&
!AfterAt.isObjCAtKeyword(tok::objc_finally))
break;
SourceLocation AtCatchFinallyLoc = ConsumeToken();
if (Tok.isObjCAtKeyword(tok::objc_catch)) {
Decl *FirstPart = nullptr;
ConsumeToken(); // consume catch
if (Tok.is(tok::l_paren)) {
ConsumeParen();
ParseScope CatchScope(this, Scope::DeclScope|Scope::AtCatchScope);
if (Tok.isNot(tok::ellipsis)) {
DeclSpec DS(AttrFactory);
ParseDeclarationSpecifiers(DS);
Declarator ParmDecl(DS, Declarator::ObjCCatchContext);
ParseDeclarator(ParmDecl);
// Inform the actions module about the declarator, so it
// gets added to the current scope.
FirstPart = Actions.ActOnObjCExceptionDecl(getCurScope(), ParmDecl);
} else
ConsumeToken(); // consume '...'
SourceLocation RParenLoc;
if (Tok.is(tok::r_paren))
RParenLoc = ConsumeParen();
else // Skip over garbage, until we get to ')'. Eat the ')'.
SkipUntil(tok::r_paren, StopAtSemi);
StmtResult CatchBody(true);
if (Tok.is(tok::l_brace))
CatchBody = ParseCompoundStatementBody();
else
Diag(Tok, diag::err_expected) << tok::l_brace;
if (CatchBody.isInvalid())
CatchBody = Actions.ActOnNullStmt(Tok.getLocation());
StmtResult Catch = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc,
RParenLoc,
FirstPart,
CatchBody.get());
if (!Catch.isInvalid())
CatchStmts.push_back(Catch.get());
} else {
Diag(AtCatchFinallyLoc, diag::err_expected_lparen_after)
<< "@catch clause";
return StmtError();
}
catch_or_finally_seen = true;
} else {
assert(Tok.isObjCAtKeyword(tok::objc_finally) && "Lookahead confused?");
ConsumeToken(); // consume finally
ParseScope FinallyScope(this, Scope::DeclScope);
StmtResult FinallyBody(true);
if (Tok.is(tok::l_brace))
FinallyBody = ParseCompoundStatementBody();
else
Diag(Tok, diag::err_expected) << tok::l_brace;
if (FinallyBody.isInvalid())
FinallyBody = Actions.ActOnNullStmt(Tok.getLocation());
FinallyStmt = Actions.ActOnObjCAtFinallyStmt(AtCatchFinallyLoc,
FinallyBody.get());
catch_or_finally_seen = true;
break;
}
}
if (!catch_or_finally_seen) {
Diag(atLoc, diag::err_missing_catch_finally);
return StmtError();
}
return Actions.ActOnObjCAtTryStmt(atLoc, TryBody.get(),
CatchStmts,
FinallyStmt.get());
}
/// objc-autoreleasepool-statement:
/// @autoreleasepool compound-statement
///
StmtResult
Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) {
ConsumeToken(); // consume autoreleasepool
if (Tok.isNot(tok::l_brace)) {
Diag(Tok, diag::err_expected) << tok::l_brace;
return StmtError();
}
// Enter a scope to hold everything within the compound stmt. Compound
// statements can always hold declarations.
ParseScope BodyScope(this, Scope::DeclScope);
StmtResult AutoreleasePoolBody(ParseCompoundStatementBody());
BodyScope.Exit();
if (AutoreleasePoolBody.isInvalid())
AutoreleasePoolBody = Actions.ActOnNullStmt(Tok.getLocation());
return Actions.ActOnObjCAutoreleasePoolStmt(atLoc,
AutoreleasePoolBody.get());
}