Skip to content

Commit

Permalink
added more checks and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
i582 committed Jun 10, 2022
1 parent 363a80e commit 25a39b8
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,24 @@ class KphpGenericsInspection : PhpInspection() {
}

override fun visitPhpClass(klass: PhpClass) {
checkInheritTag(klass)
}

private fun checkInheritTag(klass: PhpClass) {
val extendsList = klass.extendsList.referenceElements.mapNotNull { it to (it.resolve() as? PhpClass) }
val implementsList =
klass.implementsList.referenceElements.mapNotNull { it to (it.resolve() as? PhpClass) }

val allParents = extendsList + implementsList

val extendsGenericList = extendsList.filter { it.second?.isGeneric() ?: false }
val implementsGenericList = implementsList.filter { it.second?.isGeneric() ?: false }

val inheritors = extendsGenericList + implementsGenericList

if (inheritors.isEmpty()) {
return
}
val allGenericParents = extendsGenericList + implementsGenericList

val inheritTag = klass.docComment?.getTagElementsByName("@kphp-inherit")
?.firstOrNull() as? KphpDocTagInheritPsiImpl
if (inheritTag == null) {
if (inheritTag == null && allGenericParents.isNotEmpty()) {
holder.registerProblem(
klass.nameIdentifier ?: klass,
"Class extends or implements generic class/interface, please specify @kphp-inherit",
Expand All @@ -82,11 +84,11 @@ class KphpGenericsInspection : PhpInspection() {
return
}

val inherits = inheritTag.types().associateBy { it.className() }
inheritors.forEach { (ref, inheritClass) ->
if (inheritClass == null) return@forEach
val tagParents = inheritTag?.types()?.associateBy { it.className() } ?: emptyMap()
allGenericParents.forEach { (ref, parentCLass) ->
if (parentCLass == null) return@forEach

if (!inherits.containsKey(inheritClass.fqn)) {
if (!tagParents.containsKey(parentCLass.fqn)) {
holder.registerProblem(
ref.element,
"Class extends generic class/interface, but this class not specified in @kphp-inherit",
Expand All @@ -98,6 +100,33 @@ class KphpGenericsInspection : PhpInspection() {
)
}
}

tagParents.forEach { (name, decl) ->
val parent = allParents.find { it.second?.fqn == name }
if (parent == null && decl.className() != null) {
return holder.registerProblem(
decl,
"Class/interface $name not extended or implemented class/interface ${klass.name}",
ProblemHighlightType.GENERIC_ERROR,
RegenerateKphpInheritQuickFix(
SmartPointerManager.getInstance(klass.project).createSmartPsiElementPointer(klass),
needKeepExistent = true,
)
)
}

if (parent?.second?.isGeneric() == false) {
return holder.registerProblem(
decl,
"It is not necessary to specify not generic class/interface $name in @kphp-inherit",
ProblemHighlightType.GENERIC_ERROR,
RegenerateKphpInheritQuickFix(
SmartPointerManager.getInstance(klass.project).createSmartPsiElementPointer(klass),
needKeepExistent = true,
)
)
}
}
}

override fun visitPhpDocType(type: PhpDocType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ class RegenerateKphpInheritQuickFix(
}

removeTagFromDocComment(docComment, "@kphp-inherit")
docComment
.transformToMultiline(project)
.addTag(project, KphpInheritDocTag.nameWithAt, newParents, genericTag)

if (newParents.isNotEmpty()) {
docComment
.transformToMultiline(project)
.addTag(project, KphpInheritDocTag.nameWithAt, newParents, genericTag)
}
}
}
7 changes: 7 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/kphptags/KphpDocTag.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ abstract class KphpDocTag(
this.newAnnotation(HighlightSeverity.ERROR, errorMessage).range(docTag).create()
}

/**
* Helper for use inside annotate() on any error
*/
protected fun AnnotationHolder.error(tagElement: PsiElement, errorMessage: String) {
this.newAnnotation(HighlightSeverity.ERROR, errorMessage).range(tagElement).create()
}

/**
* Helper for use inside annotate() for highlighting parts
*/
Expand Down
13 changes: 13 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/kphptags/KphpInheritDocTag.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package com.vk.kphpstorm.kphptags

import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag
import com.jetbrains.php.lang.highlighter.PhpHighlightingData
import com.jetbrains.php.lang.psi.elements.PhpClass
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeInstancePsiImpl
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeTplInstantiationPsiImpl
import com.vk.kphpstorm.generics.GenericUtil.genericNonDefaultNames
import com.vk.kphpstorm.generics.GenericUtil.genericParents
import com.vk.kphpstorm.kphptags.psi.KphpDocElementTypes
import com.vk.kphpstorm.kphptags.psi.KphpDocInheritParameterDeclPsiImpl
import com.vk.kphpstorm.kphptags.psi.KphpDocTagElementType
import com.vk.kphpstorm.kphptags.psi.KphpDocTagInheritPsiImpl

Expand Down Expand Up @@ -48,5 +52,14 @@ object KphpInheritDocTag : KphpDocTag("@kphp-inherit") {
if (docTag is KphpDocTagInheritPsiImpl) {
holder.highlight(docTag, PhpHighlightingData.DOC_COMMENT)
}

val parameters = PsiTreeUtil.findChildrenOfType(rhs.parent, KphpDocInheritParameterDeclPsiImpl::class.java)
parameters.forEach {
it as KphpDocInheritParameterDeclPsiImpl
val child = it.firstChild
if (child !is ExPhpTypeTplInstantiationPsiImpl && child !is ExPhpTypeInstancePsiImpl) {
holder.error(it.firstChild, "Expected ClassName<Type>, got ${it.firstChild.text}")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import com.intellij.lang.ASTNode
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocElementType
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocRef
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.PhpDocPsiElementImpl
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.PhpDocTypeImpl
import com.vk.kphpstorm.exphptype.ExPhpTypeInstance
import com.vk.kphpstorm.exphptype.ExPhpTypeTplInstantiation
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeInstancePsiImpl
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeTplInstantiationPsiImpl
import com.vk.kphpstorm.generics.GenericUtil.getInstantiation
import com.vk.kphpstorm.helpers.toExPhpType

/**
* Inside '@kphp-inherit ExtendsClass<Type>, ImplementsClass<Type>' — 'ExtendsClass<Type>' and 'ImplementsClass<Type'
Expand All @@ -18,7 +24,16 @@ class KphpDocInheritParameterDeclPsiImpl(node: ASTNode) : PhpDocPsiElementImpl(n
}

fun className(): String? {
val instantiation = findChildByClass(ExPhpTypeTplInstantiationPsiImpl::class.java) ?: return null
return instantiation.type.types.firstOrNull { !it.contains("(") }
val instantiation = findChildByClass(PhpDocTypeImpl::class.java) ?: return null
if (instantiation !is ExPhpTypeTplInstantiationPsiImpl && instantiation !is ExPhpTypeInstancePsiImpl)
return null

val exType = instantiation.type.toExPhpType() ?: return null
if (exType is ExPhpTypeTplInstantiation)
return exType.classFqn
if (exType is ExPhpTypeInstance)
return exType.fqn

return exType.getInstantiation()?.classFqn
}
}
54 changes: 54 additions & 0 deletions src/test/fixtures/generics/general/inherit/tag/main.fixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Inherit\Tag\Main;

/** @kphp-generic T */
class GenericClass {}
/** @kphp-generic T, T2 */
interface GenericInterface {}

class NonGenericClass {}
interface NonGenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit GenericClass<T>
*/
class Foo1 extends GenericClass {}

/**
* @kphp-generic T
* @kphp-inherit GenericInterface<T, T>
*/
class Foo2 implements GenericInterface {}

class <error descr="Class extends or implements generic class/interface, please specify @kphp-inherit">Foo3</error> implements GenericInterface {}

/**
* @kphp-generic T
*/
class <error descr="Class extends or implements generic class/interface, please specify @kphp-inherit">Foo4</error> implements GenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit GenericInterface<T, T>
*/
class Foo5 extends <error descr="Class extends generic class/interface, but this class not specified in @kphp-inherit">GenericClass</error> implements GenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit <error descr="Class/interface \Inherit\Tag\Main\GenericClass not extended or implemented class/interface Foo6">GenericClass<T></error>
*/
class Foo6 extends NonGenericClass {}

/**
* @kphp-generic T
* @kphp-inherit <error descr="Class/interface \Inherit\Tag\Main\GenericInterface not extended or implemented class/interface Foo7">GenericInterface<T, T></error>
*/
class Foo7 implements NonGenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit <error descr="It is not necessary to specify not generic class/interface \Inherit\Tag\Main\NonGenericInterface in @kphp-inherit">NonGenericInterface</error>
*/
class Foo8 implements NonGenericInterface {}
56 changes: 56 additions & 0 deletions src/test/fixtures/generics/general/inherit/tag/main.qf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Inherit\Tag\Main;

/** @kphp-generic T */
class GenericClass {}
/** @kphp-generic T, T2 */
interface GenericInterface {}

class NonGenericClass {}
interface NonGenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit GenericClass<T>
*/
class Foo1 extends GenericClass {}

/**
* @kphp-generic T
* @kphp-inherit GenericInterface<T, T>
*/
class Foo2 implements GenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit GenericInterface<T, T>
*/
class Foo3 implements GenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit GenericInterface<T, T>
*/
class Foo4 implements GenericInterface {}

/**
* @kphp-generic T
* @kphp-inherit GenericClass<T>, GenericInterface<T, T>
*/
class Foo5 extends GenericClass implements GenericInterface {}

/**
* @kphp-generic T
*/
class Foo6 extends NonGenericClass {}

/**
* @kphp-generic T
*/
class Foo7 implements NonGenericInterface {}

/**
* @kphp-generic T
*/
class Foo8 implements NonGenericInterface {}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ class GenericsGeneralTest : GenericTestBase() {
runFixture("generics/general/generic_in_generic.fixture.php")
}

fun testInheritTag() {
runFixture("generics/general/inherit/tag/main.fixture.php")
}

/**
* Disabled.
*
Expand Down

0 comments on commit 25a39b8

Please sign in to comment.