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"