Skip to content

Commit

Permalink
Improved generic concreteness handling within function bodies.
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin-Dobell committed Oct 16, 2022
1 parent 9f3623c commit 1c46e79
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class LuaParameterHintsProvider : InlayParameterHintsProvider {
val isInstanceMethodUsedAsStaticMethod = ty.isColonCall && callExpr.isMethodDotCall
val searchContext = PsiSearchContext(callExpr)

val sig = ty.matchSignature(searchContext, callExpr)?.signature
val sig = ty.matchSignature(searchContext, callExpr)?.substitutedSignature

sig?.processParameters(null, callExpr.isMethodColonCall) { index, paramInfo ->
val expr = exprList.getOrNull(index) ?: return@processParameters false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.intellij.psi.PsiElementVisitor
import com.tang.intellij.lua.lang.LuaFileType
import com.tang.intellij.lua.project.LuaSettings
import com.tang.intellij.lua.psi.*
import com.tang.intellij.lua.search.ProjectSearchContext
import com.tang.intellij.lua.search.PsiSearchContext
import com.tang.intellij.lua.search.SearchContext
import com.tang.intellij.lua.ty.*
Expand Down Expand Up @@ -56,7 +57,7 @@ class MatchFunctionSignatureInspection : StrictInspection() {
override fun visitCallExpr(o: LuaCallExpr) {
super.visitCallExpr(o)

val searchContext = PsiSearchContext(o)
val searchContext = ProjectSearchContext(o.project)
val prefixExpr = o.expression
var resolvedTy = prefixExpr.guessType(searchContext)?.let {
Ty.resolve(searchContext, it)
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/tang/intellij/lua/psi/PsiExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private fun LuaExpression<*>.shouldBeInternal(context: SearchContext): ITy? {
var ret: ITy = Primitives.VOID
Ty.eachResolved(context, fTy) {
if (it is ITyFunction) {
var sig = it.matchSignature(context, p2)?.signature ?: it.mainSignature
var sig = it.matchSignature(context, p2)?.substitutedSignature ?: it.mainSignature
val substitutor = p2.createSubstitutor(context, sig)
sig = sig.substitute(context, substitutor)
ret = ret.union(context, sig.getArgTy(idx))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement


class ProjectSearchContext(override val project: Project) : SearchContext() {
class ProjectSearchContext : SearchContext {
override var project: Project

constructor(project: Project): super() {
this.project = project
}

constructor(sourceContext: SearchContext): super(sourceContext) {
this.project = sourceContext.project
}

override val element: PsiElement? = null

override fun getProjectContext() = this
}
18 changes: 17 additions & 1 deletion src/main/java/com/tang/intellij/lua/search/SearchContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,27 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectAndLibrariesScope
import com.tang.intellij.lua.ext.ILuaTypeInfer
import com.tang.intellij.lua.psi.LuaIndexExpr
import com.tang.intellij.lua.psi.LuaPsiTypeGuessable
import com.tang.intellij.lua.psi.ScopedTypeSubstitutor
import com.tang.intellij.lua.ty.ITy
import com.tang.intellij.lua.ty.ITyClass
import com.tang.intellij.lua.ty.isAnonymous
import com.tang.intellij.lua.ty.isSelfClass
import java.util.*

/**
* Created by tangzx on 2017/1/14.
*/
abstract class SearchContext {
abstract class SearchContext() {
abstract val project: Project
abstract val element: PsiElement?

open fun getProjectContext(): ProjectSearchContext {
return ProjectSearchContext(this)
}

val index: Int get() = myIndex // Multiple results index
val supportsMultipleResults: Boolean get() = myMultipleResults

Expand All @@ -46,6 +54,14 @@ abstract class SearchContext {

private val myInferCache = mutableMapOf<LuaPsiTypeGuessable, ITy>()

protected constructor(sourceContext: SearchContext) : this() {
myDumb = sourceContext.myDumb
myIndex = sourceContext.myIndex
myMultipleResults = sourceContext.myMultipleResults
myInStack = sourceContext.myInStack
myScope = sourceContext.myScope
}

fun <T> withIndex(index: Int, supportMultipleResults: Boolean = false, action: () -> T): T {
val savedIndex = this.index
val savedMultipleResults = this.supportsMultipleResults
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/tang/intellij/lua/ty/Expressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ fun LuaCallExpr.createSubstitutor(context: SearchContext, sig: IFunSignature): I
return selfSubstitutor
}

private fun LuaCallExpr.infer(context: SearchContext): ITy? {
private fun LuaCallExpr.infer(searchContext: SearchContext): ITy? {
val context = searchContext.getProjectContext()
val luaCallExpr = this
// xxx()
val expr = luaCallExpr.expression
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/tang/intellij/lua/ty/Ty.kt
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ interface ITy : Comparable<ITy> {
return flags and TyFlags.SHAPE != 0
}

fun guessMemberType(context: SearchContext, name: String): ITy? {
fun guessMemberType(searchContext: SearchContext, name: String): ITy? {
val context = if (isAnonymous) searchContext else searchContext.getProjectContext()
val member = findEffectiveMember(context, name)

if (member == null) {
Expand All @@ -189,7 +190,8 @@ interface ITy : Comparable<ITy> {
} ?: Primitives.UNKNOWN
}

fun guessIndexerType(context: SearchContext, indexTy: ITy, exact: Boolean = false): ITy? {
fun guessIndexerType(searchContext: SearchContext, indexTy: ITy, exact: Boolean = false): ITy? {
val context = searchContext.getProjectContext()
var ty: ITy? = null
val substitutor: Lazy<ITySubstitutor?> = lazy {
getMemberSubstitutor(context)
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/tang/intellij/lua/ty/TyClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ fun getSelfClassName(classTy: ITyClass): String {
}

fun isSelfClass(classTy: ITyClass): Boolean {
return classTy.className.endsWith(CLASS_NAME_SUFFIX_SELF)
return classTy.isAnonymous && classTy.className.endsWith(CLASS_NAME_SUFFIX_SELF)
}

private const val GENERIC_PARAMETER_NAME_SUFFIX_CONCRETE = "#concrete"
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/tang/intellij/lua/ty/TyGeneric.kt
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ interface ITyGeneric : ITyResolvable {
override fun getMemberSubstitutor(context: SearchContext): ITySubstitutor {
val resolvedBase = TyAliasSubstitutor().substitute(context, base)
val baseParams = resolvedBase.getParams(context) ?: arrayOf()
var parameterSubstitutor = TyParameterSubstitutor.withArgs(baseParams, args)
val parameterSubstitutor = TyParameterSubstitutor.withArgs(baseParams, args)
return super.getMemberSubstitutor(context)?.let {
TyChainSubstitutor.chain(it, parameterSubstitutor)
} ?: parameterSubstitutor
Expand Down
31 changes: 31 additions & 0 deletions src/test/resources/inspections/function_generics_scope.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,34 @@ local function scopedGenericAnnotatedReturnStatement(value)
return value
end
end

---@generic V
---@param value V
---@return V
function recursiveConcreteness(value)
---@type V
local v

value = v
v = value

local resultantVFromRecursiveCall = recursiveConcreteness(v)

v = resultantVFromRecursiveCall
resultantVFromRecursiveCall = v

---@type boolean
local differentVForRecursiveCall
local resultantVFromDifferentVRecursiveCall = recursiveConcreteness(differentVForRecursiveCall)

differentVForRecursiveCall = resultantVFromDifferentVRecursiveCall
resultantVFromDifferentVRecursiveCall = differentVForRecursiveCall

v = <error descr="Type mismatch. Required: 'V' Found: 'boolean'">differentVForRecursiveCall</error>
v = <error descr="Type mismatch. Required: 'V' Found: 'boolean'">resultantVFromDifferentVRecursiveCall</error>

differentVForRecursiveCall = <error descr="Type mismatch. Required: 'boolean' Found: 'V'">v</error>
resultantVFromDifferentVRecursiveCall = <error descr="Type mismatch. Required: 'boolean' Found: 'V'">v</error>

return value
end
39 changes: 39 additions & 0 deletions src/test/resources/inspections/generic_class_scope.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,42 @@ end
---@param arg T
GenericInMethod.lambdaMethodShadow = function(arg)
end

---@class Concreteness<T>
---@field myT T
local Concreteness = {}

---@return T
function Concreteness:getT()
---@type T
local t
return t
end

---@param t T
function Concreteness:setT(t)
end

function Concreteness:testConcreteClassGenericWithinImplementationScope(t)
---@type T
local t

t = self:getT()
self:setT(t)

self.myT = self:getT()
self:setT(self.myT)

---@type Concreteness<number>
local numberConcreteness
numberConcreteness:setT(<error descr="Type mismatch. Required: 'number' Found: 'T'">self:getT()</error>)
self:setT(<error descr="Type mismatch. Required: 'T' Found: 'number'">numberConcreteness:getT()</error>)
self.myT = <error descr="Type mismatch. Required: 'T' Found: 'number'">numberConcreteness:getT()</error>
numberConcreteness:setT(<error descr="Type mismatch. Required: 'number' Found: 'T'">self.myT</error>)

t = <error descr="Type mismatch. Required: 'T' Found: 'number'">numberConcreteness:getT()</error>
numberConcreteness:setT(<error descr="Type mismatch. Required: 'number' Found: 'T'">t</error>)

t = <error descr="Type mismatch. Required: 'T' Found: 'number'">numberConcreteness.myT</error>
numberConcreteness.myT = <error descr="Type mismatch. Required: 'number' Found: 'T'">t</error>
end

0 comments on commit 1c46e79

Please sign in to comment.