Skip to content

Commit

Permalink
added initial support for class inherit
Browse files Browse the repository at this point in the history
  • Loading branch information
i582 committed Jun 10, 2022
1 parent 25a39b8 commit f7b998d
Show file tree
Hide file tree
Showing 18 changed files with 255 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ class ExPhpTypeInstancePsiImpl(node: ASTNode) : PhpDocTypeImpl(node) {
PhpType().add(text)
}

if (needBeGenericT(text)) {
if (isGenericT()) {
return PhpType().add("%$text")
}

return getType(this, text)
}

private fun needBeGenericT(text: String): Boolean {
fun isGenericT(): Boolean {
val isGenericT = GenericUtil.nameIsGeneric(this, text)

val phpDoc = parent.parent ?: return isGenericT
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/com/vk/kphpstorm/generics/GenericCall.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ abstract class GenericCall(val project: Project) {
abstract val callArgs: Array<PsiElement>
abstract val explicitSpecsPsi: GenericInstantiationPsiCommentImpl?
abstract val argumentsTypes: List<ExPhpType?>
abstract val klass: PhpClass?
protected var contextType: ExPhpType? = null

abstract fun element(): PsiElement
Expand Down Expand Up @@ -103,7 +104,7 @@ abstract class GenericCall(val project: Project) {
// необходимы обв списка для дальнейших инспекций

// В первую очередь, выводим все типы шаблонов из аргументов функции (при наличии)
reifier.reifyAllGenericsT(null, function.parameters, genericNames, argumentsTypes, contextType)
reifier.reifyAllGenericsT(klass, function.parameters, genericNames, argumentsTypes, contextType)
// Далее, выводим все типы шаблонов из явного списка типов (при наличии)
extractor.extractExplicitGenericsT(genericNames(), explicitSpecsPsi)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class GenericConstructorCall(private val call: NewExpression) : GenericCall(call
.filterIsInstance<PhpTypedElement>().map { it.type.global(project).toExPhpType() }
override val explicitSpecsPsi = GenericUtil.findInstantiationComment(call)

private val klass: PhpClass?
override val klass: PhpClass?
private val method: Method?

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class GenericFunctionCall(private val call: FunctionReference) : GenericCall(cal
override val argumentsTypes: List<ExPhpType?> = callArgs
.filterIsInstance<PhpTypedElement>().map { it.type.global(project).toExPhpType() }
override val explicitSpecsPsi = findInstantiationComment(call)
override val klass = null

private val function: Function? = call.resolve() as? Function

Expand Down
14 changes: 10 additions & 4 deletions src/main/kotlin/com/vk/kphpstorm/generics/GenericMethodCall.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.vk.kphpstorm.generics

import com.intellij.psi.PsiElement
import com.jetbrains.php.PhpIndex
import com.jetbrains.php.lang.psi.elements.Method
import com.jetbrains.php.lang.psi.elements.MethodReference
import com.jetbrains.php.lang.psi.elements.PhpClass
import com.jetbrains.php.lang.psi.elements.PhpTypedElement
import com.jetbrains.php.lang.psi.resolve.types.PhpType
import com.vk.kphpstorm.exphptype.ExPhpType
Expand All @@ -19,19 +21,21 @@ class GenericMethodCall(private val call: MethodReference) : GenericCall(call.pr
override val explicitSpecsPsi = GenericUtil.findInstantiationComment(call)

private val method = call.resolve() as? Method
private val klass = method?.containingClass
// TODO
private val containingClass = method?.containingClass
override val klass: PhpClass?

init {
val callType = call.classReference?.type?.global(project)

val classType = PhpType().add(callType).global(project)
val parsed = classType.toExPhpType()

val instantiation = parsed?.getInstantiations()?.firstOrNull {
it.classFqn == klass?.fqn
}
val instantiation = parsed?.getInstantiations()?.firstOrNull()

if (instantiation != null) {
klass = PhpIndex.getInstance(project).getAnyByFQN(instantiation.classFqn).firstOrNull()

val specialization = instantiation.specializationList
val classSpecializationNameMap = mutableMapOf<String, ExPhpType>()
val genericNames = klass?.genericNames() ?: emptyList()
Expand All @@ -43,6 +47,8 @@ class GenericMethodCall(private val call: MethodReference) : GenericCall(call.pr
classSpecializationNameMap.forEach { (name, type) ->
reifier.implicitClassSpecializationNameMap[name] = type
}
} else {
klass = null
}

init()
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/generics/GenericUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import com.jetbrains.php.lang.psi.resolve.types.PhpType
import com.vk.kphpstorm.exphptype.*
import com.vk.kphpstorm.generics.psi.GenericInstantiationPsiCommentImpl
import com.vk.kphpstorm.kphptags.psi.KphpDocGenericParameterDecl
import com.vk.kphpstorm.kphptags.psi.KphpDocInheritParameterDeclPsiImpl
import com.vk.kphpstorm.kphptags.psi.KphpDocTagGenericPsiImpl
import com.vk.kphpstorm.kphptags.psi.KphpDocTagInheritPsiImpl

object GenericUtil {
fun PhpNamedElement.isGeneric() = docComment?.getTagElementsByName("@kphp-generic")?.firstOrNull() != null
Expand Down Expand Up @@ -60,6 +62,15 @@ object GenericUtil {
return extendsList.filter { it.isGeneric() } to implementsList.filter { it.isGeneric() }
}

fun PhpClass.genericInheritInstantiation(className: String): KphpDocInheritParameterDeclPsiImpl? {
val docT = docComment?.getTagElementsByName("@kphp-inherit")?.firstOrNull() as? KphpDocTagInheritPsiImpl
?: return null

return docT.types().find {
it.className() == className
}
}

fun ExPhpType.isGenericPipe(): Boolean {
if (this is ExPhpTypePipe) {
if (this.items.size != 2) return false
Expand Down
35 changes: 35 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/generics/GenericsReifier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import com.jetbrains.php.lang.psi.elements.Parameter
import com.jetbrains.php.lang.psi.elements.PhpClass
import com.jetbrains.php.lang.psi.elements.PhpTypedElement
import com.vk.kphpstorm.exphptype.*
import com.vk.kphpstorm.generics.GenericUtil.genericInheritInstantiation
import com.vk.kphpstorm.generics.GenericUtil.genericNames
import com.vk.kphpstorm.generics.GenericUtil.genericParents
import com.vk.kphpstorm.generics.GenericUtil.getGenericTypeOrSelf
import com.vk.kphpstorm.generics.GenericUtil.getInstantiation
import com.vk.kphpstorm.generics.GenericUtil.isGeneric
Expand Down Expand Up @@ -81,6 +84,38 @@ class GenericsReifier(val project: Project) {
implicitSpecs.add(type)
}
implicitSpecializationNameMap.putAll(implicitClassSpecializationNameMap)

if (klass != null) {
val (extendsList, implementsList) = klass.genericParents()

val parentsList = extendsList + implementsList
parentsList.forEach { parent ->
val extendsName = parent.fqn
val genericNames = parent.genericNames()
val inheritInstantiation = klass.genericInheritInstantiation(extendsName)
if (inheritInstantiation != null) {
val specList = inheritInstantiation.specializationList()

val classSpecializationMap = genericNames.associate {
it.name to it.defaultType
}.toMutableMap()

for (i in 0 until Integer.min(genericNames.size, specList.size)) {
val genericT = genericNames[i]
val spec = specList[i]

classSpecializationMap[genericT.name] = spec
}

classSpecializationMap.forEach classForEach@{ (name, type) ->
if (type == null) {
return@classForEach
}
implicitSpecializationNameMap[name] = type.instantiateGeneric(implicitSpecializationNameMap)
}
}
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ abstract class ResolvingGenericBase(val project: Project) {
return instantiate()
}

protected fun specialization(): Map<String, ExPhpType> {
protected fun specialization(): MutableMap<String, ExPhpType> {
val specialization = specializationList()

val specializationNameMap = mutableMapOf<String, ExPhpType>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ package com.vk.kphpstorm.generics

import com.intellij.openapi.project.Project
import com.jetbrains.php.PhpIndex
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocReturnTag
import com.jetbrains.php.lang.psi.elements.Method
import com.jetbrains.php.lang.psi.elements.Parameter
import com.jetbrains.php.lang.psi.elements.PhpClass
import com.jetbrains.php.lang.psi.elements.impl.PhpClassImpl
import com.jetbrains.php.lang.psi.resolve.types.PhpType
import com.vk.kphpstorm.exphptype.ExPhpTypeGenericsT
import com.vk.kphpstorm.exphptype.ExPhpTypeTplInstantiation
import com.vk.kphpstorm.generics.GenericUtil.genericInheritInstantiation
import com.vk.kphpstorm.generics.GenericUtil.genericNames
import com.vk.kphpstorm.generics.GenericUtil.genericParents
import com.vk.kphpstorm.generics.GenericUtil.getInstantiations
import com.vk.kphpstorm.generics.GenericUtil.isReturnGeneric
import com.vk.kphpstorm.helpers.toExPhpType
import com.vk.kphpstorm.kphptags.psi.KphpDocGenericParameterDecl
import com.vk.kphpstorm.typeProviders.GenericMethodsTypeProvider
import java.lang.Integer.min

class ResolvingGenericMethodCall(project: Project) : ResolvingGenericBase(project) {
override var klass: PhpClass? = null
Expand All @@ -25,10 +30,62 @@ class ResolvingGenericMethodCall(project: Project) : ResolvingGenericBase(projec
override var classGenericType: ExPhpTypeTplInstantiation? = null

override fun instantiate(): PhpType? {
val klass = klass ?: return null

val specializationNameMap = specialization()

val returnTag = method?.docComment?.returnTag ?: return null
val exType = returnTag.type.toExPhpType(project) ?: return null
val (extendsList, implementsList) = klass.genericParents()

val parentsList = extendsList + implementsList
parentsList.forEach { parent ->
val extendsName = parent.fqn
val genericNames = parent.genericNames()
val inheritInstantiation = klass.genericInheritInstantiation(extendsName)
if (inheritInstantiation != null) {
val specList = inheritInstantiation.specializationList()

val classSpecializationMap = genericNames.associate {
it.name to it.defaultType
}.toMutableMap()

for (i in 0 until min(genericNames.size, specList.size)) {
val genericT = genericNames[i]
val spec = specList[i]

classSpecializationMap[genericT.name] = spec
}

classSpecializationMap.forEach classForEach@{ (name, type) ->
if (type == null) {
return@classForEach
}
specializationNameMap[name] = type.instantiateGeneric(specializationNameMap)
}
}
}

val classImpl = klass as PhpClassImpl

var returnTag: PhpDocReturnTag? = null

val ifaces = classImpl.directImplementedInterfaces
ifaces.forEach { iface ->
val method = iface.findMethodByName(method!!.name)
returnTag = method?.docComment?.returnTag ?: return@forEach
}

val classes = listOf(classImpl.superClass)
classes.forEach { parent ->
val method = parent?.findMethodByName(method!!.name)
returnTag = method?.docComment?.returnTag ?: return@forEach
}

val classMethodReturnTag = method?.docComment?.returnTag
if (classMethodReturnTag != null) {
returnTag = classMethodReturnTag
}

val exType = returnTag?.type?.toExPhpType(project) ?: return null
val specializedType = exType.instantiateGeneric(specializationNameMap)

return specializedType.toPhpType()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ object KphpHighlightingData {
val PHPDOC_TAG_KPHP = TextAttributesKey.createTextAttributesKey("PHPDOC_TAG_KPHP", PhpHighlightingData.DOC_TAG)
val PHPDOC_TAG_REGULAR = TextAttributesKey.createTextAttributesKey("PHPDOC_TAG_REGULAR", PhpHighlightingData.DOC_TAG)
val PHPDOC_TYPE_INSIDE = TextAttributesKey.createTextAttributesKey("PHPDOC_TYPE_INSIDE", PhpHighlightingData.DOC_IDENTIFIER)
val PHPDOC_GENERIC_TYPE_T = TextAttributesKey.createTextAttributesKey("PHPDOC_GENERIC_TYPE_T", PhpHighlightingData.DOC_IDENTIFIER)

val FUNC_CALL_REGULAR = TextAttributesKey.createTextAttributesKey("FUNC_CALL_REGULAR", PhpHighlightingData.FUNCTION_CALL)
val FUNC_CALL_KPHP_NATIVE = TextAttributesKey.createTextAttributesKey("FUNC_CALL_KPHP_NATIVE", PhpHighlightingData.FUNCTION_CALL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.jetbrains.php.lang.psi.elements.PhpClass
import com.jetbrains.php.lang.psi.elements.impl.ClassReferenceImpl
import com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl
import com.jetbrains.php.lang.psi.resolve.types.PhpType
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeInstancePsiImpl
import com.vk.kphpstorm.helpers.KPHP_NATIVE_FUNCTIONS
import com.vk.kphpstorm.kphptags.ALL_KPHPDOC_TAGS
import com.vk.kphpstorm.kphptags.psi.KphpDocTagImpl
Expand Down Expand Up @@ -122,6 +123,9 @@ class KphpStormAnnotator : Annotator {
@Suppress("UNUSED_PARAMETER")
private fun onTypeInsidePhpdocTag(element: PhpDocTypeImpl, holder: AnnotationHolder) {
// for future: highlight primitives and classes, template args and shape keys
if (element is ExPhpTypeInstancePsiImpl && element.isGenericT()) {
holder.textAttributes(element, KphpHighlightingData.PHPDOC_GENERIC_TYPE_T)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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.ExPhpType
import com.vk.kphpstorm.exphptype.ExPhpTypeInstance
import com.vk.kphpstorm.exphptype.ExPhpTypeTplInstantiation
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeInstancePsiImpl
Expand Down Expand Up @@ -36,4 +37,15 @@ class KphpDocInheritParameterDeclPsiImpl(node: ASTNode) : PhpDocPsiElementImpl(n

return exType.getInstantiation()?.classFqn
}

fun specializationList(): List<ExPhpType> {
val instantiationPsi = findChildByClass(PhpDocTypeImpl::class.java) ?: return emptyList()
if (instantiationPsi !is ExPhpTypeTplInstantiationPsiImpl)
return emptyList()

val exType = instantiationPsi.type.toExPhpType() ?: return emptyList()
val instantiation = exType.getInstantiation() ?: return emptyList()

return instantiation.specializationList
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@ object KphpDocTagInheritElementType :
}

override fun createStub(psi: PhpDocTag, parentStub: StubElement<*>?): PhpDocTagStub {
// stub value is 'T1,T2:ExtendsClass,T2=default' — without spaces
// TODO: add stubs
return KphpDocTagStubImpl(parentStub, this, psi.name, "stubValue")
val stubValue = (psi as KphpDocTagInheritPsiImpl).types()
.joinToString(",") {
val type = StringBuilder()

if (it.className() != null) {
type.append(it.className().toString())
}

type.toString()
}
return KphpDocTagStubImpl(parentStub, this, psi.name, stubValue)
}

override fun serialize(stub: PhpDocTagStub, dataStream: StubOutputStream) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ package com.vk.kphpstorm.typeProviders

import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.util.parentOfType
import com.jetbrains.php.lang.psi.elements.ClassReference
import com.jetbrains.php.lang.psi.elements.NewExpression
import com.jetbrains.php.lang.psi.elements.PhpClass
import com.jetbrains.php.lang.psi.resolve.types.PhpCharTypeKey
import com.jetbrains.php.lang.psi.resolve.types.PhpType
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4
import com.vk.kphpstorm.exphptype.psi.ExPhpTypeTplInstantiationPsiImpl
import com.vk.kphpstorm.generics.GenericUtil.genericInheritInstantiation
import com.vk.kphpstorm.generics.IndexingGenericFunctionCall
import com.vk.kphpstorm.generics.ResolvingGenericConstructorCall

Expand All @@ -26,6 +31,17 @@ class GenericClassesTypeProvider : PhpTypeProvider4 {
return PhpType().add(KEY.sign(data))
}

if (p is ClassReference && p.name == "parent") {
val containingClass = p.parentOfType<PhpClass>()
if (containingClass != null) {
val superClass = containingClass.extendsList.referenceElements.firstOrNull() ?: return null
val superClassName = superClass.fqn ?: return null
val instantiationParameter = containingClass.genericInheritInstantiation(superClassName)
val instantiation = instantiationParameter?.firstChild as? ExPhpTypeTplInstantiationPsiImpl
return instantiation?.type
}
}

return null
}

Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/colorSchemes/KphpAddonsDarcula.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
<option name="FONT_TYPE" value="2"/>
</value>
</option>
<option name="PHPDOC_GENERIC_TYPE_T">
<value>
<option name="FOREGROUND" value="9AA6B3"/>
<option name="FONT_TYPE" value="2"/>
</value>
</option>

<option name="FUNC_CALL_KPHP_NATIVE">
<value>
Expand Down
Loading

0 comments on commit f7b998d

Please sign in to comment.