Skip to content

Commit

Permalink
Improved support for lambda classes with static methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin-Dobell committed Jul 16, 2022
1 parent 1a78ef5 commit bd912ee
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/tang/intellij/lua/lua.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ localDef ::= ID attribute? {
]
stubClass = "com.tang.intellij.lua.stubs.LuaLocalDefStub"
}

private localDefList ::= localDef (',' localDef)*
localDefStat ::= 'local' localDefList ('=' exprList)? {
pin = 1
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/com/tang/intellij/lua/psi/LuaPsiImplUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,12 @@ fun guessParentType(tableField: LuaTableField, context: SearchContext): ITy {
// has a "setmetatable" in scope that isn't functionally equivalent to Lua's setmetatable... too bad.
if (argList.size == 2 && argList[1] == tableField.parent && callExpr.nameExpr?.text == Constants.FUNCTION_SETMETATABLE) {
val callMetamethod = (tableField.parent as LuaTableExpr).findField(Constants.METAMETHOD_CALL)?.valueExpr as? LuaFuncBodyOwner<*>
(argList[0].guessType(context) ?: callMetamethod?.guessReturnType(context)) as? ITyClass
val returnClass = callMetamethod?.guessReturnType(context) as? ITyClass
if (returnClass != null && returnClass.className != Constants.WORD_SELF && !isSelfClass(returnClass)) {
returnClass
} else {
argList[0].guessType(context) as? ITyClass
}
} else {
null
}
Expand All @@ -509,7 +514,7 @@ fun guessIndexType(tableField: LuaTableField, context: SearchContext): ITy? {
tableField.idExpr?.guessType(context)
}

if (indexTy != null) {
if (indexTy != null || tableField.lbrack != null) {
return indexTy
} else {
var fieldIndex = 0
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/com/tang/intellij/lua/ty/TyClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,65 @@ class TyTable(val table: LuaTableExpr) : TyClass(getTableTypeName(table)) {
}

override fun doLazyInit(searchContext: SearchContext) = Unit

override fun processMembers(context: SearchContext, deep: Boolean, process: ProcessTypeMember): Boolean {
if (!context.isDumb) {
return super.processMembers(context, deep, process)
}

table.tableFieldList.forEach {
if (!process(this, it)) {
return false
}
}

return true
}

override fun processMember(context: SearchContext, name: String, deep: Boolean, process: ProcessTypeMember): Boolean {
if (!context.isDumb) {
return super.processMember(context, name, deep, process)
}

return table.tableFieldList.firstOrNull {
val fieldName = it.name
if (fieldName != null) {
fieldName == name
} else {
it.indexType?.getType()?.let {
(it is ITyPrimitive && it.primitiveKind == TyPrimitiveKind.String)
|| (it is TyPrimitiveLiteral && it.primitiveKind == TyPrimitiveKind.String && it.value == name)
} ?: false
}
}?.let {
process(this, it)
} ?: true
}

override fun processIndexer(context: SearchContext, indexTy: ITy, exact: Boolean, deep: Boolean, process: ProcessTypeMember): Boolean {
if (!context.isDumb) {
return super.processIndexer(context, indexTy, exact, deep, process)
}

var narrowestTypeMember: TypeMember? = null
var narrowestIndexTy: ITy? = null

table.tableFieldList.forEach { field ->
val candidateIndexerTy = field.guessIndexType(context) ?: field.name?.let {
TyPrimitiveLiteral.getTy(TyPrimitiveKind.String, it)
}

if ((!exact && candidateIndexerTy?.contravariantOf(context, indexTy, TyVarianceFlags.STRICT_UNKNOWN) == true)
|| candidateIndexerTy == indexTy) {
if (narrowestIndexTy?.contravariantOf(context, candidateIndexerTy, TyVarianceFlags.STRICT_UNKNOWN) != false) {
narrowestTypeMember = field
narrowestIndexTy = candidateIndexerTy
}
}
}

return narrowestTypeMember?.let { process(this, it) } ?: true
}
}

fun getDocTableTypeName(table: LuaDocTableDef): String {
Expand Down
52 changes: 52 additions & 0 deletions src/test/resources/inspections/lambda_class.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,55 @@ local aNumber = lambdaClass.getNumber()
---@type string
local aString = <error descr="Type mismatch. Required: 'string' Found: 'number'">lambdaClass.getNumber()</error>
---@class ParentLambdaClassWithStatic
---@class ParentLambdaClassWithStatic__static
---@overload fun(): ParentLambdaClassWithStatic
local ParentLambdaClassWithStatic = {}
---@class LambdaClassWithStatic
---@class LambdaClassWithStatic__static
---@overload fun(): LambdaClassWithStatic
local LambdaClassWithStatic = {}
function LambdaClassWithStatic.staticMethod()
end
setmetatable(LambdaClassWithStatic, {
---@return LambdaClassWithStatic
__call = function(_)
local self = --[[---@type self]] ParentLambdaClassWithStatic()
---@return self
function self.method()
return self
end
return self
end,
})
---@type LambdaClassWithStatic
local lambdaClassWithStatic
---@type LambdaClassWithStatic__static
local lambdaClassWithStatic__static
---@type ParentLambdaClassWithStatic
local parentLambdaClassWithStatic
---@type ParentLambdaClassWithStatic__static
local parentLambdaClassWithStatic__static
lambdaClassWithStatic.method()
<error descr="Unknown function 'method'."><error descr="No such member 'method' found on type 'LambdaClassWithStatic__static'">lambdaClassWithStatic__static.method</error>()</error>
<error descr="Unknown function 'method'."><error descr="No such member 'method' found on type 'ParentLambdaClassWithStatic'">parentLambdaClassWithStatic.method</error>()</error>
<error descr="Unknown function 'method'."><error descr="No such member 'method' found on type 'ParentLambdaClassWithStatic__static'">parentLambdaClassWithStatic__static.method</error>()</error>
<error descr="Unknown function 'staticMethod'."><error descr="No such member 'staticMethod' found on type 'LambdaClassWithStatic'">lambdaClassWithStatic.staticMethod</error>()</error>
lambdaClassWithStatic__static.staticMethod()
<error descr="Unknown function 'staticMethod'."><error descr="No such member 'staticMethod' found on type 'ParentLambdaClassWithStatic'">parentLambdaClassWithStatic.staticMethod</error>()</error>
<error descr="Unknown function 'staticMethod'."><error descr="No such member 'staticMethod' found on type 'ParentLambdaClassWithStatic__static'">parentLambdaClassWithStatic__static.staticMethod</error>()</error>
2 changes: 1 addition & 1 deletion src/test/resources/inspections/self.lua
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ local SetmetatableSelf = {}

setmetatable(SetmetatableSelf, {
__call = function(_)
---@type self @In the presence of setmetatable, self's type is pulled from setmetatable()'s first arg or the return type of __call.
---@type self @In the presence of setmetatable, self's type is pulled from the return type of __call or setmetatable()'s first arg.
local self

function self.returnsSelf()
Expand Down

0 comments on commit bd912ee

Please sign in to comment.