From e953eba23dd88757d1d859d59ace2d1ba55deeca Mon Sep 17 00:00:00 2001 From: Benjamin Dobell Date: Sun, 25 Jul 2021 05:34:40 +1000 Subject: [PATCH] New inspection with detection for circular alias declarations. --- .../inspection/doc/IllegalAliasInspection.kt | 71 +++++++++++++++++++ src/main/java/com/tang/intellij/lua/ty/Ty.kt | 2 +- .../com/tang/intellij/lua/ty/TyGeneric.kt | 9 ++- .../resources/META-INF/luanalysis-core.xml | 9 +++ .../intellij/test/completion/TestAlias.kt | 4 +- .../tang/intellij/test/completion/TestSelf.kt | 4 +- .../test/inspections/TypeSafetyTest.kt | 4 ++ .../resources/inspections/recursive_alias.lua | 39 ---------- 8 files changed, 96 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/tang/intellij/lua/codeInsight/inspection/doc/IllegalAliasInspection.kt diff --git a/src/main/java/com/tang/intellij/lua/codeInsight/inspection/doc/IllegalAliasInspection.kt b/src/main/java/com/tang/intellij/lua/codeInsight/inspection/doc/IllegalAliasInspection.kt new file mode 100644 index 000000000..3b3c697ee --- /dev/null +++ b/src/main/java/com/tang/intellij/lua/codeInsight/inspection/doc/IllegalAliasInspection.kt @@ -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() + val pendingTys = if (resolved is TyUnion) { + visitedTys.add(resolved) + mutableListOf().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) + } + } + } + } + } + } + } +} diff --git a/src/main/java/com/tang/intellij/lua/ty/Ty.kt b/src/main/java/com/tang/intellij/lua/ty/Ty.kt index 127c657e1..64b4d2f9c 100644 --- a/src/main/java/com/tang/intellij/lua/ty/Ty.kt +++ b/src/main/java/com/tang/intellij/lua/ty/Ty.kt @@ -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 { diff --git a/src/main/java/com/tang/intellij/lua/ty/TyGeneric.kt b/src/main/java/com/tang/intellij/lua/ty/TyGeneric.kt index 7b0e1569d..772d06f6f 100644 --- a/src/main/java/com/tang/intellij/lua/ty/TyGeneric.kt +++ b/src/main/java/com/tang/intellij/lua/ty/TyGeneric.kt @@ -123,13 +123,17 @@ interface ITyGeneric : ITyResolvable { override fun resolve(context: SearchContext, genericArgs: Array?): 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, 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 } @@ -255,7 +259,8 @@ open class TyGeneric(override val args: Array, 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) diff --git a/src/main/resources/META-INF/luanalysis-core.xml b/src/main/resources/META-INF/luanalysis-core.xml index f67a33b8d..7eeeda204 100644 --- a/src/main/resources/META-INF/luanalysis-core.xml +++ b/src/main/resources/META-INF/luanalysis-core.xml @@ -359,6 +359,15 @@ runForWholeFile="true" implementationClass="com.tang.intellij.lua.codeInsight.inspection.doc.UnresolvedTypeInspection"/> + + {} -infiniteAlias1 = 1 -infiniteAlias1 = 'string' -infiniteAlias1 = infiniteAlias2 -infiniteAlias2 = {} -infiniteAlias2 = 1 -infiniteAlias2 = 'string' -infiniteAlias2 = infiniteAlias1 - - ----@alias InfiniteGenericAlias1 T|InfiniteGenericAlias2 - ----@alias InfiniteGenericAlias2 InfiniteGenericAlias1 - ----@type InfiniteGenericAlias1 -local infiniteGenericAlias1 - ----@type InfiniteGenericAlias2 -local infiniteGenericAlias2 - -infiniteGenericAlias1 = 1 -infiniteGenericAlias1 = 'string' -infiniteGenericAlias1 = infiniteGenericAlias2 - -infiniteGenericAlias2 = 1 -infiniteGenericAlias2 = 'string' -infiniteGenericAlias2 = infiniteGenericAlias1 - - ---@alias JSONObject table ---@alias JSONArray JSONValue[] ---@alias JSONContainer JSONObject | JSONArray