diff --git a/src/libs/cplusplus/BackwardsScanner.cpp b/src/libs/cplusplus/BackwardsScanner.cpp
index 7f1f6cdfe6b9d816a656346e760b6d2ae5ae4c6f..46fb15efe42464c136824656c940f0fd93ad0fc3 100644
--- a/src/libs/cplusplus/BackwardsScanner.cpp
+++ b/src/libs/cplusplus/BackwardsScanner.cpp
@@ -40,6 +40,7 @@ BackwardsScanner::BackwardsScanner(const QTextCursor &cursor, const QString &suf
 {
     _tokenize.setQtMocRunEnabled(true);
     _tokenize.setSkipComments(true);
+    _tokenize.setObjCEnabled(true);
     _text = _block.text().left(cursor.position() - cursor.block().position());
 
     if (! suffix.isEmpty())
diff --git a/src/libs/cplusplus/ExpressionUnderCursor.cpp b/src/libs/cplusplus/ExpressionUnderCursor.cpp
index 995eeace37dc8129dd7001b1a1e29d85144c6b03..67980ce4e127fbd02232e706283e5832f5925f6c 100644
--- a/src/libs/cplusplus/ExpressionUnderCursor.cpp
+++ b/src/libs/cplusplus/ExpressionUnderCursor.cpp
@@ -106,6 +106,8 @@ int ExpressionUnderCursor::startOfExpression_helper(BackwardsScanner &tk, int in
             return startOfExpression(tk, index - 2);
         } else if (tk[index - 2].is(T_DOT_STAR) || tk[index - 2].is(T_ARROW_STAR)) {
             return startOfExpression(tk, index - 2);
+//        } else if (tk[index - 2].is(T_IDENTIFIER) && tk[index - 3].is(T_LBRACKET)) {
+//            return index - 3;
         }
         return index - 1;
     } else if (tk[index - 1].is(T_RPAREN)) {
diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp
index 4d3ed932759f5dee2d490a7c33bd38ce71daaf0a..653d86b0d00fa2a1bbfd561657a1395ce8842539 100644
--- a/src/libs/cplusplus/LookupContext.cpp
+++ b/src/libs/cplusplus/LookupContext.cpp
@@ -89,8 +89,10 @@ bool LookupContext::maybeValidSymbol(Symbol *symbol,
                                      ResolveMode mode,
                                      const QList<Symbol *> &candidates)
 {
-    if (((mode & ResolveNamespace) && symbol->isNamespace()) ||
-        ((mode & ResolveClass)     && symbol->isClass())     ||
+    if (((mode & ResolveNamespace)    && symbol->isNamespace())    ||
+        ((mode & ResolveClass)        && symbol->isClass())        ||
+        ((mode & ResolveObjCClass)    && symbol->isObjCClass())    ||
+        ((mode & ResolveObjCProtocol) && symbol->isObjCProtocol()) ||
          (mode & ResolveSymbol)) {
         return ! candidates.contains(symbol);
     }
@@ -527,6 +529,69 @@ void LookupContext::expandObjCMethod(ObjCMethod *method,
         expandedScopes->append(method->arguments());
 }
 
+void LookupContext::expandObjCClass(ObjCClass *klass,
+                                    const QList<Scope *> &visibleScopes,
+                                    QList<Scope *> *expandedScopes) const
+{
+    {// expand other @interfaces, @implementations and categories for this class:
+        const QList<Symbol *> classList = resolveObjCClass(klass->name(), visibleScopes);
+        foreach (Symbol *otherClass, classList) {
+            if (otherClass == klass)
+                continue;
+            expand(otherClass->asObjCClass()->members(), visibleScopes, expandedScopes);
+        }
+    }
+
+    // expand definitions in the currect class:
+    for (unsigned i = 0; i < klass->memberCount(); ++i) {
+        Symbol *symbol = klass->memberAt(i);
+        if (Class *nestedClass = symbol->asClass()) {
+            if (! nestedClass->name()) {
+                expand(nestedClass->members(), visibleScopes, expandedScopes);
+            }
+        } else if (Enum *e = symbol->asEnum()) {
+            expand(e->members(), visibleScopes, expandedScopes);
+        }
+    }
+
+    // expand the base class:
+    if (ObjCBaseClass *baseClass = klass->baseClass()) {
+        Name *baseClassName = baseClass->name();
+        const QList<Symbol *> baseClassCandidates = resolveObjCClass(baseClassName,
+                                                                     visibleScopes);
+
+        for (int j = 0; j < baseClassCandidates.size(); ++j) {
+            if (ObjCClass *baseClassSymbol = baseClassCandidates.at(j)->asObjCClass())
+                expand(baseClassSymbol->members(), visibleScopes, expandedScopes);
+        }
+    }
+
+    // expand the protocols:
+    for (unsigned i = 0; i < klass->protocolCount(); ++i) {
+        Name *protocolName = klass->protocolAt(i)->name();
+        const QList<Symbol *> protocolCandidates = resolveObjCProtocol(protocolName, visibleScopes);
+        for (int j = 0; j < protocolCandidates.size(); ++j) {
+            if (ObjCProtocol *protocolSymbol = protocolCandidates.at(j)->asObjCProtocol())
+                expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes);
+        }
+    }
+}
+
+void LookupContext::expandObjCProtocol(ObjCProtocol *protocol, const QList<Scope *> &visibleScopes, QList<Scope *> *expandedScopes) const
+{
+    // First expand the protocol itself
+    expand(protocol->members(), visibleScopes, expandedScopes);
+
+    // Then do the same for any incorporated protocol
+    for (unsigned i = 0; i < protocol->protocolCount(); ++i) {
+        ObjCBaseProtocol *baseProtocol = protocol->protocolAt(i);
+        const QList<Symbol *> protocolList = resolveObjCProtocol(baseProtocol->name(), visibleScopes);
+        foreach (Symbol *symbol, protocolList)
+            if (ObjCProtocol *protocolSymbol = symbol->asObjCProtocol())
+                expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes);
+    }
+}
+
 void LookupContext::expand(Scope *scope,
                            const QList<Scope *> &visibleScopes,
                            QList<Scope *> *expandedScopes) const
@@ -546,6 +611,8 @@ void LookupContext::expand(Scope *scope,
         expandFunction(fun, visibleScopes, expandedScopes);
     } else if (ObjCMethod *meth = scope->owner()->asObjCMethod()) {
         expandObjCMethod(meth, visibleScopes, expandedScopes);
+    } else if (ObjCClass *objcKlass = scope->owner()->asObjCClass()) {
+        expandObjCClass(objcKlass, visibleScopes, expandedScopes);
     }
 }
 
diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h
index e9d84c83aeccd1fe8f7a053134ed2d222cb03c7d..6a69b3330bb71fd65fc6cd1dbd70ae728b532d58 100644
--- a/src/libs/cplusplus/LookupContext.h
+++ b/src/libs/cplusplus/LookupContext.h
@@ -75,12 +75,20 @@ public:
     QList<Symbol *> resolveClassOrNamespace(Name *name) const
     { return resolveClassOrNamespace(name, visibleScopes()); }
 
+    QList<Symbol *> resolveObjCClass(Name *name) const
+    { return resolveObjCClass(name, visibleScopes()); }
+
+    QList<Symbol *> resolveObjCProtocol(Name *name) const
+    { return resolveObjCProtocol(name, visibleScopes()); }
+
     enum ResolveMode {
         ResolveSymbol           = 0x01,
         ResolveClass            = 0x02,
         ResolveNamespace        = 0x04,
         ResolveClassOrNamespace = ResolveClass  | ResolveNamespace,
-        ResolveAll              = ResolveSymbol | ResolveClassOrNamespace
+        ResolveObjCClass        = 0x08,
+        ResolveObjCProtocol     = 0x10,
+        ResolveAll              = ResolveSymbol | ResolveClassOrNamespace | ResolveObjCClass | ResolveObjCProtocol
     };
 
     QList<Symbol *> resolve(Name *name, const QList<Scope *> &visibleScopes,
@@ -95,6 +103,12 @@ public:
     QList<Symbol *> resolveClassOrNamespace(Name *name, const QList<Scope *> &visibleScopes) const
     { return resolve(name, visibleScopes, ResolveClassOrNamespace); }
 
+    QList<Symbol *> resolveObjCClass(Name *name, const QList<Scope *> &visibleScopes) const
+    { return resolve(name, visibleScopes, ResolveObjCClass); }
+
+    QList<Symbol *> resolveObjCProtocol(Name *name, const QList<Scope *> &visibleScopes) const
+    { return resolve(name, visibleScopes, ResolveObjCProtocol); }
+
     QList<Scope *> visibleScopes() const
     { return _visibleScopes; }
 
@@ -128,6 +142,14 @@ public:
                           const QList<Scope *> &visibleScopes,
                           QList<Scope *> *expandedScopes) const;
 
+    void expandObjCClass(ObjCClass *klass,
+                         const QList<Scope *> &visibleScopes,
+                         QList<Scope *> *expandedScopes) const;
+
+    void expandObjCProtocol(ObjCProtocol *protocol,
+                            const QList<Scope *> &visibleScopes,
+                            QList<Scope *> *expandedScopes) const;
+
     void expandEnumOrAnonymousSymbol(ScopedSymbol *scopedSymbol,
                                      QList<Scope *> *expandedScopes) const;
 
diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp
index 9b8dceebd3778be5e5e30f7a20029dd49c82804a..ba6952e7bff965264877261bff33ace8ba2ab696 100644
--- a/src/libs/cplusplus/ResolveExpression.cpp
+++ b/src/libs/cplusplus/ResolveExpression.cpp
@@ -738,11 +738,67 @@ ResolveExpression::resolveMember(Name *memberName, Class *klass,
 }
 
 
+QList<ResolveExpression::Result>
+ResolveExpression::resolveMember(Name *memberName, ObjCClass *klass) const
+{
+    QList<Result> results;
+
+    if (!memberName || !klass)
+        return results;
+
+    QList<Scope *> scopes;
+    _context.expand(klass->members(), _context.visibleScopes(), &scopes);
+
+    QList<Symbol *> candidates = _context.resolve(memberName, scopes);
+
+    foreach (Symbol *candidate, candidates) {
+        FullySpecifiedType ty = candidate->type();
+
+        results.append(Result(ty, candidate));
+    }
+
+    return removeDuplicates(results);
+}
+
 bool ResolveExpression::visit(PostIncrDecrAST *)
 {
     return false;
 }
 
+bool ResolveExpression::visit(ObjCMessageExpressionAST *ast)
+{
+    QList<Result> receiverResults = operator()(ast->receiver_expression);
+
+    if (!receiverResults.isEmpty()) {
+        Result result = receiverResults.first();
+        FullySpecifiedType ty = result.first.simplified();
+        Name *klassName = 0;
+
+        if (const ObjCClass *classTy = ty->asObjCClassType()) {
+            // static access, e.g.:
+            // [NSObject description];
+            klassName = classTy->name();
+        } else if (const PointerType *ptrTy = ty->asPointerType()) {
+            const FullySpecifiedType pointeeTy = ptrTy->elementType();
+            if (pointeeTy && pointeeTy->isNamedType()) {
+                // dynamic access, e.g.:
+                // NSObject *obj = ...; [obj release];
+                klassName = pointeeTy->asNamedType()->name();
+            }
+        }
+
+        if (klassName&&ast->selector && ast->selector->selector_name) {
+            ResolveObjCClass resolveObjCClass;
+            QList<Symbol *> resolvedSymbols = resolveObjCClass(klassName, result, _context);
+            foreach (Symbol *resolvedSymbol, resolvedSymbols)
+                if (ObjCClass *klass = resolvedSymbol->asObjCClass())
+                    _results.append(resolveMember(ast->selector->selector_name, klass));
+        }
+    }
+
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 ResolveClass::ResolveClass()
 { }
@@ -811,3 +867,35 @@ QList<Symbol *> ResolveClass::resolveClass(Name *name,
 
     return resolvedSymbols;
 }
+
+ResolveObjCClass::ResolveObjCClass()
+{}
+
+QList<Symbol *> ResolveObjCClass::operator ()(Name *name,
+                                              const ResolveExpression::Result &p,
+                                              const LookupContext &context)
+{
+    QList<Symbol *> resolvedSymbols;
+
+    const QList<Symbol *> candidates =
+            context.resolve(name, context.visibleScopes(p));
+
+    foreach (Symbol *candidate, candidates) {
+        if (ObjCClass *klass = candidate->asObjCClass()) {
+            if (resolvedSymbols.contains(klass))
+                continue; // we already know about `klass'
+            resolvedSymbols.append(klass);
+        } else if (candidate->isTypedef()) {
+            if (Declaration *decl = candidate->asDeclaration()) {
+                if (decl->type()->isObjCClassType()) {
+                    ObjCClass *klass = decl->type()->asObjCClassType();
+                    if (resolvedSymbols.contains(klass))
+                        continue;
+                    resolvedSymbols.append(klass);
+                }
+            }
+        }
+    }
+
+    return resolvedSymbols;
+}
diff --git a/src/libs/cplusplus/ResolveExpression.h b/src/libs/cplusplus/ResolveExpression.h
index 34541e9f45eb3f03b93d74093358505f78d68d5a..adda2716c369b1e0c292fc4d03070c527114a5f1 100644
--- a/src/libs/cplusplus/ResolveExpression.h
+++ b/src/libs/cplusplus/ResolveExpression.h
@@ -61,6 +61,8 @@ public:
     QList<Result> resolveMember(Name *memberName, Class *klass,
                                 Name *className = 0) const;
 
+    QList<Result> resolveMember(Name *memberName, ObjCClass *klass) const;
+
 protected:
     QList<Result> switchResults(const QList<Result> &symbols);
 
@@ -109,6 +111,9 @@ protected:
     virtual bool visit(PostIncrDecrAST *ast);
     virtual bool visit(MemberAccessAST *ast);
 
+    // Objective-C expressions
+    virtual bool visit(ObjCMessageExpressionAST *ast);
+
     QList<Scope *> visibleScopes(const Result &result) const;
 
 private:
@@ -136,6 +141,16 @@ private:
     QList<ResolveExpression::Result> _blackList;
 };
 
+class CPLUSPLUS_EXPORT ResolveObjCClass
+{
+public:
+    ResolveObjCClass();
+
+    QList<Symbol *> operator()(Name *name,
+                               const ResolveExpression::Result &p,
+                               const LookupContext &context);
+};
+
 
 } // end of namespace CPlusPlus
 
diff --git a/src/shared/cplusplus/CheckDeclaration.cpp b/src/shared/cplusplus/CheckDeclaration.cpp
index 0d1546234fbff830dfe0d753f0fbc07a91d23734..728e87385d836d1f1ffb7546c4942d66f72898a7 100644
--- a/src/shared/cplusplus/CheckDeclaration.cpp
+++ b/src/shared/cplusplus/CheckDeclaration.cpp
@@ -662,15 +662,13 @@ bool CheckDeclaration::visit(ObjCMethodDeclarationAST *ast)
         Declaration *decl = control()->newDeclaration(ast->firstToken(), methodType->name());
         decl->setType(methodType);
         symbol = decl;
+        symbol->setStorage(methodType->storage());
     }
 
     symbol->setStartOffset(tokenAt(ast->firstToken()).offset);
     symbol->setEndOffset(tokenAt(ast->lastToken()).offset);
     symbol->setVisibility(semantic()->currentVisibility());
 
-    if (semantic()->isObjCClassMethod(ast->method_prototype->method_type_token))
-        symbol->setStorage(Symbol::Static);
-
     _scope->enterSymbol(symbol);
 
     return false;
diff --git a/src/shared/cplusplus/CheckDeclarator.cpp b/src/shared/cplusplus/CheckDeclarator.cpp
index 844cd9a5c71b0e816acbea342c0c0930c381bf2c..9f283811059c4ad9c33e97d23872836fd4c5a255 100644
--- a/src/shared/cplusplus/CheckDeclarator.cpp
+++ b/src/shared/cplusplus/CheckDeclarator.cpp
@@ -246,21 +246,32 @@ bool CheckDeclarator::visit(ReferenceAST *)
 
 bool CheckDeclarator::visit(ObjCMethodPrototypeAST *ast)
 {
+    if (!ast)
+        return false;
+
+    if (!ast->selector) {
+        // TODO: (EV) this currently happens when parsing:
+        //   + (id<NSSomeProtocol>) zoo;
+        // where the parser will start doing template magic. We'll need to disambiguate this case.
+        return false;
+    }
+
     FullySpecifiedType returnType = semantic()->check(ast->type_name, _scope);
 
     unsigned location = ast->firstToken();
 
-    Name *name = semantic()->check(ast->selector, _scope);
+    semantic()->check(ast->selector, _scope);
 
-    ObjCMethod *method = control()->newObjCMethod(location, name);
+    ObjCMethod *method = control()->newObjCMethod(location, ast->selector->selector_name);
     ast->symbol = method;
     method->setSourceLocation(location);
     method->setScope(_scope);
     method->setVisibility(semantic()->currentVisibility());
     method->setReturnType(returnType);
+    if (semantic()->isObjCClassMethod(tokenKind(ast->method_type_token)))
+        method->setStorage(Symbol::Static);
 
     if (ast->selector && ast->selector->asObjCSelectorWithArguments()) {
-        // TODO: add arguments (EV)
         for (ObjCMessageArgumentDeclarationListAST *it = ast->argument_list; it; it = it->next) {
             ObjCMessageArgumentDeclarationAST *argDecl = it->value;
 
@@ -273,8 +284,6 @@ bool CheckDeclarator::visit(ObjCMethodPrototypeAST *ast)
 
     _fullySpecifiedType = FullySpecifiedType(method);
 
-    // TODO: check which specifiers are allowed here (EV)
-
     return false;
 }
 
diff --git a/src/shared/cplusplus/CheckExpression.cpp b/src/shared/cplusplus/CheckExpression.cpp
index 1c97bc91f135210d87c69823520931d2a8ccd957..01259d3c2cfdf52e0639477005251c5bbe2d1a52 100644
--- a/src/shared/cplusplus/CheckExpression.cpp
+++ b/src/shared/cplusplus/CheckExpression.cpp
@@ -368,7 +368,7 @@ bool CheckExpression::visit(MemberAccessAST *ast)
 
 bool CheckExpression::visit(ObjCMessageExpressionAST *ast)
 {
-    semantic()->check(ast->receiver_expression, _scope);
+    (void) semantic()->check(ast->receiver_expression, _scope);
     (void) semantic()->check(ast->selector, _scope);
 
     accept(ast->argument_list); // ### not necessary.
diff --git a/src/shared/cplusplus/CheckName.cpp b/src/shared/cplusplus/CheckName.cpp
index d3a9c2b2446e65022ba5cad98c653aab44f2ae3a..6476b3295f16cee1c56c984c0425a7d026248084 100644
--- a/src/shared/cplusplus/CheckName.cpp
+++ b/src/shared/cplusplus/CheckName.cpp
@@ -379,8 +379,9 @@ bool CheckName::visit(TemplateIdAST *ast)
 bool CheckName::visit(ObjCSelectorWithoutArgumentsAST *ast)
 {
     std::vector<Name *> names;
-    Identifier *id = identifier(ast->name_token);
-    names.push_back(control()->nameId(id));
+    Identifier *id = control()->findOrInsertIdentifier(spell(ast->name_token));
+    NameId *nameId = control()->nameId(id);
+    names.push_back(nameId);
     _name = control()->selectorNameId(&names[0], names.size(), false);
     ast->selector_name = _name;
 
@@ -391,10 +392,9 @@ bool CheckName::visit(ObjCSelectorWithArgumentsAST *ast)
 {
     std::vector<Name *> names;
     for (ObjCSelectorArgumentListAST *it = ast->selector_argument_list; it; it = it->next) {
-        Identifier *id =  identifier(it->value->name_token);
-        Name *name = control()->nameId(id);
-
-        names.push_back(name);
+        Identifier *id = control()->findOrInsertIdentifier(spell(it->value->name_token));
+        NameId *nameId = control()->nameId(id);
+        names.push_back(nameId);
     }
 
     if (!names.empty()) {
diff --git a/src/shared/cplusplus/Names.cpp b/src/shared/cplusplus/Names.cpp
index 9e60804e77b908968bb59d469c726807f3a4dd80..973e8d8366a976bc18213a7c61731757c3460294 100644
--- a/src/shared/cplusplus/Names.cpp
+++ b/src/shared/cplusplus/Names.cpp
@@ -289,7 +289,9 @@ void SelectorNameId::accept0(NameVisitor *visitor)
 
 Identifier *SelectorNameId::identifier() const
 {
-    // FIXME: (EV)
+    if (! _nameCount)
+        return 0;
+
     return nameAt(0)->identifier();
 }
 
diff --git a/src/shared/cplusplus/Scope.cpp b/src/shared/cplusplus/Scope.cpp
index 16026f8060a9d8b149e158ca988153bcdfcc03c7..2c4eea10f71b1d55e72ead00adf78efc1e41f05a 100644
--- a/src/shared/cplusplus/Scope.cpp
+++ b/src/shared/cplusplus/Scope.cpp
@@ -245,6 +245,9 @@ Symbol *Scope::lookat(Identifier *id) const
                 break;
         } else if (identity->isQualifiedNameId()) {
             assert(0);
+        } else if (SelectorNameId *selectorNameId = identity->asSelectorNameId()) {
+            if (selectorNameId->identifier()->isEqualTo(id))
+                break;
         }
     }
     return symbol;
diff --git a/src/shared/cplusplus/Semantic.cpp b/src/shared/cplusplus/Semantic.cpp
index 134e6c594a1c8468f0cbe261de342e028d0346f3..8baf10d4b09218fc92bce568c08ce864c0674035 100644
--- a/src/shared/cplusplus/Semantic.cpp
+++ b/src/shared/cplusplus/Semantic.cpp
@@ -232,9 +232,7 @@ bool Semantic::isObjCClassMethod(int tokenKind) const
     case T_PLUS:
         return true;
     case T_MINUS:
-        return false;
     default:
-        // TODO EV: assert here?
         return false;
     }
 }
diff --git a/src/shared/cplusplus/Symbol.cpp b/src/shared/cplusplus/Symbol.cpp
index c04df48bafe68caa89d4d42a077c44bc16a8d372..6cd6573aa6a1974aa24ef768b55d2ae142116de5 100644
--- a/src/shared/cplusplus/Symbol.cpp
+++ b/src/shared/cplusplus/Symbol.cpp
@@ -104,6 +104,9 @@ protected:
     virtual void visit(QualifiedNameId *name)
     { _value = operator()(name->unqualifiedNameId()); }
 
+    virtual void visit(SelectorNameId *name)
+    { _value = name->identifier()->hashCode(); }
+
 private:
     unsigned _value;
 };
@@ -151,6 +154,9 @@ protected:
     virtual void visit(QualifiedNameId *name)
     { _identity = name->unqualifiedNameId(); }
 
+    virtual void visit(SelectorNameId *name)
+    { _identity = name; }
+
 private:
     Name *_identity;
 };
@@ -461,6 +467,12 @@ bool Symbol::isArgument() const
 bool Symbol::isBaseClass() const
 { return asBaseClass() != 0; }
 
+bool Symbol::isObjCBaseClass() const
+{ return asObjCBaseClass() != 0; }
+
+bool Symbol::isObjCBaseProtocol() const
+{ return asObjCBaseProtocol() != 0; }
+
 bool Symbol::isObjCClass() const
 { return asObjCClass() != 0; }
 
diff --git a/tests/auto/cplusplus/ast/tst_ast.cpp b/tests/auto/cplusplus/ast/tst_ast.cpp
index 86eda5befe4d6685d7f77ce637e34d38033c219c..dab517287b874c9891dbdfd38dc980e6c2b3526a 100644
--- a/tests/auto/cplusplus/ast/tst_ast.cpp
+++ b/tests/auto/cplusplus/ast/tst_ast.cpp
@@ -70,6 +70,7 @@ private slots:
     void assignment_2();
 
     // objc++
+    void objc_simple_class();
     void objc_attributes_followed_by_at_keyword();
     void objc_protocol_forward_declaration_1();
     void objc_protocol_definition_1();
@@ -512,6 +513,20 @@ void tst_AST::cpp_initializer_or_function_declaration()
     QCOMPARE(param->type_specifier_list->value->asNamedTypeSpecifier()->name->asSimpleName()->identifier_token, 4U);
 }
 
+void tst_AST::objc_simple_class()
+{
+    QSharedPointer<TranslationUnit> unit(parseDeclaration("\n"
+                                                          "@interface Zoo {} +(id)alloc;-(id)init;@end\n"
+                                                          "@implementation Zoo\n"
+                                                          "+(id)alloc{}\n"
+                                                          "-(id)init{}\n"
+                                                          "@end\n"
+                                                          ));
+
+    AST *ast = unit->ast();
+    QVERIFY(ast);
+}
+
 void tst_AST::objc_attributes_followed_by_at_keyword()
 {
     QSharedPointer<TranslationUnit> unit(parseDeclaration("\n"
diff --git a/tests/auto/cplusplus/lookup/tst_lookup.cpp b/tests/auto/cplusplus/lookup/tst_lookup.cpp
index 8b64c58395de4a89a02d8e47a8f6558f498f58ab..abccca356825f553448771c3ed661a37fcd9edc6 100644
--- a/tests/auto/cplusplus/lookup/tst_lookup.cpp
+++ b/tests/auto/cplusplus/lookup/tst_lookup.cpp
@@ -6,7 +6,10 @@
 #include <ASTVisitor.h>
 #include <TranslationUnit.h>
 #include <CppDocument.h>
+#include <Literals.h>
 #include <LookupContext.h>
+#include <Name.h>
+#include <ResolveExpression.h>
 #include <Symbols.h>
 #include <Overview.h>
 
@@ -55,6 +58,11 @@ class tst_Lookup: public QObject
 
 private Q_SLOTS:
     void base_class_defined_1();
+
+    // Objective-C
+    void simple_class_1();
+    void class_with_baseclass();
+    void class_with_protocol_with_protocol();
 };
 
 void tst_Lookup::base_class_defined_1()
@@ -110,5 +118,204 @@ void tst_Lookup::base_class_defined_1()
     QVERIFY(classToAST.value(derivedClass) != 0);
 }
 
+void tst_Lookup::simple_class_1()
+{
+    const QByteArray source = "\n"
+        "@interface Zoo {} +(id)alloc; -(id)init; @end\n"
+        "@implementation Zoo +(id)alloc{} -(id)init{} -(void)dealloc{} @end\n";
+
+    Document::Ptr doc = Document::create("simple_class_1");
+    doc->setSource(source);
+    doc->parse();
+    doc->check();
+
+    QVERIFY(doc->diagnosticMessages().isEmpty());
+    QCOMPARE(doc->globalSymbolCount(), 2U);
+
+    Snapshot snapshot;
+    snapshot.insert(doc->fileName(), doc);
+
+    Document::Ptr emptyDoc = Document::create("<empty>");
+
+    ObjCClass *iface = doc->globalSymbolAt(0)->asObjCClass();
+    QVERIFY(iface);
+    QVERIFY(iface->isInterface());
+    QCOMPARE(iface->memberCount(), 2U);
+
+    ObjCClass *impl = doc->globalSymbolAt(1)->asObjCClass();
+    QVERIFY(impl);
+    QVERIFY(!impl->isInterface());
+    QCOMPARE(impl->memberCount(), 3U);
+
+    ObjCMethod *allocMethod = impl->memberAt(0)->asObjCMethod();
+    QVERIFY(allocMethod);
+    QVERIFY(allocMethod->name() && allocMethod->name()->identifier());
+    QCOMPARE(QLatin1String(allocMethod->name()->identifier()->chars()), QLatin1String("alloc"));
+
+    ObjCMethod *deallocMethod = impl->memberAt(2)->asObjCMethod();
+    QVERIFY(deallocMethod);
+    QVERIFY(deallocMethod->name() && deallocMethod->name()->identifier());
+    QCOMPARE(QLatin1String(deallocMethod->name()->identifier()->chars()), QLatin1String("dealloc"));
+
+    const LookupContext ctxt(impl, emptyDoc, doc, snapshot);
+
+    // check class resolving:
+    const QList<Symbol *> candidates = ctxt.resolveObjCClass(impl->name());
+    QCOMPARE(candidates.size(), 2);
+    QVERIFY(candidates.contains(iface));
+    QVERIFY(candidates.contains(impl));
+
+    // check scope expansion:
+    QList<Scope *> expandedScopes;
+    ctxt.expand(impl->members(), ctxt.visibleScopes(), &expandedScopes);
+    QCOMPARE(expandedScopes.size(), 2);
+
+    const ResolveExpression resolver(ctxt);
+
+    // check method resolving:
+    QList<ResolveExpression::Result> results = resolver.resolveMember(allocMethod->name(), impl);
+    QCOMPARE(results.size(), 2);
+    QVERIFY(results.at(0).second == allocMethod || results.at(1).second == allocMethod);
+    QVERIFY(results.at(0).second->asDeclaration() || results.at(1).second->asDeclaration());
+
+    results = resolver.resolveMember(deallocMethod->name(), impl);
+    QCOMPARE(results.size(), 1);
+    QCOMPARE(results.at(0).second, deallocMethod);
+}
+
+void tst_Lookup::class_with_baseclass()
+{
+    const QByteArray source = "\n"
+                              "@implementation BaseZoo {} -(void)baseDecl; -(void)baseMethod{} @end\n"
+                              "@interface Zoo: BaseZoo {} +(id)alloc; -(id)init; @end\n"
+                              "@implementation Zoo +(id)alloc{} -(id)init{} -(void)dealloc{} @end\n";
+
+    Document::Ptr doc = Document::create("class_with_baseclass");
+    doc->setSource(source);
+    doc->parse();
+    doc->check();
+
+    QVERIFY(doc->diagnosticMessages().isEmpty());
+    QCOMPARE(doc->globalSymbolCount(), 3U);
+
+    Snapshot snapshot;
+    snapshot.insert(doc->fileName(), doc);
+
+    Document::Ptr emptyDoc = Document::create("<empty>");
+
+    ObjCClass *baseZoo = doc->globalSymbolAt(0)->asObjCClass();
+    QVERIFY(baseZoo);
+    QVERIFY(!baseZoo->isInterface());
+    QCOMPARE(baseZoo->memberCount(), 2U);
+
+    ObjCClass *zooIface = doc->globalSymbolAt(1)->asObjCClass();
+    QVERIFY(zooIface);
+    QVERIFY(zooIface->isInterface());
+    QVERIFY(zooIface->baseClass()->name() == baseZoo->name());
+
+    ObjCClass *zooImpl = doc->globalSymbolAt(2)->asObjCClass();
+    QVERIFY(zooImpl);
+    QVERIFY(!zooImpl->isInterface());
+    QCOMPARE(zooImpl->memberCount(), 3U);
+
+    Declaration *baseDecl = baseZoo->memberAt(0)->asDeclaration();
+    QVERIFY(baseDecl);
+    QVERIFY(baseDecl->name() && baseDecl->name()->identifier());
+    QCOMPARE(QLatin1String(baseDecl->name()->identifier()->chars()), QLatin1String("baseDecl"));
+
+    ObjCMethod *baseMethod = baseZoo->memberAt(1)->asObjCMethod();
+    QVERIFY(baseMethod);
+    QVERIFY(baseMethod->name() && baseMethod->name()->identifier());
+    QCOMPARE(QLatin1String(baseMethod->name()->identifier()->chars()), QLatin1String("baseMethod"));
+
+    const LookupContext ctxt(zooImpl, emptyDoc, doc, snapshot);
+
+    const QList<Symbol *> candidates = ctxt.resolveObjCClass(baseZoo->name());
+    QCOMPARE(candidates.size(), 1);
+    QVERIFY(candidates.contains(baseZoo));
+
+    QList<Scope *> expandedScopes;
+    ctxt.expand(zooImpl->members(), ctxt.visibleScopes(), &expandedScopes);
+    QCOMPARE(expandedScopes.size(), 3);
+
+    const ResolveExpression resolver(ctxt);
+
+    QList<ResolveExpression::Result> results = resolver.resolveMember(baseDecl->name(), zooImpl);
+    QCOMPARE(results.size(), 1);
+    QCOMPARE(results.at(0).second, baseDecl);
+
+    results = resolver.resolveMember(baseMethod->name(), zooImpl);
+    QCOMPARE(results.size(), 1);
+    QCOMPARE(results.at(0).second, baseMethod);
+}
+
+void tst_Lookup::class_with_protocol_with_protocol()
+{
+    const QByteArray source = "\n"
+                              "@protocol P1 -(void)p1method; @end\n"
+                              "@protocol P2 <P1> -(void)p2method; @end\n"
+                              "@interface Zoo <P2> {} +(id)alloc; -(id)init; @end\n"
+                              "@implementation Zoo +(id)alloc{} -(id)init{} -(void)dealloc{} @end\n";
+
+    Document::Ptr doc = Document::create("class_with_protocol_with_protocol");
+    doc->setSource(source);
+    doc->parse();
+    doc->check();
+
+    QVERIFY(doc->diagnosticMessages().isEmpty());
+    QCOMPARE(doc->globalSymbolCount(), 4U);
+
+    Snapshot snapshot;
+    snapshot.insert(doc->fileName(), doc);
+
+    Document::Ptr emptyDoc = Document::create("<empty>");
+
+    ObjCProtocol *P1 = doc->globalSymbolAt(0)->asObjCProtocol();
+    QVERIFY(P1);
+    QCOMPARE(P1->memberCount(), 1U);
+    QCOMPARE(P1->protocolCount(), 0U);
+
+    Declaration *p1method = P1->memberAt(0)->asDeclaration();
+    QVERIFY(p1method);
+    QCOMPARE(QLatin1String(p1method->name()->identifier()->chars()), QLatin1String("p1method"));
+
+    ObjCProtocol *P2 = doc->globalSymbolAt(1)->asObjCProtocol();
+    QVERIFY(P2);
+    QCOMPARE(P2->memberCount(), 1U);
+    QCOMPARE(P2->protocolCount(), 1U);
+    QCOMPARE(QLatin1String(P2->protocolAt(0)->name()->identifier()->chars()), QLatin1String("P1"));
+
+    ObjCClass *zooImpl = doc->globalSymbolAt(3)->asObjCClass();
+    QVERIFY(zooImpl);
+
+    const LookupContext ctxt(zooImpl, emptyDoc, doc, snapshot);
+
+    {
+        const QList<Symbol *> candidates = ctxt.resolveObjCProtocol(P1->name());
+        QCOMPARE(candidates.size(), 1);
+        QVERIFY(candidates.contains(P1));
+    }
+
+    {
+        const QList<Symbol *> candidates = ctxt.resolveObjCProtocol(P2->protocolAt(0)->name());
+        QCOMPARE(candidates.size(), 1);
+        QVERIFY(candidates.contains(P1));
+    }
+
+    QList<Scope *> expandedScopes;
+    ctxt.expand(zooImpl->members(), ctxt.visibleScopes(), &expandedScopes);
+    QCOMPARE(expandedScopes.size(), 4);
+
+    const ResolveExpression resolver(ctxt);
+
+    QList<ResolveExpression::Result> results = resolver.resolveMember(p1method->name(), zooImpl);
+    QCOMPARE(results.size(), 1);
+    QCOMPARE(results.at(0).second, p1method);
+
+    results = resolver.resolveMember(p1method->name(), zooImpl);
+    QCOMPARE(results.size(), 1);
+    QCOMPARE(results.at(0).second, p1method);
+}
+
 QTEST_APPLESS_MAIN(tst_Lookup)
 #include "tst_lookup.moc"
diff --git a/tests/auto/cplusplus/semantic/tst_semantic.cpp b/tests/auto/cplusplus/semantic/tst_semantic.cpp
index b42449e6a0eaaf628be04f728941f0c0d6996604..31b5f208b401a3b3a00e587f2d30b2c73434eedb 100644
--- a/tests/auto/cplusplus/semantic/tst_semantic.cpp
+++ b/tests/auto/cplusplus/semantic/tst_semantic.cpp
@@ -31,11 +31,13 @@ public:
     { control.setDiagnosticClient(&diag); }
 
     TranslationUnit *parse(const QByteArray &source,
-                           TranslationUnit::ParseMode mode)
+                           TranslationUnit::ParseMode mode,
+                           bool enableObjc)
     {
         StringLiteral *fileId = control.findOrInsertStringLiteral("<stdin>");
         TranslationUnit *unit = new TranslationUnit(&control, fileId);
         unit->setSource(source.constData(), source.length());
+        unit->setObjCEnabled(enableObjc);
         unit->parse(mode);
         return unit;
     }
@@ -76,19 +78,24 @@ public:
             : errorCount(0)
         { }
 
-        virtual void report(int, StringLiteral *,
-                            unsigned, unsigned,
-                            const char *, va_list)
-        { ++errorCount; }
+        virtual void report(int level,
+                            StringLiteral *fileName,
+                            unsigned line, unsigned column,
+                            const char *format, va_list ap)
+        {
+            ++errorCount;
+
+            qDebug() << fileName->chars()<<':'<<line<<':'<<column<<' '<<QString().sprintf(format, ap);
+        }
     };
 
     Diagnostic diag;
 
 
-    QSharedPointer<Document> document(const QByteArray &source)
+    QSharedPointer<Document> document(const QByteArray &source, bool enableObjc = false)
     {
         diag.errorCount = 0; // reset the error count.
-        TranslationUnit *unit = parse(source, TranslationUnit::ParseTranlationUnit);
+        TranslationUnit *unit = parse(source, TranslationUnit::ParseTranlationUnit, enableObjc);
         QSharedPointer<Document> doc(new Document(unit));
         doc->check();
         doc->errorCount = diag.errorCount;
@@ -110,6 +117,8 @@ private slots:
     void template_instance_1();
 
     void expression_under_cursor_1();
+
+    void objcClass_1();
 };
 
 void tst_Semantic::function_declaration_1()
@@ -438,5 +447,42 @@ void tst_Semantic::expression_under_cursor_1()
     QCOMPARE(expression, QString("bar"));
 }
 
+void tst_Semantic::objcClass_1()
+{
+    QSharedPointer<Document> doc = document("\n"
+                                            "@interface Zoo {} +(id)alloc;-(id)init;@end\n"
+                                            "@implementation Zoo\n"
+                                            "+(id)alloc{}\n"
+                                            "-(id)init{}\n"
+                                            "-(void)dealloc{}\n"
+                                            "@end\n",
+                                            true);
+
+    QCOMPARE(doc->errorCount, 0U);
+    QCOMPARE(doc->globals->symbolCount(), 2U);
+
+    ObjCClass *iface = doc->globals->symbolAt(0)->asObjCClass();
+    QVERIFY(iface);
+    QVERIFY(iface->isInterface());
+    QCOMPARE(iface->memberCount(), 2U);
+
+    ObjCClass *impl = doc->globals->symbolAt(1)->asObjCClass();
+    QVERIFY(impl);
+    QVERIFY(!impl->isInterface());
+    QCOMPARE(impl->memberCount(), 3U);
+
+    ObjCMethod *allocMethod = impl->memberAt(0)->asObjCMethod();
+    QVERIFY(allocMethod);
+    QVERIFY(allocMethod->name() && allocMethod->name()->identifier());
+    QCOMPARE(QLatin1String(allocMethod->name()->identifier()->chars()), QLatin1String("alloc"));
+    QVERIFY(allocMethod->isStatic());
+
+    ObjCMethod *deallocMethod = impl->memberAt(2)->asObjCMethod();
+    QVERIFY(deallocMethod);
+    QVERIFY(deallocMethod->name() && deallocMethod->name()->identifier());
+    QCOMPARE(QLatin1String(deallocMethod->name()->identifier()->chars()), QLatin1String("dealloc"));
+    QVERIFY(!deallocMethod->isStatic());
+}
+
 QTEST_APPLESS_MAIN(tst_Semantic)
 #include "tst_semantic.moc"