Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

super forcing #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ interface ExPhpType {
fun instantiateTemplate(nameMap: Map<String, ExPhpType>): ExPhpType
fun isAssignableFrom(rhs: ExPhpType, project: Project): Boolean

fun dropForce(): ExPhpType?

companion object {
val INT = ExPhpTypePrimitive(KphpPrimitiveTypes.INT)
val FLOAT = ExPhpTypePrimitive(KphpPrimitiveTypes.FLOAT)
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeAny.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ class ExPhpTypeAny : ExPhpType {
// anything can be assigned to any
return true
}

override fun dropForce(): ExPhpType {
return this
}
}
4 changes: 4 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeArray.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ class ExPhpTypeArray(val inner: ExPhpType) : ExPhpType {
else -> false
}
}

override fun dropForce(): ExPhpType? {
return ExPhpTypeArray(inner.dropForce() ?: return null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,11 @@ class ExPhpTypeCallable(val argTypes: List<ExPhpType>, val returnType: ExPhpType
is ExPhpTypeNullable -> isAssignableFrom(rhs.inner, project)
else -> false
}

override fun dropForce(): ExPhpType {
val newArgTypes = argTypes.mapNotNull { it.dropForce() }
val newReturnType = returnType?.dropForce()

return ExPhpTypeCallable(newArgTypes, newReturnType)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ class ExPhpTypeForcing(val inner: ExPhpType) : ExPhpType {
// that's why everything is assignable to forcing type, making stricy typing inspections assume all is ok
return true
}

override fun dropForce(): ExPhpType? {
return inner.dropForce()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ class ExPhpTypeInstance(val fqn: String) : ExPhpType {

else -> false
}

override fun dropForce(): ExPhpType {
return this
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ class ExPhpTypeNullable(val inner: ExPhpType) : ExPhpType {
is ExPhpTypePrimitive -> rhs === ExPhpType.NULL || inner.isAssignableFrom(rhs, project)
else -> inner.isAssignableFrom(rhs, project)
}

override fun dropForce(): ExPhpType? {
return ExPhpTypeNullable(inner.dropForce() ?: return null)
}
}
14 changes: 14 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypePipe.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ class ExPhpTypePipe(val items: List<ExPhpType>) : ExPhpType {
}
}

override fun dropForce(): ExPhpType {
val typePipe = if (items.count { it is ExPhpTypeForcing } == 0) {
ExPhpTypePipe(items.mapNotNull { it.dropForce() })
} else {
ExPhpTypePipe(items.filterIsInstance<ExPhpTypeForcing>().mapNotNull { it.dropForce() })
}

if (typePipe.items.size == 1) {
return typePipe.items.first()
}

return typePipe
}

/**
* Heruistics: let this == "string|int", where can this be assigned to?
* To "string" — no, "int" not compatible. To "string|float" — yes. To "string|int|A" — yes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class ExPhpTypePrimitive(val typeStr: String) : ExPhpType {
else -> false
}

override fun dropForce(): ExPhpType {
return this
}

companion object {
private fun canBeAssigned(l: ExPhpTypePrimitive, r: ExPhpTypePrimitive) = with(ExPhpType.Companion) {
when {
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeShape.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ class ExPhpTypeShape(val items: List<ShapeItem>) : ExPhpType {
is ExPhpTypeShape -> true // any shape is compatible with any other, for simplification (tuples are not)
else -> false
}

override fun dropForce(): ExPhpType {
return ExPhpTypeShape(items.mapNotNull { ShapeItem(it.keyName, it.nullable, it.type.dropForce() ?: return@mapNotNull null ) })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ class ExPhpTypeTplInstantiation(val classFqn: String, val specializationList: Li
is ExPhpTypeTplInstantiation -> classFqn == rhs.classFqn && specializationList.size == rhs.specializationList.size
else -> false
}

override fun dropForce(): ExPhpType {
return this
}
}
4 changes: 4 additions & 0 deletions src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeTuple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ class ExPhpTypeTuple(val items: List<ExPhpType>) : ExPhpType {
is ExPhpTypeTuple -> items.size == rhs.items.size && items.indices.all { items[it].isAssignableFrom(rhs.items[it], project) }
else -> false
}

override fun dropForce(): ExPhpType {
return ExPhpTypeTuple(items.mapNotNull { it.dropForce() })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ object PhpTypeToExPhpTypeParsing {

// some "forced" that can occur often, @see [ForcingTypeProvider], \\ not needed
"force(string)" to ExPhpTypeForcing(ExPhpType.STRING),
"force(float)" to ExPhpTypeForcing(ExPhpType.FLOAT),
"force(int)" to ExPhpTypeForcing(ExPhpType.INT),
"force(bool)" to ExPhpTypeForcing(ExPhpType.BOOL),
"force(kmixed)" to ExPhpTypeForcing(ExPhpType.KMIXED),
Expand Down Expand Up @@ -285,7 +286,7 @@ object PhpTypeToExPhpTypeParsing {
val lhs = parseTypeArray(builder) ?: return null
// wrap with ExPhpTypePipe only 'T1|T2', leaving 'T' being as is
if (!builder.compare('|') && !builder.compare('/'))
return if (lhs is ExPhpTypeForcing) lhs.inner else lhs
return lhs

val pipeItems = mutableListOf(lhs)
while (builder.compareAndEat('|') || builder.compareAndEat('/')) {
Expand Down Expand Up @@ -313,11 +314,6 @@ object PhpTypeToExPhpTypeParsing {
if (size == 2 && pipeItems[1] === ExPhpType.NULL)
return createNullableOrSimplified(pipeItems[0])

// T1|T2|...|force(T) will be just T
for (item in pipeItems)
if (item is ExPhpTypeForcing)
return item.inner

return ExPhpTypePipe(pipeItems)
}

Expand Down Expand Up @@ -347,7 +343,7 @@ object PhpTypeToExPhpTypeParsing {
else
FQN_PREPARSED[str] ?: parseTypeExpression(ExPhpTypeBuilder(str))
}
createPipeOrSimplified(items)
createPipeOrSimplified(items)?.dropForce()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,16 @@ function demo() {
$a->s_arr[] = $s_arr[0];
$a->s_arr = str_replace('search', 'replace', getSArr());

<error descr="Can't assign 'int' to 'string' $s">$a->s = str_replace('s', 'r', getTrash())</error>;
<error descr="Can't assign 'int|bool' to 'string' $s">$a->s = str_replace('s', 'r', getTrash())</error>;
}

function demo2() {
$a = new A;
$a->s = array_first_value($a->s_arr);
<error descr="Can't assign 'string' to 'A' $a">$a->a = array_first_value($a->s_arr)</error>;
$a->s_arr[] = array_last_value($a->s_arr);
<error descr="Can't assign 'mixed|any' to 'string' $s">$a->s = array_first_value($a->var)</error>;
<error descr="Can't assign 'mixed|any' to 'A' $a">$a->a = array_first_value($a->var)</error>;
<error descr="Can't assign 'mixed' to 'string' $s">$a->s = array_first_value($a->var)</error>;
<error descr="Can't assign 'mixed' to 'A' $a">$a->a = array_first_value($a->var)</error>;
$a->var = array_first_value($a->var);
$a->s_arr = array_filter_by_key($a->s_arr, function($k) { return true; });
<error descr="Can't assign 'int[]' to 'string[]' $s_arr">$a->s_arr = array_filter_by_key($a->i_arr, function($k) { return true; })</error>;
Expand Down
24 changes: 21 additions & 3 deletions src/test/kotlin/com/vk/kphpstorm/testing/tests/ExPhpTypeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ class ExPhpTypeTest : TestCase() {
}

fun testParsingFromString() {
fun String.toExPhpType(): ExPhpType =
PhpTypeToExPhpTypeParsing.parseFromString(this) ?: throw RuntimeException("Couldnt parse $this")

"int".toExPhpType().apply {
Assert.assertTrue(this is ExPhpTypePrimitive)
Assert.assertSame(this, ExPhpType.INT)
Expand Down Expand Up @@ -111,4 +108,25 @@ class ExPhpTypeTest : TestCase() {
Assert.assertTrue(PhpType.STRING.toExPhpType()!!.isAssignableFrom(phpType.toExPhpType()!!, createMockProject()))
}

fun testSupperForcing() {
checkDrop("int|string", "int|string")
checkDrop("force(force(int[])[])", "int[][]")
checkDrop("string|bool|force(int)", "int")
checkDrop("string|bool|force(int)|force(float)", "int|float")
checkDrop("force(force(int[])[])|force(null)", "int[][]|null")
checkDrop("tuple(force(int)|string, int|force(string), bool)", "tuple(int,string,bool)")
checkDrop("shape(key: int|force(string), key2:bool, key3: float|bool|force(int[][]))", "shape(key:string,key2:bool,key3:int[][])")
checkDrop("callable(string,force(int)|string):force(force(int[])[])|string", "callable(string,int):int[][]")
checkDrop("callable(int,force(?bool)|string)", "callable(int,?bool):void")
}

private fun String.toExPhpType(): ExPhpType =
PhpTypeToExPhpTypeParsing.parseFromString(this) ?: throw RuntimeException("Couldnt parse $this")

private fun checkDrop(forceType: String, expectedType: String) {
forceType.toExPhpType().apply {
Assert.assertEquals(expectedType, this.dropForce().toString())

}
}
}