Commit f1e66546 authored by Erik Verbruggen's avatar Erik Verbruggen

Added protocol & class definedness checks, and added property attribute checks.

parent b43f2792
......@@ -51,6 +51,7 @@ void CheckUndefinedSymbols::setGlobalNamespaceBinding(NamespaceBindingPtr global
{
_globalNamespaceBinding = globalNamespaceBinding;
_types.clear();
_protocols.clear();
if (_globalNamespaceBinding) {
QSet<NamespaceBinding *> processed;
......@@ -130,6 +131,20 @@ void CheckUndefinedSymbols::addType(Name *name)
_types.insert(QByteArray(id->chars(), id->size()));
}
void CheckUndefinedSymbols::addProtocol(Name *name)
{
if (!name)
return;
if (Identifier *id = name->identifier())
_protocols.insert(QByteArray(id->chars(), id->size()));
}
bool CheckUndefinedSymbols::isProtocol(const QByteArray &name) const
{
return _protocols.contains(name);
}
void CheckUndefinedSymbols::buildTypeMap(Class *klass)
{
addType(klass->name());
......@@ -186,9 +201,9 @@ void CheckUndefinedSymbols::buildTypeMap(NamespaceBinding *binding, QSet<Namespa
for (unsigned i = 0; i < klass->memberCount(); ++i)
buildMemberTypeMap(klass->memberAt(i));
} else if (ObjCForwardProtocolDeclaration *fProto = member->asObjCForwardProtocolDeclaration()) {
addType(fProto->name());
addProtocol(fProto->name());
} else if (ObjCProtocol *proto = member->asObjCProtocol()) {
addType(proto->name());
addProtocol(proto->name());
for (unsigned i = 0; i < proto->memberCount(); ++i)
buildMemberTypeMap(proto->memberAt(i));
......@@ -466,3 +481,53 @@ bool CheckUndefinedSymbols::visit(SizeofExpressionAST *ast)
return true;
}
bool CheckUndefinedSymbols::visit(ObjCClassDeclarationAST *ast)
{
if (NameAST *nameAST = ast->superclass) {
bool resolvedSuperClassName = false;
if (Name *name = nameAST->name) {
Identifier *id = name->identifier();
const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
if (isType(spell))
resolvedSuperClassName = true;
}
if (! resolvedSuperClassName) {
translationUnit()->warning(nameAST->firstToken(),
"expected class-name after ':' token");
}
}
return true;
}
bool CheckUndefinedSymbols::visit(ObjCProtocolRefsAST *ast)
{
for (IdentifierListAST *iter = ast->identifier_list; iter; iter = iter->next) {
if (NameAST *nameAST = iter->name) {
bool resolvedProtocolName = false;
if (Name *name = nameAST->name) {
Identifier *id = name->identifier();
const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
if (isProtocol(spell))
resolvedProtocolName = true;
}
if (!resolvedProtocolName) {
char after;
if (iter == ast->identifier_list)
after = '<';
else
after = ',';
translationUnit()->warning(nameAST->firstToken(), "expected protocol name after '%c' token", after);
}
}
}
return false;
}
......@@ -59,6 +59,8 @@ protected:
void buildTypeMap(Class *klass);
void buildMemberTypeMap(Symbol *member);
void buildTypeMap(NamespaceBinding *binding, QSet<NamespaceBinding *> *processed);
void addProtocol(Name *name);
bool isProtocol(const QByteArray &name) const;
FunctionDeclaratorAST *currentFunctionDeclarator() const;
CompoundStatementAST *compoundStatement() const;
......@@ -92,6 +94,9 @@ protected:
virtual bool visit(CastExpressionAST *ast);
virtual bool visit(SizeofExpressionAST *ast);
virtual bool visit(ObjCClassDeclarationAST *ast);
virtual bool visit(ObjCProtocolRefsAST *ast);
private:
Document::Ptr _doc;
NamespaceBindingPtr _globalNamespaceBinding;
......@@ -100,6 +105,7 @@ private:
QList<TemplateDeclarationAST *> _templateDeclarationStack;
QList<CompoundStatementAST *> _compoundStatementStack;
QSet<QByteArray> _types;
QSet<QByteArray> _protocols;
QSet<QByteArray> _namespaceNames;
};
......
......@@ -2359,6 +2359,8 @@ unsigned ObjCMethodPrototypeAST::lastToken() const
{
if (attributes)
return attributes->lastToken();
else if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
else if (arguments)
return arguments->lastToken();
else if (type_name)
......
......@@ -2994,6 +2994,7 @@ public:
ObjCTypeNameAST *type_name;
ObjCSelectorAST *selector;
ObjCMessageArgumentDeclarationListAST *arguments;
unsigned dot_dot_dot_token;
SpecifierAST *attributes;
public: // annotations
......
......@@ -137,6 +137,8 @@ class ForwardClassDeclaration;
class Token;
// Objective-C symbols
class ObjCBaseClass;
class ObjCBaseProtocol;
class ObjCClass;
class ObjCForwardClassDeclaration;
class ObjCProtocol;
......
......@@ -57,6 +57,7 @@
#include "Control.h"
#include "Literals.h"
#include <cassert>
#include <QtCore/QByteArray>
CPLUSPLUS_BEGIN_NAMESPACE
......@@ -506,11 +507,25 @@ bool CheckDeclaration::visit(ObjCProtocolDeclarationAST *ast)
ObjCProtocol *protocol = control()->newObjCProtocol(sourceLocation, protocolName);
protocol->setStartOffset(tokenAt(ast->firstToken()).offset);
protocol->setEndOffset(tokenAt(ast->lastToken()).offset);
ast->symbol = protocol;
if (ast->protocol_refs && ast->protocol_refs->identifier_list) {
for (IdentifierListAST *iter = ast->protocol_refs->identifier_list; iter; iter = iter->next) {
NameAST* name = iter->name;
Name *protocolName = semantic()->check(name, _scope);
ObjCBaseProtocol *baseProtocol = control()->newObjCBaseProtocol(name->firstToken(), protocolName);
protocol->addProtocol(baseProtocol);
}
}
int previousObjCVisibility = semantic()->switchObjCVisibility(Function::Public);
for (DeclarationListAST *it = ast->member_declarations; it; it = it->next) {
semantic()->check(it->declaration, protocol->members());
}
(void) semantic()->switchObjCVisibility(previousObjCVisibility);
ast->symbol = protocol;
_scope->enterSymbol(protocol);
// TODO EV: walk protocols and method prototypes
return false;
}
......@@ -562,7 +577,21 @@ bool CheckDeclaration::visit(ObjCClassDeclarationAST *ast)
klass->setCategoryName(categoryName);
}
// TODO: super-class, and protocols (EV)
if (ast->superclass) {
Name *superClassName = semantic()->check(ast->superclass, _scope);
ObjCBaseClass *superKlass = control()->newObjCBaseClass(ast->superclass->firstToken(), superClassName);
klass->setBaseClass(superKlass);
}
if (ast->protocol_refs && ast->protocol_refs->identifier_list) {
for (IdentifierListAST *iter = ast->protocol_refs->identifier_list; iter; iter = iter->next) {
NameAST* name = iter->name;
Name *protocolName = semantic()->check(name, _scope);
ObjCBaseProtocol *baseProtocol = control()->newObjCBaseProtocol(name->firstToken(), protocolName);
klass->addProtocol(baseProtocol);
}
}
_scope->enterSymbol(klass);
int previousObjCVisibility = semantic()->switchObjCVisibility(Function::Protected);
......@@ -627,4 +656,86 @@ bool CheckDeclaration::visit(ObjCVisibilityDeclarationAST *ast)
return false;
}
enum PropertyAttributes {
None = 0,
Assign = 1 << 0,
Retain = 1 << 1,
Copy = 1 << 2,
ReadOnly = 1 << 3,
ReadWrite = 1 << 4,
Getter = 1 << 5,
Setter = 1 << 6,
NonAtomic = 1 << 7,
WritabilityMask = ReadOnly | ReadWrite,
SetterSemanticsMask = Assign | Retain | Copy,
};
bool CheckDeclaration::checkPropertyAttribute(ObjCPropertyAttributeAST *attrAst,
int &flags,
int attr)
{
if (flags & attr) {
translationUnit()->warning(attrAst->attribute_identifier_token,
"duplicate property attribute \"%s\"",
spell(attrAst->attribute_identifier_token));
return false;
} else {
flags |= attr;
return true;
}
}
bool CheckDeclaration::visit(ObjCPropertyDeclarationAST *ast)
{
int propAttrs = None;
for (ObjCPropertyAttributeListAST *iter= ast->property_attributes; iter; iter = iter->next) {
ObjCPropertyAttributeAST *attrAst = iter->attr;
if (!attrAst)
continue;
const char *attrName = spell(attrAst->attribute_identifier_token);
if (!qstrcmp("getter", attrName)) {
if (checkPropertyAttribute(attrAst, propAttrs, Getter)) {
// TODO: find method declaration for getter
}
} else if (!qstrcmp("setter", attrName)) {
if (checkPropertyAttribute(attrAst, propAttrs, Setter)) {
// TODO: find method declaration for setter
}
} else if (!qstrcmp("readwrite", attrName)) {
checkPropertyAttribute(attrAst, propAttrs, ReadWrite);
} else if (!qstrcmp("readonly", attrName)) {
checkPropertyAttribute(attrAst, propAttrs, ReadOnly);
} else if (!qstrcmp("assign", attrName)) {
checkPropertyAttribute(attrAst, propAttrs, Assign);
} else if (!qstrcmp("retain", attrName)) {
checkPropertyAttribute(attrAst, propAttrs, Retain);
} else if (!qstrcmp("copy", attrName)) {
checkPropertyAttribute(attrAst, propAttrs, Copy);
} else if (!qstrcmp("nonatomic", attrName)) {
checkPropertyAttribute(attrAst, propAttrs, NonAtomic);
}
}
if (propAttrs & ReadOnly && propAttrs & ReadWrite)
// Should this be an error instead of only a warning?
translationUnit()->warning(ast->property_token,
"property can have at most one attribute \"readonly\" or \"readwrite\" specified");
int setterSemAttrs = propAttrs & SetterSemanticsMask;
if (setterSemAttrs
&& setterSemAttrs != Assign
&& setterSemAttrs != Retain
&& setterSemAttrs != Copy) {
// Should this be an error instead of only a warning?
translationUnit()->warning(ast->property_token,
"property can have at most one attribute \"assign\", \"retain\", or \"copy\" specified");
}
// TODO: Check if the next line is correct (EV)
semantic()->check(ast->simple_declaration, _scope);
return false;
}
CPLUSPLUS_END_NAMESPACE
......@@ -97,7 +97,12 @@ protected:
virtual bool visit(ObjCClassForwardDeclarationAST *ast);
virtual bool visit(ObjCMethodDeclarationAST *ast);
virtual bool visit(ObjCVisibilityDeclarationAST *ast);
virtual bool visit(ObjCPropertyDeclarationAST *ast);
private:
bool checkPropertyAttribute(ObjCPropertyAttributeAST *attrAst,
int &flags,
int attr);
private:
DeclarationAST *_declaration;
Scope *_scope;
......
......@@ -265,14 +265,15 @@ bool CheckDeclarator::visit(ObjCMethodPrototypeAST *ast)
method->setReturnType(returnType);
if (ast->selector && ast->selector->asObjCSelectorWithArguments()) {
// TODO: check the parameters (EV)
// fun->setVariadic(...);
// TODO: add arguments (EV)
for (ObjCMessageArgumentDeclarationListAST *it = ast->arguments; it; it = it->next) {
ObjCMessageArgumentDeclarationAST *argDecl = it->argument_declaration;
semantic()->check(argDecl, method->arguments());
}
if (ast->dot_dot_dot_token)
method->setVariadic(true);
}
_fullySpecifiedType = FullySpecifiedType(method);
......
......@@ -416,8 +416,8 @@ bool CheckSpecifier::visit(AttributeSpecifierAST *ast)
bool CheckSpecifier::visit(ObjCTypeNameAST * /*ast*/)
{
// TODO: implement this (EV)
_fullySpecifiedType = FullySpecifiedType();
return false;
// _fullySpecifiedType = FullySpecifiedType();
return true;
}
CPLUSPLUS_END_NAMESPACE
......@@ -128,6 +128,8 @@ public:
delete_array_entries(enums);
delete_array_entries(usingDeclarations);
delete_array_entries(classForwardDeclarations);
delete_array_entries(objcBaseClasses);
delete_array_entries(objcBaseProtocols);
delete_array_entries(objcClasses);
delete_array_entries(objcProtocols);
delete_array_entries(objcForwardClassDeclarations);
......@@ -348,6 +350,20 @@ public:
return c;
}
ObjCBaseClass *newObjCBaseClass(unsigned sourceLocation, Name *name)
{
ObjCBaseClass *c = new ObjCBaseClass(translationUnit, sourceLocation, name);
objcBaseClasses.push_back(c);
return c;
}
ObjCBaseProtocol *newObjCBaseProtocol(unsigned sourceLocation, Name *name)
{
ObjCBaseProtocol *p = new ObjCBaseProtocol(translationUnit, sourceLocation, name);
objcBaseProtocols.push_back(p);
return p;
}
ObjCClass *newObjCClass(unsigned sourceLocation, Name *name)
{
ObjCClass *c = new ObjCClass(translationUnit, sourceLocation, name);
......@@ -561,6 +577,8 @@ public:
std::vector<Enum *> enums;
std::vector<UsingDeclaration *> usingDeclarations;
std::vector<ForwardClassDeclaration *> classForwardDeclarations;
std::vector<ObjCBaseClass *> objcBaseClasses;
std::vector<ObjCBaseProtocol *> objcBaseProtocols;
std::vector<ObjCClass *> objcClasses;
std::vector<ObjCProtocol *> objcProtocols;
std::vector<ObjCForwardClassDeclaration *> objcForwardClassDeclarations;
......@@ -740,6 +758,12 @@ ForwardClassDeclaration *Control::newForwardClassDeclaration(unsigned sourceLoca
Name *name)
{ return d->newForwardClassDeclaration(sourceLocation, name); }
ObjCBaseClass *Control::newObjCBaseClass(unsigned sourceLocation, Name *name)
{ return d->newObjCBaseClass(sourceLocation, name); }
ObjCBaseProtocol *Control::newObjCBaseProtocol(unsigned sourceLocation, Name *name)
{ return d->newObjCBaseProtocol(sourceLocation, name); }
ObjCClass *Control::newObjCClass(unsigned sourceLocation, Name *name)
{ return d->newObjCClass(sourceLocation, name); }
......
......@@ -166,6 +166,9 @@ public:
/// Creates a new ForwardClassDeclaration symbol.
ForwardClassDeclaration *newForwardClassDeclaration(unsigned sourceLocation, Name *name = 0);
ObjCBaseClass *newObjCBaseClass(unsigned sourceLocation, Name *name);
ObjCBaseProtocol *newObjCBaseProtocol(unsigned sourceLocation, Name *name);
/// Creates a new Objective-C class symbol.
ObjCClass *newObjCClass(unsigned sourceLocation, Name *name = 0);
......
......@@ -51,6 +51,19 @@ static inline int classify3(const char *s) {
return Token_identifier;
}
static inline int classify4(const char *s) {
if (s[0] == 'c') {
if (s[1] == 'o') {
if (s[2] == 'p') {
if (s[3] == 'y') {
return Token_copy;
}
}
}
}
return Token_identifier;
}
static inline int classify5(const char *s) {
if (s[0] == 'b') {
if (s[1] == 'y') {
......@@ -78,7 +91,20 @@ static inline int classify5(const char *s) {
}
static inline int classify6(const char *s) {
if (s[0] == 'b') {
if (s[0] == 'a') {
if (s[1] == 's') {
if (s[2] == 's') {
if (s[3] == 'i') {
if (s[4] == 'g') {
if (s[5] == 'n') {
return Token_assign;
}
}
}
}
}
}
else if (s[0] == 'b') {
if (s[1] == 'y') {
if (s[2] == 'c') {
if (s[3] == 'o') {
......@@ -91,6 +117,32 @@ static inline int classify6(const char *s) {
}
}
}
else if (s[0] == 'g') {
if (s[1] == 'e') {
if (s[2] == 't') {
if (s[3] == 't') {
if (s[4] == 'e') {
if (s[5] == 'r') {
return Token_getter;
}
}
}
}
}
}
else if (s[0] == 's') {
if (s[1] == 'e') {
if (s[2] == 't') {
if (s[3] == 't') {
if (s[4] == 'e') {
if (s[5] == 'r') {
return Token_setter;
}
}
}
}
}
}
else if (s[0] == 'o') {
if (s[1] == 'n') {
if (s[2] == 'e') {
......@@ -104,15 +156,93 @@ static inline int classify6(const char *s) {
}
}
}
else if (s[0] == 'r') {
if (s[1] == 'e') {
if (s[2] == 't') {
if (s[3] == 'a') {
if (s[4] == 'i') {
if (s[5] == 'n') {
return Token_retain;
}
}
}
}
}
}
return Token_identifier;
}
static inline int classify8(const char *s) {
if (s[0] == 'r') {
if (s[1] == 'e') {
if (s[2] == 'a') {
if (s[3] == 'd') {
if (s[4] == 'o') {
if (s[5] == 'n') {
if (s[6] == 'l') {
if (s[7] == 'y') {
return Token_readonly;
}
}
}
}
}
}
}
}
return Token_identifier;
}
static inline int classify9(const char *s) {
if (s[0] == 'n') {
if (s[1] == 'o') {
if (s[2] == 'n') {
if (s[3] == 'a') {
if (s[4] == 't') {
if (s[5] == 'o') {
if (s[6] == 'm') {
if (s[7] == 'i') {
if (s[8] == 'c') {
return Token_nonatomic;
}
}
}
}
}
}
}
}
} else if (s[0] == 'r') {
if (s[1] == 'e') {
if (s[2] == 'a') {
if (s[3] == 'd') {
if (s[4] == 'w') {
if (s[5] == 'r') {
if (s[6] == 'i') {
if (s[7] == 't') {
if (s[8] == 'e') {
return Token_readwrite;
}
}
}
}
}
}
}
}
}
return Token_identifier;
}
int classifyObjectiveCTypeQualifiers(const char *s, int n) {
switch (n) {
case 2: return classify2(s);
case 3: return classify3(s);
case 4: return classify4(s);
case 5: return classify5(s);
case 6: return classify6(s);
case 8: return classify8(s);
case 9: return classify9(s);
default: return Token_identifier;
} // switch
}
......
......@@ -37,10 +37,18 @@ CPLUSPLUS_BEGIN_NAMESPACE
enum {
Token_in,
Token_out,
Token_copy,
Token_byref,
Token_inout,
Token_assign,
Token_bycopy,
Token_getter,
Token_retain,
Token_setter,
Token_oneway,
Token_readonly,
Token_nonatomic,
Token_readwrite,
Token_identifier
};
......
......@@ -4544,7 +4544,13 @@ bool Parser::parseObjCPropertyDeclaration(DeclarationAST *&node, SpecifierAST *a
last->comma_token = consumeToken();
last->next = new (_pool) ObjCPropertyAttributeListAST;
last = last->next;
parseObjCPropertyAttribute(last->attr);
if (!parseObjCPropertyAttribute(last->attr)) {
_translationUnit->error(_tokenIndex, "expected token `%s' got `%s'",
Token::name(T_IDENTIFIER), tok().spell());
while (LA() != T_RPAREN)
consumeToken();
break;
}
}
}
......@@ -4597,15 +4603,15 @@ bool Parser::parseObjCMethodPrototype(ObjCMethodPrototypeAST *&node)
lastArg->argument_declaration = declaration;
}
// TODO EV: get this in the ast
while (LA() == T_COMMA) {
consumeToken();
if (LA() == T_DOT_DOT_DOT) {
consumeToken();
ast->dot_dot_dot_token = consumeToken();
break;
}
// TODO: Is this still valid, and if so, should it be stored in the AST? (EV)
DeclarationAST *parameter_declaration = 0;
parseParameterDeclaration(parameter_declaration);
}
......@@ -4639,28 +4645,43 @@ bool Parser::parseObjCPropertyAttribute(ObjCPropertyAttributeAST *&node)