summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShashwat Dixit <shashwatdixit124@gmail.com>2017-03-01 10:44:38 (GMT)
committerAlexander Zhigalin <alexander@zhigalin.tk>2017-03-01 10:45:09 (GMT)
commit1f15b3b33a8bcc25dbb2e714b63a50e16d38761c (patch)
tree26b79e7332bf87a693a8efad8e5163094aa0d3fc
parent5b596c0a47909f3e17a9a0bf504b4cbfae67fb49 (diff)
support for $this as an array when implementing ArrayAccess
Summary: Accessing $this as an array if interface ArrayAccess is implemented [[ https://bugs.kde.org/show_bug.cgi?id=373414 | bug373414 ]] / [[ https://bugs.kde.org/show_bug.cgi?id=346876 | bug346876 ]] Reviewers: #kdevelop, mwolff, zhigalin, kfunk Reviewed By: #kdevelop, zhigalin, kfunk Subscribers: kdevelop-devel Tags: #kdevelop Differential Revision: https://phabricator.kde.org/D4776
-rw-r--r--duchain/builders/declarationbuilder.cpp45
-rw-r--r--duchain/tests/duchain.cpp44
-rw-r--r--duchain/tests/duchain.h2
3 files changed, 91 insertions, 0 deletions
diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp
index 1d7e054..8b3d843 100644
--- a/duchain/builders/declarationbuilder.cpp
+++ b/duchain/builders/declarationbuilder.cpp
@@ -937,6 +937,51 @@ void DeclarationBuilder::declareVariable(DUContext* parentCtx, AbstractType::Ptr
if ( identifier == thisQId
&& currentContext()->parentContext()
&& currentContext()->parentContext()->type() == DUContext::Class ) {
+
+ // checks if imports \ArrayAccess
+ ClassDeclaration* currentClass = dynamic_cast<ClassDeclaration*>(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<ClassDeclaration*>(import->owner());
+ if(importedClass) {
+ if(importedClass->prettyName().str() == "ArrayAccess" && importedClass->classType() == ClassDeclarationData::ClassType::Interface && !import->parentContext()->owner()) {
+ arrayAccess = importedClass;
+ }
+ }
+ }
+ }
+
+ IntegralType* thisVar = static_cast<IntegralType*>(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<AstNode*>() << node);
+ }
+ }
+
+ return;
+ }
+
reportError(i18n("Cannot re-assign $this."), QList<AstNode*>() << node);
return;
}
diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp
index bc9684f..d294f00 100644
--- a/duchain/tests/duchain.cpp
+++ b/duchain/tests/duchain.cpp
@@ -2954,3 +2954,47 @@ void Php::TestDUChain::testTodoExtractor()
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("<?php\n"
+ " interface ArrayAccess{} "
+ " class A implements \\ArrayAccess\n"
+ " {\n"
+ " $values = [];\n"
+ " function offsetGet($offset) { return $this->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("<?php\n"
+ " interface ArrayAccess{} "
+ " class A implements \\ArrayAccess\n"
+ " {\n"
+ " public function setTest() { $this['test'] = 'test'; } \n"
+ " }\n");
+
+ TopDUContext* top = parse(method);
+ QVERIFY(top);
+ DUChainReleaser releaseTop(top);
+ DUChainWriteLocker lock(DUChain::lock());
+
+ QCOMPARE(top->problems().size(),1);
+}
diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h
index c9fe0a5..194bada 100644
--- a/duchain/tests/duchain.h
+++ b/duchain/tests/duchain.h
@@ -149,6 +149,8 @@ private slots:
void bug296709();
void declareFinalMethod();
void testTodoExtractor();
+ void useThisAsArray();
+ void wrongUseOfThisAsArray();
};
}