fixed few bugs in compiler and plugin
This commit is contained in:
1 parent
37d093817e
commit
e447c778ed
10 files changed
+243
-45
No files matched your search
@@ -176,6 +176,8 @@ class Compiler(
|
||||
private val lambdaCaptureEntriesByRef: MutableMap<ValueFnRef, List<net.sergeych.lyng.bytecode.LambdaCaptureEntry>> =
|
||||
mutableMapOf()
|
||||
private val classFieldTypesByName: MutableMap<String, MutableMap<String, ObjClass>> = mutableMapOf()
|
||||
private val classMethodReturnTypeByName: MutableMap<String, MutableMap<String, ObjClass>> = mutableMapOf()
|
||||
private val classMethodReturnTypeDeclByName: MutableMap<String, MutableMap<String, TypeDecl>> = mutableMapOf()
|
||||
private val classScopeMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
|
||||
private val classScopeCallableMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
|
||||
private val encodedPayloadTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
||||
@@ -2614,7 +2616,34 @@ class Compiler(
|
||||
}
|
||||
|
||||
Token.Type.NOT -> {
|
||||
if (operand != null) throw ScriptError(t.pos, "unexpected operator not '!' ")
|
||||
if (operand != null) {
|
||||
val save = cc.savePos()
|
||||
val next = cc.next()
|
||||
if (next.type == Token.Type.NOT) {
|
||||
val operandRef = operand
|
||||
val receiverClass = resolveReceiverClassForMember(operandRef)
|
||||
val inferredType = resolveReceiverTypeDecl(operandRef)
|
||||
?: receiverClass?.let { TypeDecl.Simple(it.className, false) }
|
||||
if (inferredType == null) {
|
||||
operand = operandRef
|
||||
continue
|
||||
}
|
||||
if (inferredType == TypeDecl.TypeAny || inferredType == TypeDecl.TypeNullableAny) {
|
||||
operand = operandRef
|
||||
continue
|
||||
}
|
||||
val nonNullType = makeTypeDeclNonNullable(inferredType)
|
||||
operand = CastRef(
|
||||
operandRef,
|
||||
TypeDeclRef(nonNullType, t.pos),
|
||||
isNullable = false,
|
||||
atPos = t.pos
|
||||
)
|
||||
continue
|
||||
}
|
||||
cc.restorePos(save)
|
||||
throw ScriptError(t.pos, "unexpected operator not '!' ")
|
||||
}
|
||||
val op = parseTerm() ?: throw ScriptError(t.pos, "Expecting expression")
|
||||
operand = UnaryOpRef(UnaryOp.NOT, op)
|
||||
}
|
||||
@@ -3821,6 +3850,21 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeTypeDeclNonNullable(type: TypeDecl): TypeDecl {
|
||||
if (!type.isNullable) return type
|
||||
return when (type) {
|
||||
TypeDecl.TypeAny -> type
|
||||
TypeDecl.TypeNullableAny -> TypeDecl.TypeAny
|
||||
is TypeDecl.Function -> type.copy(nullable = false)
|
||||
is TypeDecl.Ellipsis -> type.copy(nullable = false)
|
||||
is TypeDecl.TypeVar -> type.copy(nullable = false)
|
||||
is TypeDecl.Union -> type.copy(nullable = false)
|
||||
is TypeDecl.Intersection -> type.copy(nullable = false)
|
||||
is TypeDecl.Simple -> TypeDecl.Simple(type.name, false)
|
||||
is TypeDecl.Generic -> TypeDecl.Generic(type.name, type.args, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeMiniTypeNullable(type: MiniTypeRef): MiniTypeRef {
|
||||
return when (type) {
|
||||
is MiniTypeName -> type.copy(nullable = true)
|
||||
@@ -4491,6 +4535,48 @@ class Compiler(
|
||||
is TypeDecl.Intersection -> "I:${type.options.joinToString("&") { typeDeclKey(it) }}"
|
||||
}
|
||||
|
||||
private fun classMethodReturnTypeDecl(targetClass: ObjClass?, name: String): TypeDecl? {
|
||||
if (targetClass == null) return null
|
||||
if (targetClass == ObjDynamic.type) return TypeDecl.TypeAny
|
||||
val member = targetClass.getInstanceMemberOrNull(name, includeAbstract = true)
|
||||
val declaringName = member?.declaringClass?.className
|
||||
if (declaringName != null) {
|
||||
classMethodReturnTypeDeclByName[declaringName]?.get(name)?.let { return it }
|
||||
classMethodReturnTypeByName[declaringName]?.get(name)?.let {
|
||||
return TypeDecl.Simple(it.className, false)
|
||||
}
|
||||
}
|
||||
classMethodReturnTypeDeclByName[targetClass.className]?.get(name)?.let { return it }
|
||||
classMethodReturnTypeByName[targetClass.className]?.get(name)?.let {
|
||||
return TypeDecl.Simple(it.className, false)
|
||||
}
|
||||
member?.typeDecl?.let { declaredType ->
|
||||
if (declaredType is TypeDecl.Function) return declaredType.returnType
|
||||
return declaredType
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun classMethodReturnClass(targetClass: ObjClass?, name: String): ObjClass? {
|
||||
if (targetClass == null) return null
|
||||
if (targetClass == ObjDynamic.type) return ObjDynamic.type
|
||||
classMethodReturnTypeDecl(targetClass, name)?.let { declared ->
|
||||
resolveTypeDeclObjClass(declared)?.let { return it }
|
||||
if (declared is TypeDecl.TypeVar) return Obj.rootObjectType
|
||||
}
|
||||
val member = targetClass.getInstanceMemberOrNull(name, includeAbstract = true)
|
||||
val declaringName = member?.declaringClass?.className
|
||||
if (declaringName != null) {
|
||||
classMethodReturnTypeByName[declaringName]?.get(name)?.let { return it }
|
||||
}
|
||||
classMethodReturnTypeByName[targetClass.className]?.get(name)?.let { return it }
|
||||
val declaredType = member?.typeDecl
|
||||
if (declaredType is TypeDecl.Function) {
|
||||
resolveTypeDeclObjClass(declaredType.returnType)?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun inferObjClassFromRef(ref: ObjRef): ObjClass? = when (ref) {
|
||||
is ConstRef -> ref.constValue as? ObjClass ?: ref.constValue.objClass
|
||||
is LocalVarRef -> nameObjClass[ref.name] ?: resolveClassByName(ref.name)
|
||||
@@ -4511,6 +4597,11 @@ class Compiler(
|
||||
is RangeRef -> ObjRange.type
|
||||
is ClassOperatorRef -> ObjClassType
|
||||
is CastRef -> resolveTypeRefClass(ref.castTypeRef())
|
||||
is IndexRef -> {
|
||||
val targetClass = resolveReceiverClassForMember(ref.targetRef)
|
||||
classMethodReturnClass(targetClass, "getAt")
|
||||
?: inferFieldReturnClass(targetClass, "getAt")
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -4547,6 +4638,12 @@ class Compiler(
|
||||
else -> TypeDecl.TypeVar("${typeDeclName(targetDecl)}.${ref.name}", false)
|
||||
}
|
||||
}
|
||||
is IndexRef -> {
|
||||
val targetDecl = resolveReceiverTypeDecl(ref.targetRef)
|
||||
inferMethodCallReturnTypeDecl("getAt", targetDecl)?.let { return it }
|
||||
val targetClass = resolveReceiverClassForMember(ref.targetRef)
|
||||
classMethodReturnTypeDecl(targetClass, "getAt")
|
||||
}
|
||||
is MethodCallRef -> methodReturnTypeDeclByRef[ref]
|
||||
is CallRef -> callReturnTypeDeclByRef[ref]
|
||||
is StatementRef -> (ref.statement as? ExpressionStatement)?.let { resolveReceiverTypeDecl(it.ref) }
|
||||
@@ -4616,6 +4713,12 @@ class Compiler(
|
||||
val targetClass = resolveReceiverClassForMember(ref.target)
|
||||
inferFieldReturnClass(targetClass, ref.name)
|
||||
}
|
||||
is IndexRef -> {
|
||||
val targetClass = resolveReceiverClassForMember(ref.targetRef)
|
||||
classMethodReturnClass(targetClass, "getAt")
|
||||
?: inferFieldReturnClass(targetClass, "getAt")
|
||||
?: inferMethodCallReturnClass("getAt")
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -5102,6 +5205,7 @@ class Compiler(
|
||||
}
|
||||
|
||||
private fun resolveTypeRefClass(ref: ObjRef): ObjClass? = when (ref) {
|
||||
is TypeDeclRef -> resolveTypeDeclObjClass(ref.decl())
|
||||
is ConstRef -> ref.constValue as? ObjClass
|
||||
is LocalSlotRef -> resolveTypeDeclObjClass(TypeDecl.Simple(ref.name, false)) ?: nameObjClass[ref.name]
|
||||
is LocalVarRef -> resolveTypeDeclObjClass(TypeDecl.Simple(ref.name, false)) ?: nameObjClass[ref.name]
|
||||
@@ -7885,6 +7989,23 @@ class Compiler(
|
||||
val rawFnStatements = parsedFnStatements?.let { unwrapBytecodeDeep(it) }
|
||||
val inferredReturnClass = returnTypeDecl?.let { resolveTypeDeclObjClass(it) }
|
||||
?: inferReturnClassFromStatement(rawFnStatements)
|
||||
if (declKind == SymbolKind.MEMBER && extTypeName == null) {
|
||||
val ownerClassName = (parentContext as? CodeContext.ClassBody)?.name
|
||||
if (ownerClassName != null) {
|
||||
val returnDecl = returnTypeDecl
|
||||
?: inferredReturnClass?.let { TypeDecl.Simple(it.className, false) }
|
||||
if (returnDecl != null) {
|
||||
classMethodReturnTypeDeclByName
|
||||
.getOrPut(ownerClassName) { mutableMapOf() }[name] = returnDecl
|
||||
resolveTypeDeclObjClass(returnDecl)?.let { returnClass ->
|
||||
classMethodReturnTypeByName
|
||||
.getOrPut(ownerClassName) { mutableMapOf() }[name] = returnClass
|
||||
classFieldTypesByName
|
||||
.getOrPut(ownerClassName) { mutableMapOf() }[name] = returnClass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (declKind != SymbolKind.MEMBER && inferredReturnClass != null) {
|
||||
callableReturnTypeByName[name] = inferredReturnClass
|
||||
val slotLoc = lookupSlotLocation(name, includeModule = true)
|
||||
|
||||
@@ -46,7 +46,7 @@ private val fallbackKeywordIds = setOf(
|
||||
"private", "protected", "static", "open", "extern", "init", "get", "set", "by", "step",
|
||||
// control flow and misc
|
||||
"if", "else", "when", "while", "do", "for", "try", "catch", "finally",
|
||||
"throw", "return", "break", "continue", "this", "null", "true", "false", "unset"
|
||||
"throw", "return", "break", "continue", "this", "null", "true", "false", "unset", "void"
|
||||
)
|
||||
|
||||
/** Maps lexer token type (and sometimes value) to a [HighlightKind]. */
|
||||
|
||||
@@ -179,6 +179,7 @@ object LyngLanguageTools {
|
||||
val source = analysis.source
|
||||
val out = ArrayList<LyngSemanticSpan>(128)
|
||||
val covered = HashSet<Pair<Int, Int>>()
|
||||
fun isCurrentSource(pos: Pos): Boolean = pos.source === source
|
||||
|
||||
fun addRange(start: Int, end: Int, kind: LyngSemanticKind) {
|
||||
if (start < 0 || end <= start || end > analysis.text.length) return
|
||||
@@ -187,6 +188,7 @@ object LyngLanguageTools {
|
||||
}
|
||||
|
||||
fun addName(pos: Pos, name: String, kind: LyngSemanticKind) {
|
||||
if (!isCurrentSource(pos)) return
|
||||
val s = source.offsetOf(pos)
|
||||
addRange(s, s + name.length, kind)
|
||||
}
|
||||
@@ -206,7 +208,9 @@ object LyngLanguageTools {
|
||||
addTypeSegments(t.returnType)
|
||||
}
|
||||
is MiniTypeVar -> {
|
||||
addRange(source.offsetOf(t.range.start), source.offsetOf(t.range.end), LyngSemanticKind.TypeRef)
|
||||
if (isCurrentSource(t.range.start) && isCurrentSource(t.range.end)) {
|
||||
addRange(source.offsetOf(t.range.start), source.offsetOf(t.range.end), LyngSemanticKind.TypeRef)
|
||||
}
|
||||
}
|
||||
is MiniTypeUnion -> {
|
||||
t.options.forEach { addTypeSegments(it) }
|
||||
@@ -262,7 +266,9 @@ object LyngLanguageTools {
|
||||
|
||||
mini.imports.forEach { imp ->
|
||||
imp.segments.forEach { seg ->
|
||||
addRange(source.offsetOf(seg.range.start), source.offsetOf(seg.range.end), LyngSemanticKind.TypeRef)
|
||||
if (isCurrentSource(seg.range.start) && isCurrentSource(seg.range.end)) {
|
||||
addRange(source.offsetOf(seg.range.start), source.offsetOf(seg.range.end), LyngSemanticKind.TypeRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -565,19 +565,6 @@ class TypesTest {
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun testNullableGenericTypes() = runTest {
|
||||
// eval("""
|
||||
// fun t<T>(): String =
|
||||
// when(T) {
|
||||
// is Object -> "%s is Object"(T::class.name)
|
||||
// else -> throw "It should not happen"
|
||||
// }
|
||||
// assert( Int is Object)
|
||||
// assertEquals( t<Int>(), "Class is Object")
|
||||
// """.trimIndent())
|
||||
// }
|
||||
|
||||
@Test fun testIndexer() = runTest {
|
||||
eval("""
|
||||
class Greeter {
|
||||
@@ -596,6 +583,38 @@ class TypesTest {
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test fun testIndexer2() = runTest {
|
||||
eval("""
|
||||
class Foo(bar)
|
||||
|
||||
class Greeter {
|
||||
override fun getAt(name): Foo = Foo("Hello, %s!"(name))
|
||||
}
|
||||
assertEquals("Hello, Bob!",Greeter()["Bob"].bar)
|
||||
val g = Greeter()
|
||||
assertEquals("Hello, Bob!",g["Bob"].bar)
|
||||
|
||||
// it should work with objects too:
|
||||
object Polite {
|
||||
override fun getAt(name): Foo? = Foo("How do you do, %s?"(name))
|
||||
}
|
||||
|
||||
assertEquals("How do you do, Bob?",Polite["Bob"].bar)
|
||||
assertEquals("How do you do, Bob?",Polite["Bob"]?.bar)
|
||||
assertEquals("How do you do, Bob?",Polite["Bob"]!!.bar)
|
||||
|
||||
class Greeter2 {
|
||||
override fun getAt(name): Foo? = Foo("How do you do, %s?"(name))
|
||||
}
|
||||
val g2 = Greeter2()
|
||||
assertEquals("How do you do, Bob?",g2["Bob"]?.bar)
|
||||
val g2v: Foo = g2["Bob"]!!
|
||||
assertEquals("How do you do, Bob?",g2v.bar)
|
||||
assertEquals("How do you do, Bob?",Greeter2()["Bob"].bar)
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
// @Test fun nonTrivialOperatorsTest() = runTest {
|
||||
// val s = Script.newScope()
|
||||
// s.eval("""
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
package net.sergeych.lyng.tools
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.miniast.MiniClassDecl
|
||||
import net.sergeych.lyng.miniast.MiniMemberTypeAliasDecl
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.Source
|
||||
import net.sergeych.lyng.miniast.*
|
||||
import net.sergeych.lyng.stdlib_included.rootLyng
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
@@ -127,4 +128,48 @@ class LyngLanguageToolsTest {
|
||||
val dis = LyngLanguageTools.disassembleSymbol(code, "add")
|
||||
assertTrue(!dis.contains("not a compiled body"), "Disassembly should be produced, got: $dis")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun languageTools_semanticHighlights_ignore_foreign_sources() {
|
||||
val localSource = Source("local.lyng", "val x = 1")
|
||||
val foreignSource = Source("defs.lyng.d", "val y = 2")
|
||||
val localStart = Pos(localSource, 0, 0)
|
||||
val foreignStart = Pos(foreignSource, 0, 0)
|
||||
|
||||
val mini = MiniScript(
|
||||
range = MiniRange(localStart, localStart),
|
||||
declarations = mutableListOf(
|
||||
MiniValDecl(
|
||||
range = MiniRange(foreignStart, foreignStart),
|
||||
name = "y",
|
||||
mutable = false,
|
||||
type = null,
|
||||
initRange = null,
|
||||
doc = null,
|
||||
nameStart = foreignStart
|
||||
)
|
||||
),
|
||||
imports = mutableListOf(
|
||||
MiniImport(
|
||||
range = MiniRange(foreignStart, foreignStart),
|
||||
segments = listOf(
|
||||
MiniImport.Segment("defs", MiniRange(foreignStart, foreignStart))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
val analysis = LyngAnalysisResult(
|
||||
source = localSource,
|
||||
text = localSource.text,
|
||||
mini = mini,
|
||||
binding = null,
|
||||
resolution = null,
|
||||
importedModules = emptyList(),
|
||||
diagnostics = emptyList(),
|
||||
lexicalHighlights = emptyList()
|
||||
)
|
||||
|
||||
val spans = LyngLanguageTools.semanticHighlights(analysis)
|
||||
assertTrue(spans.isEmpty(), "Semantic spans should ignore positions from foreign sources, got $spans")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user