Skip to content

Commit

Permalink
Ensure global classes (i.e. built-ins) can be extended.
Browse files Browse the repository at this point in the history
Closes #55
  • Loading branch information
Benjamin-Dobell committed Jan 16, 2023
1 parent 5d721ed commit 5db79fb
Show file tree
Hide file tree
Showing 13 changed files with 46 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import com.tang.intellij.lua.psi.LuaCallExpr
import com.tang.intellij.lua.psi.LuaExpression
import com.tang.intellij.lua.psi.LuaIndexExpr
import com.tang.intellij.lua.psi.LuaVisitor
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class UndeclaredMemberInspection : StrictInspection() {
val memberName = o.name

Ty.eachResolved(context, prefix) { prefixTy ->
if (!prefixTy.isGlobal && !(prefixTy.isUnknown && LuaSettings.instance.isUnknownIndexable)) {
if ((!prefixTy.isGlobal && !prefixTy.isUnknown) || !LuaSettings.instance.isUnknownIndexable) {
if (memberName != null) {
if (prefixTy.guessMemberType(context, memberName) == null) {
myHolder.registerProblem(o, "No such member '%s' found on type '%s'".format(memberName, prefixTy))
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/tang/intellij/lua/lang/LuaLanguage.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
public class LuaLanguage extends Language {

public static final int INDEX_VERSION = 70;
public static final int INDEX_VERSION = 71;

public static final LuaLanguage INSTANCE = new LuaLanguage();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ class LuaClassMethodType : LuaStubElementType<LuaClassMethodDefStatStub, LuaClas
}
}

if (classNameSet.isEmpty()) {
classNameSet.add(createSerializedClass(expr.text))
}


val visibility = def.visibility
val isStatic = methodName.dot != null
val isDeprecated = def.isDeprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,6 @@ class LuaDocTagClassStubImpl(override val className: String,
override val classType: TyClass
get() {
val flags = if (isShape) TyFlags.SHAPE else 0
return createSerializedClass(className, params, className, superClass, signatures, aliasName, flags)
return createSerializedClass(className, params, null, superClass, signatures, aliasName, flags)
}
}
32 changes: 18 additions & 14 deletions src/main/java/com/tang/intellij/lua/ty/Expressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ private fun LuaNameExpr.infer(context: SearchContext): ITy? {
}
}

withSearchGuard(this) {
var ty = withSearchGuard(this) {
val multiResolve = multiResolve(context, this)
var maxTimes = 10

Expand All @@ -400,6 +400,19 @@ private fun LuaNameExpr.infer(context: SearchContext): ITy? {

type
} ?: getType(context, this)

// Global
if (ty == null && context.isDumb && this.isGlobal()) {
// In order to facilitate the extension of globals without needing to *explicitly* refer to the
// underlying global variable's class by name. Since we can't look up / resolve the global's variable's
// type during indexing, we instead attach members to a global type (identified by variable name). When
// we process a class' members later, we also process against its aliasName i.e. name of global variable.
// NOTE: We only need to hit this code path in "dumb mode" (i.e. during stub indexing) since that's where
// we create/index class members. All other times, we'll leave the ty as nil (unknown).
ty = TyClass.createGlobalType(this)
}

ty
})
}

Expand Down Expand Up @@ -429,26 +442,17 @@ private fun getType(context: SearchContext, def: PsiElement): ITy? {
}
}
}

//Global
if (type != null && isGlobal(def) && def.docTy == null && type !is ITyPrimitive) {
// Explicitly instantiating a union (not calling the type.union()) as the global type resolves to type,
// and hence we would have just got type back. We're creating a union because we need to ensure members
// are indexed against the global name (for completion) as well as the other type (for type resolution).
type = TyUnion(listOf(type, TyClass.createGlobalType(def)))
}

type
}
is LuaPsiTypeGuessable -> def.guessType(context)
else -> null
}
}

private fun isGlobal(nameExpr: LuaNameExpr): Boolean {
val minx = nameExpr as LuaNameExprMixin
val gs = minx.greenStub
return gs?.isGlobal ?: (resolveLocal(null, nameExpr) == null)
fun LuaNameExpr.isGlobal(): Boolean {
val mixin = this as LuaNameExprMixin
val greenStub = mixin.greenStub
return greenStub?.isGlobal ?: (resolveLocal(null, this) == null)
}

fun LuaLiteralExpr.infer(): ITy {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/tang/intellij/lua/ty/Ty.kt
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ abstract class Ty(override val kind: TyKind) : ITy {
override fun toString() = displayName

override fun contravariantOf(context: SearchContext, other: ITy, varianceFlags: Int): Boolean {
if ((other.kind == TyKind.Unknown && varianceFlags and TyVarianceFlags.STRICT_UNKNOWN == 0)
if (((other.kind == TyKind.Unknown || other.isGlobal) && varianceFlags and TyVarianceFlags.STRICT_UNKNOWN == 0)
|| (other.kind == TyKind.Nil && varianceFlags and TyVarianceFlags.STRICT_NIL == 0 && !LuaSettings.instance.isNilStrict)
) {
return true
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/tang/intellij/lua/ty/TyClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import com.tang.intellij.lua.stubs.*

interface ITyClass : ITyResolvable {
val className: String
val varName: String
val varName: String?

var superClass: ITy?
var aliasName: String?
Expand Down Expand Up @@ -168,7 +168,7 @@ private fun equalToShape(context: SearchContext, target: ITy, source: ITy): Bool

abstract class TyClass(override val className: String,
override var params: Array<TyGenericParameter>? = null,
override val varName: String = "",
override val varName: String? = "",
override var superClass: ITy? = null,
override var signatures: Array<IFunSignature>? = null
) : Ty(TyKind.Class), ITyClass {
Expand Down Expand Up @@ -407,7 +407,7 @@ abstract class TyClass(override val className: String,
class TyPsiDocClass(override val psi: LuaDocTagClass) : TyClass(
psi.name,
psi.genericDefList.map { TyGenericParameter(it) }.toTypedArray(),
"",
null,
psi.superClass?.getType(),
psi.overloads
), IPsiTy<LuaDocTagClass> {
Expand All @@ -425,7 +425,7 @@ class TyPsiDocClass(override val psi: LuaDocTagClass) : TyClass(

open class TySerializedClass(name: String,
params: Array<TyGenericParameter>? = null,
varName: String = name,
varName: String? = null,
superClass: ITy? = null,
signatures: Array<IFunSignature>? = null,
alias: String? = null,
Expand Down Expand Up @@ -467,7 +467,7 @@ class TyLazyClass(name: String, val psi: PsiElement? = null) : TySerializedClass

fun createSerializedClass(name: String,
params: Array<TyGenericParameter>? = null,
varName: String = name,
varName: String? = null,
superClass: ITy? = null,
signatures: Array<IFunSignature>? = null,
alias: String? = null,
Expand Down Expand Up @@ -619,7 +619,7 @@ fun getConcreteGenericParameterName(genericParam: TyGenericParameter): String {
}

fun getGlobalTypeName(text: String): String {
return if (text == Constants.WORD_G) text else "$$text"
return if (text == Constants.WORD_G) text else "${Constants.WORD_G}.$text"
}

fun getGlobalTypeName(nameExpr: LuaNameExpr): String {
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 @@ -30,7 +30,7 @@ fun genericParameterName(def: LuaDocGenericDef): String {
return "${def.id.text}@${def.node.startOffset}@${def.containingFile.getFileIdentifier()}"
}

class TyGenericParameter(name: String, varName: String, superClass: ITy? = null) : TySerializedClass(name, emptyArray(), varName, superClass, null) {
class TyGenericParameter(name: String, override val varName: String, superClass: ITy? = null) : TySerializedClass(name, emptyArray(), varName, superClass, null) {
constructor(def: LuaDocGenericDef) : this(genericParameterName(def), def.id.text, def.superClass?.getType())

override fun equals(other: Any?): Boolean {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/tang/intellij/lua/ty/TyRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -332,10 +332,10 @@ open class TyRenderer : TyVisitor(), ITyRenderer {
}
clazz.isAnonymous -> {
if (isSuffixedClass(clazz)) {
clazz.varName
} else {
"[local ${clazz.varName}]"
clazz.varName?.let { return it }
}

"[local ${clazz.varName}]"
}
clazz.isGlobal -> "[global ${clazz.varName}]"
else -> "${clazz.className}${renderGenericParams(clazz.params?.map { it.toString() })}"
Expand Down
7 changes: 7 additions & 0 deletions src/test/resources/inspections/global_definitions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ function GlobalAnonymousClass.doSomethingElse()
end

GlobalAnonymousClass.anotherNumber = 2

--- Extend built-in global without explicitly referring to its type

---@param a number
function math.extension(a)
return a
end
6 changes: 5 additions & 1 deletion src/test/resources/inspections/global_usage.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ aNumber = GlobalAnonymousClass.anotherNumber
aString = <error descr="Type mismatch. Required: 'string' Found: 'number'">GlobalClass.someNumber</error>
aString = <error descr="Type mismatch. Required: 'string' Found: 'number'">GlobalAnonymousClass.anotherNumber</error>

---@type number
local fromStdLib = math.pi
aNumber = fromStdLib
aString = <error descr="Type mismatch. Required: 'string' Found: 'number'">fromStdLib</error>

aNumber = math.extension(aNumber)
aString = <error descr="Type mismatch. Required: 'string' Found: 'number'">math.extension(aNumber)</error>
4 changes: 2 additions & 2 deletions src/test/resources/inspections/self.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ end
---@return self
function SelfA.dotMethod()
---@type self
local selfTypedVar = <warning descr="Undeclared variable 'self'.">self</warning>
local selfTypedVar

---@type SelfA
local someSelfA
Expand All @@ -46,7 +46,7 @@ end
---@return self
SelfA.lambdaMethod = function()
---@type self
local selfTypedVar = <warning descr="Undeclared variable 'self'.">self</warning>
local selfTypedVar

---@type SelfA
local someSelfA
Expand Down

0 comments on commit 5db79fb

Please sign in to comment.