Skip to content

Commit

Permalink
New inspection with detection for circular alias declarations.
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin-Dobell committed Jul 24, 2021
1 parent e29f03a commit e953eba
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.tang.intellij.lua.codeInsight.inspection.doc

import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import com.tang.intellij.lua.comment.psi.LuaDocTagAlias
import com.tang.intellij.lua.comment.psi.LuaDocVisitor
import com.tang.intellij.lua.search.SearchContext
import com.tang.intellij.lua.ty.ITy
import com.tang.intellij.lua.ty.ITyGeneric
import com.tang.intellij.lua.ty.ITyResolvable
import com.tang.intellij.lua.ty.TyUnion

class IllegalAliasInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : LuaDocVisitor() {
override fun visitTagAlias(o: LuaDocTagAlias) {
val alias = o.type
val context = SearchContext.get(o.project)
val resolved = alias.resolve(context)

val visitedTys = mutableSetOf<ITy>()
val pendingTys = if (resolved is TyUnion) {
visitedTys.add(resolved)
mutableListOf<ITy>().apply { addAll(resolved.getChildTypes()) }
} else {
mutableListOf(resolved)
}

while (pendingTys.isNotEmpty()) {
val pendingTy = pendingTys.removeLast()

if (visitedTys.add(pendingTy)) {
if (pendingTy == alias || (pendingTy as? ITyGeneric)?.base == alias) {
holder.registerProblem(o, "Alias '${alias.name}' circularly references itself.", ProblemHighlightType.ERROR)
return
}

val resolvedMemberTy = (pendingTy as? ITyResolvable)?.resolve(context) ?: pendingTy

if (resolvedMemberTy != pendingTy) {
if (resolvedMemberTy is TyUnion) {
pendingTys.addAll(resolvedMemberTy.getChildTypes())
} else {
pendingTys.add(resolvedMemberTy)
}
}
}
}
}
}
}
}
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 @@ -838,7 +838,7 @@ abstract class Ty(override val kind: TyKind) : ITy {
if (visitedTys.add(pendingTy)) {
val resolvedMemberTy = (pendingTy as? ITyResolvable)?.resolve(context) ?: pendingTy

if (resolvedMemberTy !== pendingTy) {
if (resolvedMemberTy != pendingTy) {
if (resolvedMemberTy is TyUnion) {
pendingTys.addAll(resolvedMemberTy.getChildTypes())
} else {
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/com/tang/intellij/lua/ty/TyGeneric.kt
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,17 @@ interface ITyGeneric : ITyResolvable {

override fun resolve(context: SearchContext, genericArgs: Array<out ITy>?): ITy {
val resolved = (base as? ITyResolvable)?.resolve(context, args) ?: base
return if (resolved !== base) resolved else this
return if (resolved != base) resolved else this
}
}

open class TyGeneric(override val args: Array<out ITy>, override val base: ITy) : Ty(TyKind.Generic), ITyGeneric {

override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}

return other is ITyGeneric && other.base == base && other.displayName == displayName
}

Expand Down Expand Up @@ -255,7 +259,8 @@ open class TyGeneric(override val args: Array<out ITy>, override val base: ITy)

if (otherBase != null) {
if (otherBase.equals(resolvedBase, context)) {
return args.size == otherArgs?.size && args.asSequence().zip(otherArgs.asSequence()).all { (arg, otherArg) ->
val baseArgCount = otherArgs?.size ?: 0
return baseArgCount == 0 || args.size == otherArgs?.size && args.asSequence().zip(otherArgs.asSequence()).all { (arg, otherArg) ->
// Args are always invariant as we don't support use-site variance nor immutable/read-only annotations
arg.equals(otherArg, context)
|| (flags and TyVarianceFlags.STRICT_UNKNOWN == 0 && otherArg.isUnknown)
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/META-INF/luanalysis-core.xml
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,15 @@
runForWholeFile="true"
implementationClass="com.tang.intellij.lua.codeInsight.inspection.doc.UnresolvedTypeInspection"/>

<localInspection
displayName="Illegal alias in emmy doc"
enabledByDefault="true"
groupName="Lua > Emmy doc"
level="ERROR"
language="Lua"
runForWholeFile="true"
implementationClass="com.tang.intellij.lua.codeInsight.inspection.doc.IllegalAliasInspection"/>

<localInspection
displayName="Illegal inheritance in emmy doc"
enabledByDefault="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package com.tang.intellij.test.completion

class TestAlias : TestCompletionBase() {
fun `test alias 1`() {
fun `test alias`() {
myFixture.configureByFile("class.lua")
doTest("""
--- test_alias.lua
Expand All @@ -35,4 +35,4 @@ class TestAlias : TestCompletionBase() {
assertTrue(it.contains("sayHello"))
}
}
}
}
4 changes: 2 additions & 2 deletions src/test/kotlin/com/tang/intellij/test/completion/TestSelf.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package com.tang.intellij.test.completion

class TestSelf : TestCompletionBase() {
fun `test alias 1`() {
fun `test self`() {
doTest("""
--- test_self.lua
Expand All @@ -38,4 +38,4 @@ class TestSelf : TestCompletionBase() {
assertTrue(it.contains("bb"))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package com.tang.intellij.test.inspections
import com.tang.intellij.lua.codeInsight.inspection.*
import com.tang.intellij.lua.codeInsight.inspection.doc.GenericConstraintInspection
import com.tang.intellij.lua.codeInsight.inspection.doc.GenericParameterShadowed
import com.tang.intellij.lua.codeInsight.inspection.doc.IllegalAliasInspection
import com.tang.intellij.lua.codeInsight.inspection.doc.IllegalInheritanceInspection
import com.tang.intellij.lua.lang.LuaLanguageLevel
import com.tang.intellij.lua.project.LuaSettings
import com.tang.intellij.lua.project.StdLibraryProvider
Expand All @@ -27,6 +29,8 @@ class TypeSafetyTest : LuaInspectionsTestBase(
AssignTypeInspection(),
GenericConstraintInspection(),
GenericParameterShadowed(),
IllegalAliasInspection(),
IllegalInheritanceInspection(),
IllegalOverrideInspection(),
MatchFunctionSignatureInspection(),
ReturnTypeInspection(),
Expand Down
39 changes: 0 additions & 39 deletions src/test/resources/inspections/recursive_alias.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,45 +36,6 @@ reallyIndirectGenericAlias = someGenericClass
someGenericClass = reallyIndirectGenericAlias



---@alias InfiniteAlias1 string|InfiniteAlias2
---@alias InfiniteAlias2 number|InfiniteAlias1

---@type InfiniteAlias1
local infiniteAlias1

---@type InfiniteAlias2
local infiniteAlias2

infiniteAlias1 = <error descr="Type mismatch. Required: 'InfiniteAlias1' Found: 'table'">{}</error>
infiniteAlias1 = 1
infiniteAlias1 = 'string'
infiniteAlias1 = infiniteAlias2
infiniteAlias2 = <error descr="Type mismatch. Required: 'InfiniteAlias2' Found: 'table'">{}</error>
infiniteAlias2 = 1
infiniteAlias2 = 'string'
infiniteAlias2 = infiniteAlias1


---@alias InfiniteGenericAlias1<T> T|InfiniteGenericAlias2<T>

---@alias InfiniteGenericAlias2<T> InfiniteGenericAlias1<T>

---@type InfiniteGenericAlias1<string>
local infiniteGenericAlias1

---@type InfiniteGenericAlias2<string>
local infiniteGenericAlias2

infiniteGenericAlias1 = <error descr="Type mismatch. Required: 'InfiniteGenericAlias1<string>' Found: '1'">1</error>
infiniteGenericAlias1 = 'string'
infiniteGenericAlias1 = infiniteGenericAlias2

infiniteGenericAlias2 = <error descr="Type mismatch. Required: 'InfiniteGenericAlias2<string>' Found: '1'">1</error>
infiniteGenericAlias2 = 'string'
infiniteGenericAlias2 = infiniteGenericAlias1


---@alias JSONObject table<string, JSONValue>
---@alias JSONArray JSONValue[]
---@alias JSONContainer JSONObject | JSONArray
Expand Down

0 comments on commit e953eba

Please sign in to comment.