diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -937,6 +937,51 @@ if ( identifier == thisQId && currentContext()->parentContext() && currentContext()->parentContext()->type() == DUContext::Class ) { + + // checks if imports \ArrayAccess + ClassDeclaration* currentClass = dynamic_cast(currentContext()->parentContext()->owner()); + ClassDeclaration* arrayAccess = nullptr; + + auto imports = currentContext()->parentContext()->importedParentContexts(); + for( const DUContext::Import& ctx : imports ) { + DUContext* import = ctx.context(topContext()); + if(import->type() == DUContext::Class) { + ClassDeclaration* importedClass = dynamic_cast(import->owner()); + if(importedClass) { + if(importedClass->prettyName().str() == "ArrayAccess" && importedClass->classType() == ClassDeclarationData::ClassType::Interface && !import->parentContext()->owner()) { + arrayAccess = importedClass; + } + } + } + } + + IntegralType* thisVar = static_cast(type.data()); + // check if this is used as array + if(arrayAccess && currentClass && thisVar && thisVar->dataType() == AbstractType::TypeArray) + { + uint noOfFunc = 0; + auto declarations = currentContext()->parentContext()->localDeclarations(); + // check if class implements all 4 functions + for(auto &dec : declarations) { + if(dec->isFunctionDeclaration()) { + QualifiedIdentifier func = dec->qualifiedIdentifier(); + QString funcname = func.last().identifier().str(); + if(funcname == "offsetexists" || funcname == "offsetget" || funcname == "offsetset" || funcname == "offsetunset") { + noOfFunc++; + } + } + } + + if(noOfFunc < 4) { + // check if class is not abstract + if(currentClass->classModifier() != ClassDeclarationData::ClassModifier::Abstract) { + reportError(i18n("Class %1 contains %2 abstract methods and must therefore be declared abstract or implement the remaining methods.",currentClass->prettyName().str(),4-noOfFunc), QList() << node); + } + } + + return; + } + reportError(i18n("Cannot re-assign $this."), QList() << node); return; } diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h --- a/duchain/tests/duchain.h +++ b/duchain/tests/duchain.h @@ -149,6 +149,8 @@ void bug296709(); void declareFinalMethod(); void testTodoExtractor(); + void useThisAsArray(); + void wrongUseOfThisAsArray(); }; } diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp --- a/duchain/tests/duchain.cpp +++ b/duchain/tests/duchain.cpp @@ -2954,3 +2954,47 @@ QCOMPARE(top->problems().at(1)->description(), QString("FIXME blub")); QCOMPARE(top->problems().at(1)->range(), RangeInRevision(2, 4, 2, 14)); } + +void TestDUChain::useThisAsArray() +{ + QByteArray method("values[$offset]; }\n" + " function offsetSet($offset, $value) { $this->values[$offset] = $value; }\n" + " function offsetExists($offset) { return array_key_exists($offset, $this->values); }\n" + " function offsetUnset($offset) { unset($this->values[$offset]); }\n" + " function setTest() { $this['test'] = 'test'; } \n" + " }\n"); + + TopDUContext* top = parse(method); + QVERIFY(top); + DUChainReleaser releaseTop(top); + DUChainWriteLocker lock(DUChain::lock()); + + QCOMPARE(top->importedParentContexts().count(), 1); + QVERIFY(DUChain::self()->chainForDocument(internalFunctionFile())); + QCOMPARE(DUChain::self()->chainForDocument(internalFunctionFile()), top->importedParentContexts().first().context(top)); + + QVERIFY(top->problems().isEmpty()); +} + +void TestDUChain::wrongUseOfThisAsArray() +{ + // missing functions from \ArrayAccess and not declared abstract + QByteArray method("problems().size(),1); +}