Skip to content

Commit 5d85062

Browse files
committed
初步完成接口相关设施
1 parent 44c85cd commit 5d85062

File tree

5 files changed

+221
-24
lines changed

5 files changed

+221
-24
lines changed

EmmyLua-Common/src/main/ext/com/tang/intellij/lua/stubs/index/LuaClassMemberIndex.kt

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,41 @@ class LuaClassMemberIndex : StubIndex<Int, LuaClassMember>() {
7272
true
7373
}
7474
return founded
75-
// from supper
76-
// val superClassName = type.superClassName
77-
// if (superClassName != null && superClassName != className) {
78-
// return process(superClassName, fieldName, context, processor)
79-
// }
75+
}
76+
}
77+
return true
78+
}
79+
80+
fun processOrigin(
81+
className: String,
82+
fieldName: String,
83+
context: SearchContext,
84+
processor: Processor<LuaClassMember>,
85+
deep: Boolean = true
86+
): Boolean {
87+
val key = "$className*$fieldName"
88+
if (!process(key, context, processor))
89+
return false
90+
91+
if (deep) {
92+
val classDef = LuaClassIndex.find(className, context)
93+
if (classDef != null) {
94+
val type = classDef.type
95+
// from alias
96+
type.lazyInit(context)
97+
val notFound = type.processAlias(Processor {
98+
process(it, fieldName, context, processor, false)
99+
})
100+
if (!notFound)
101+
return false
80102

103+
val superClassName = type.superClassName
104+
if(superClassName != null) {
105+
val superClass = LuaClassIndex.find(superClassName, context)
106+
if(superClass is TyClass && !superClass.isInterface){
107+
return process(superClassName, fieldName, context, processor, true)
108+
}
109+
}
81110
}
82111
}
83112
return true
@@ -109,6 +138,32 @@ class LuaClassMemberIndex : StubIndex<Int, LuaClassMember>() {
109138
return perfect
110139
}
111140

141+
fun findOrigin(type: ITyClass, fieldName: String, context: SearchContext): LuaClassMember? {
142+
var perfect: LuaClassMember? = null
143+
var docField: LuaDocTagField? = null
144+
var tableField: LuaTableField? = null
145+
processOrigin(type.className, fieldName, context, {
146+
when (it) {
147+
is LuaDocTagField -> {
148+
docField = it
149+
false
150+
}
151+
is LuaTableField -> {
152+
tableField = it
153+
true
154+
}
155+
else -> {
156+
if (perfect == null)
157+
perfect = it
158+
true
159+
}
160+
}
161+
})
162+
if (docField != null) return docField
163+
if (tableField != null) return tableField
164+
return perfect
165+
}
166+
112167
fun processAll(
113168
type: ITyClass,
114169
fieldName: String,

EmmyLua-Common/src/main/java/com/tang/intellij/lua/ty/TyClass.kt

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.tang.intellij.lua.psi.*
3131
import com.tang.intellij.lua.psi.search.LuaClassInheritorsSearch
3232
import com.tang.intellij.lua.psi.search.LuaShortNamesManager
3333
import com.tang.intellij.lua.search.SearchContext
34+
import com.tang.intellij.lua.stubs.index.LuaClassMemberIndex
3435
import com.tang.lsp.nameRange
3536

3637
interface ITyClass : ITy {
@@ -74,6 +75,8 @@ interface ITyClass : ITy {
7475
}
7576
}
7677

78+
79+
fun findOriginMember(name: String, searchContext: SearchContext): LuaClassMember?
7780
fun findMember(name: String, searchContext: SearchContext): LuaClassMember?
7881
fun findMemberType(name: String, searchContext: SearchContext): ITy?
7982
fun findSuperMember(name: String, searchContext: SearchContext): LuaClassMember?
@@ -161,6 +164,10 @@ abstract class TyClass(
161164
}
162165
}
163166

167+
override fun findOriginMember(name: String, searchContext: SearchContext): LuaClassMember? {
168+
return LuaClassMemberIndex.findOrigin(this, name, searchContext)
169+
}
170+
164171
override fun findMember(name: String, searchContext: SearchContext): LuaClassMember? {
165172
return LuaShortNamesManager.getInstance(searchContext.project).findMember(this, name, searchContext)
166173
}
@@ -197,6 +204,7 @@ abstract class TyClass(
197204
aliasName = tyClass.aliasName
198205
superClassName = tyClass.superClassName
199206
interfaceNames = tyClass.interfaceNames
207+
isInterface = tyClass.isInterface
200208
}
201209
}
202210

@@ -210,17 +218,18 @@ abstract class TyClass(
210218
return null
211219
}
212220

213-
override fun getInterfaces(context: SearchContext): List<ITy>? {
221+
override fun getInterfaces(context: SearchContext): List<ITyClass>? {
214222
if (interfaceNames == null) {
215223
return null
216224
}
217225

218-
val result = mutableListOf<ITy>()
226+
val result = mutableListOf<ITyClass>()
219227
interfaceNames!!.forEach {
220-
if (it != superClassName) {
221-
val ty = Ty.getBuiltin(it) ?: LuaShortNamesManager.getInstance(context.project)
222-
.findClass(it, context)?.type
223-
if (ty != null) {
228+
val ty = Ty.getBuiltin(it) ?: LuaShortNamesManager.getInstance(context.project)
229+
.findClass(it, context)?.type
230+
if (ty != null && ty is ITyClass) {
231+
ty.lazyInit(context)
232+
if (ty.isInterface) {
224233
result.add(ty)
225234
}
226235
}
@@ -231,6 +240,7 @@ abstract class TyClass(
231240
override fun subTypeOf(other: ITy, context: SearchContext, strict: Boolean): Boolean {
232241
// class extends table
233242
if (other == Ty.TABLE) return true
243+
if (other is TyGeneric && other.base == Ty.TABLE) return true
234244
if (super.subTypeOf(other, context, strict)) return true
235245

236246
// Lazy init for superclass
@@ -241,6 +251,33 @@ abstract class TyClass(
241251
isSubType = superType == other
242252
!isSubType
243253
}
254+
255+
if (!isSubType && (other is ITyClass)) {
256+
other.lazyInit(context)
257+
if (other.isInterface) {
258+
isSubType = true
259+
other.processMembers(context, { _, member ->
260+
if (member.name == null) {
261+
isSubType = false
262+
return@processMembers
263+
}
264+
val thisMember = findMember(member.name!!, context)
265+
if (thisMember == null) {
266+
isSubType = false
267+
return@processMembers
268+
}
269+
270+
val thisMemberType = thisMember.guessType(context)
271+
val interfaceMemberType = member.guessType(context)
272+
if (!thisMemberType.subTypeOf(interfaceMemberType, context, strict)) {
273+
isSubType = false
274+
return@processMembers
275+
}
276+
277+
}, false)
278+
}
279+
}
280+
244281
return isSubType
245282
}
246283

@@ -289,6 +326,9 @@ abstract class TyClass(
289326
}
290327
if (!processor(cls))
291328
return false
329+
if (cls.isInterface) {
330+
break
331+
}
292332
}
293333
cur = cls
294334
}
@@ -345,7 +385,7 @@ class TyPsiDocClass(tagClass: LuaDocTagClass) : TyClass(tagClass.name) {
345385
interfaceNames = classList.map { it.text }
346386
}
347387
}
348-
if(tagClass.`interface` != null){
388+
if (tagClass.`interface` != null) {
349389
isInterface = true
350390
}
351391

EmmyLua-LS/src/main/kotlin/com/tang/vscode/diagnostics/DiagnosticsService.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.intellij.psi.PsiElement
55
import com.intellij.psi.PsiErrorElement
66
import com.intellij.psi.util.PsiTreeUtil
77
import com.tang.intellij.lua.comment.psi.LuaDocPsiElement
8+
import com.tang.intellij.lua.comment.psi.LuaDocTagClass
89
import com.tang.intellij.lua.psi.*
910
import com.tang.intellij.lua.search.SearchContext
1011
import com.tang.intellij.lua.ty.*
@@ -51,11 +52,13 @@ object DiagnosticsService {
5152
DeprecatedInspection.nameExprDeprecatedInspections(it, file, diagnostics)
5253
UndeclaredVariableInspection.undeclaredVariableInspections(it, file, diagnostics)
5354
}
55+
is LuaDocTagClass -> {
56+
InheritInspection.inheritInspections(it, file, diagnostics)
57+
}
5458
}
5559
true
5660
}
5761
}
5862

5963

60-
6164
}

EmmyLua-LS/src/main/kotlin/com/tang/vscode/diagnostics/inspections/FunctionInspection.kt

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@ object FunctionInspection {
5757
val paramType = param.guessType(context)
5858
if (!paramTypeCheck(pi, param, context)) {
5959
val diagnostic = Diagnostic()
60-
diagnostic.message =
61-
"Type mismatch '${paramType.displayName}' not match type '${pi.ty.displayName}'"
60+
diagnostic.message = if (paramType is TyClass && paramType.isInterface) {
61+
"Type mismatch '${paramType.displayName}' not match interface '${paramType.displayName}'"
62+
} else {
63+
"Type mismatch '${paramType.displayName}' not match type '${paramType.displayName}'"
64+
}
6265
diagnostic.severity = Severity.makeSeverity(DiagnosticsOptions.parameterValidation)
6366
diagnostic.range = param.textRange.toRange(file)
6467
diagnostics.add(diagnostic)
@@ -94,15 +97,6 @@ object FunctionInspection {
9497
return false
9598
}
9699

97-
// 由于没有接口 interface
98-
// 那么将匿名表传递给具有特定类型的定义类型也都被认为是合理的
99-
// 暂时不做field检查
100-
if (variable is LuaTableExpr &&
101-
(defineType.kind == TyKind.Class || defineType.kind == TyKind.Array || defineType.kind == TyKind.Tuple)
102-
) {
103-
return true
104-
}
105-
106100
// 类似于回调函数的写法,不写传参是非常普遍的,所以只需要认为定义类型是个函数就通过
107101
if (variable is LuaClosureExpr && defineType.kind == TyKind.Function) {
108102
return true
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.tang.vscode.diagnostics.inspections
2+
3+
import com.intellij.openapi.util.TextRange
4+
import com.tang.intellij.lua.comment.psi.LuaDocTagClass
5+
import com.tang.intellij.lua.psi.search.LuaShortNamesManager
6+
import com.tang.intellij.lua.search.SearchContext
7+
import com.tang.intellij.lua.ty.ITyClass
8+
import com.tang.intellij.lua.ty.TyPsiDocClass
9+
import com.tang.lsp.ILuaFile
10+
import com.tang.lsp.toRange
11+
import org.eclipse.lsp4j.Diagnostic
12+
import org.eclipse.lsp4j.DiagnosticSeverity
13+
import org.eclipse.lsp4j.Range
14+
15+
object InheritInspection {
16+
fun inheritInspections(docClass: LuaDocTagClass, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
17+
val superRef = docClass.superClassNameRef
18+
if (superRef != null) {
19+
val classList = superRef.classNameRefList
20+
21+
val context = SearchContext.get(docClass.project)
22+
val originType = docClass.type
23+
if (originType.isInterface && classList.isNotEmpty()) {
24+
val diagnostic = Diagnostic()
25+
diagnostic.message =
26+
"Interface ‘${docClass.text}’ do not support inheritance"
27+
diagnostic.severity = DiagnosticSeverity.Warning
28+
diagnostic.range = docClass.textRange.toRange(file)
29+
diagnostics.add(diagnostic)
30+
return
31+
}
32+
33+
for (i in 0 until classList.size) {
34+
val clazz = LuaShortNamesManager.getInstance(docClass.project).findClass(classList[i].text, context)
35+
36+
if (clazz != null) {
37+
val clazzType = clazz.type
38+
if (i != 0 && !clazzType.isInterface) {
39+
val diagnostic = Diagnostic()
40+
diagnostic.message =
41+
"Multiple inheritance cannot inherit non-interface classes '${classList[i].text}'"
42+
diagnostic.severity = DiagnosticSeverity.Warning
43+
diagnostic.range = classList[i].textRange.toRange(file)
44+
diagnostics.add(diagnostic)
45+
}
46+
47+
if (clazzType.isInterface) {
48+
interfaceCheck(originType, clazzType, context, docClass.textRange.toRange(file), diagnostics)
49+
}
50+
} else {
51+
val diagnostic = Diagnostic()
52+
diagnostic.message = "Inherit an undefined class or interface '${classList[i].text}'"
53+
diagnostic.severity = DiagnosticSeverity.Warning
54+
diagnostic.range = classList[i].textRange.toRange(file)
55+
diagnostics.add(diagnostic)
56+
}
57+
}
58+
}
59+
}
60+
61+
private fun interfaceCheck(
62+
originType: ITyClass,
63+
interfaceType: ITyClass,
64+
context: SearchContext,
65+
range: Range,
66+
diagnostics: MutableList<Diagnostic>
67+
): Boolean {
68+
var isSubType = true
69+
interfaceType.processMembers(context, { _, member ->
70+
if (member.name == null) {
71+
isSubType = false
72+
return@processMembers
73+
}
74+
val originMember = originType.findOriginMember(member.name!!, context)
75+
if (originMember == null) {
76+
val diagnostic = Diagnostic()
77+
diagnostic.message =
78+
"Does not implement member ${member.name}:${member.guessType(context).displayName} of interface '${interfaceType.displayName}'"
79+
diagnostic.severity = DiagnosticSeverity.Warning
80+
diagnostic.range = range
81+
diagnostics.add(diagnostic)
82+
isSubType = false
83+
return@processMembers
84+
}
85+
86+
val thisMemberType = originMember.guessType(context)
87+
val interfaceMemberType = member.guessType(context)
88+
if (!thisMemberType.subTypeOf(interfaceMemberType, context, true)) {
89+
val diagnostic = Diagnostic()
90+
diagnostic.message =
91+
"Member '${member.name}' is not compatible with the type '${interfaceMemberType.displayName}'" +
92+
" of interface '${interfaceType.displayName}' 's member '${member.name}'"
93+
diagnostic.severity = DiagnosticSeverity.Warning
94+
diagnostic.range = range
95+
diagnostics.add(diagnostic)
96+
isSubType = false
97+
return@processMembers
98+
}
99+
100+
}, false)
101+
102+
return isSubType
103+
}
104+
105+
}

0 commit comments

Comments
 (0)