Skip to content

Commit

Permalink
Merge pull request #168 from pontem-network/index-expr
Browse files Browse the repository at this point in the history
Index expr support for compiler v2
  • Loading branch information
mkurnikov authored Jul 4, 2024
2 parents 8e7da61 + 4eb5ac0 commit 5a76d49
Show file tree
Hide file tree
Showing 48 changed files with 2,571 additions and 209 deletions.
5 changes: 5 additions & 0 deletions src/main/grammars/MoveParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,7 @@ CallExpr ::= (Path &'(') ValueArgumentList {
implements = [
"org.move.lang.core.psi.PathExpr"
"org.move.lang.core.psi.ext.MvCallable"
"org.move.lang.core.psi.MvAcquireTypesOwner"
]
}
ValueArgumentList ::= '(' ValueArgumentList_items? ')' {
Expand Down Expand Up @@ -1030,11 +1031,15 @@ MethodCall ::= IDENTIFIER ColonTypeArgumentList? ValueArgumentList
"org.move.lang.core.psi.ext.MvMethodOrField"
"org.move.lang.core.psi.ext.MvMethodOrPath"
"org.move.lang.core.psi.ext.MvCallable"
"org.move.lang.core.psi.MvAcquireTypesOwner"
]
mixin = "org.move.lang.core.psi.ext.MvMethodCallMixin"
}

IndexExpr ::= Expr IndexArg
{
implements = [ "org.move.lang.core.psi.MvAcquireTypesOwner" ]
}
// Do not inline this rule, it breaks expression parsing
private IndexArg ::= '[' Expr ']'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class MvProjectSettingsService(
val dumpStateOnTestFailure: Boolean get() = state.dumpStateOnTestFailure

val enableResourceAccessControl: Boolean get() = state.enableResourceAccessControl
val enableIndexExpr: Boolean get() = state.enableIndexExpr
val addCompilerV2CLIFlags: Boolean get() = state.addCompilerV2CLIFlags

// default values for settings
Expand All @@ -50,6 +51,9 @@ class MvProjectSettingsService(
@AffectsParseTree
var enableResourceAccessControl: Boolean by property(false)

@AffectsHighlighting
var enableIndexExpr: Boolean by property(false)

@AffectsMoveProjectsMetadata
var fetchAptosDeps: Boolean by property(false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ abstract class MvProjectSettingsServiceBase<T: MvProjectSettingsBase<T>>(
}

@TestOnly
fun modifyTemporary(parentDisposable: Disposable, modifySettings: (T) -> Unit) {
fun modifyTemporary(testRootDisposable: Disposable, modifySettings: (T) -> Unit) {
val oldState = state
loadState(oldState.copy().also(modifySettings))
Disposer.register(parentDisposable) {
Disposer.register(testRootDisposable) {
loadState(oldState)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ class PerProjectAptosConfigurable(val project: Project): BoundConfigurable("Apto
)
.bindSelected(state::enableResourceAccessControl)
}
row {
checkBox("Enable indexing")
.comment(
"Enables resource and vector indexing (i.e. v[0], R[@0x1]) " +
"for the Move files."
)
.bindSelected(state::enableIndexExpr)
}
}
group("Command Line Options") {
row {
Expand Down Expand Up @@ -102,6 +110,7 @@ class PerProjectAptosConfigurable(val project: Project): BoundConfigurable("Apto
it.skipFetchLatestGitDeps = state.skipFetchLatestGitDeps
it.dumpStateOnTestFailure = state.dumpStateOnTestFailure
it.enableResourceAccessControl = state.enableResourceAccessControl
it.enableIndexExpr = state.enableIndexExpr
it.addCompilerV2CLIFlags = state.addCompilerV2CLIFlags
it.fetchAptosDeps = state.fetchAptosDeps
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/org/move/ide/annotator/MvSyntaxErrorAnnotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.move.ide.annotator
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.move.cli.settings.moveSettings
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.*
import org.move.lang.utils.Diagnostic
Expand All @@ -20,6 +21,7 @@ class MvSyntaxErrorAnnotator: MvAnnotatorBase() {
override fun visitStruct(s: MvStruct) = checkStruct(moveHolder, s)
override fun visitFunction(o: MvFunction) = checkFunction(moveHolder, o)
override fun visitSpecFunction(o: MvSpecFunction) = checkSpecFunction(moveHolder, o)
override fun visitIndexExpr(o: MvIndexExpr) = checkIndexExpr(moveHolder, o)
}
element.accept(visitor)
}
Expand Down Expand Up @@ -56,6 +58,14 @@ class MvSyntaxErrorAnnotator: MvAnnotatorBase() {
}
}

private fun checkIndexExpr(holder: MvAnnotationHolder, indexExpr: MvIndexExpr) {
if (!indexExpr.project.moveSettings.enableIndexExpr) {
Diagnostic
.IndexExprIsNotAllowed(indexExpr)
.addToHolder(holder)
}
}

private fun checkStruct(holder: MvAnnotationHolder, struct: MvStruct) {
val native = struct.native ?: return
val errorRange = TextRange.create(native.startOffset, struct.structKw.endOffset)
Expand Down Expand Up @@ -154,6 +164,7 @@ class MvSyntaxErrorAnnotator: MvAnnotatorBase() {
}
}

@Suppress("CompanionObjectInExtension")
companion object {
private val INTEGER_WITH_SUFFIX_REGEX =
Regex("([0-9a-zA-Z_]+)(u[0-9]{1,4})")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.move.lang.core.psi.*
class WrapWithParensExprFix(castExpr: MvCastExpr) : DiagnosticIntentionFix<MvCastExpr>(castExpr) {
override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo =
IntentionPreviewInfo.EMPTY

override fun getFamilyName(): String = "Wrap cast with ()"
override fun getText(): String = "Wrap cast with ()"

Expand All @@ -23,7 +24,6 @@ class WrapWithParensExprFix(castExpr: MvCastExpr) : DiagnosticIntentionFix<MvCas
}

private fun wrapWithParensExpr(psiFactory: MvPsiFactory, expr: MvExpr): MvParensExpr {
val parensExpr = psiFactory.expr<MvParensExpr>("(dummy_ident)")
parensExpr.expr?.replace(expr)
val parensExpr = psiFactory.wrapWithParens(expr)
return expr.replace(parensExpr) as MvParensExpr
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package org.move.ide.inspections

import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.codeInspection.*
import com.intellij.model.SideEffectGuard
import com.intellij.model.SideEffectGuard.EffectType.EXEC
import com.intellij.model.SideEffectGuard.EffectType.SETTINGS
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
Expand Down Expand Up @@ -107,6 +110,9 @@ abstract class DiagnosticIntentionFix<T : PsiElement>(element: T) :
override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo =
IntentionPreviewInfo.EMPTY

override fun generatePreview(project: Project, editor: Editor, file: PsiFile): IntentionPreviewInfo =
IntentionPreviewInfo.EMPTY

override fun getFamilyName(): String = text

override fun isAvailable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.move.ide.inspections

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import org.move.cli.settings.moveSettings
import org.move.ide.inspections.fixes.AddAcquiresFix
import org.move.ide.presentation.fullnameNoArgs
import org.move.lang.core.psi.*
Expand All @@ -13,31 +14,39 @@ import org.move.lang.core.types.ty.TyStruct
import org.move.lang.core.types.ty.TyTypeParameter
import org.move.lang.moveProject

class MvMissingAcquiresInspection : MvLocalInspectionTool() {
class MvMissingAcquiresInspection: MvLocalInspectionTool() {

override val isSyntaxOnly: Boolean get() = true

override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : MvVisitor() {
override fun visitCallExpr(o: MvCallExpr) = visitCallable(o)
override fun visitMethodCall(o: MvMethodCall) = visitCallable(o)
object: MvVisitor() {
override fun visitCallExpr(o: MvCallExpr) = visitAcquiredTypesOwner(o)
override fun visitMethodCall(o: MvMethodCall) = visitAcquiredTypesOwner(o)
override fun visitIndexExpr(o: MvIndexExpr) {
if (!o.project.moveSettings.enableIndexExpr) return
visitAcquiredTypesOwner(o)
}

private fun visitCallable(callable: MvCallable) {
val outerFunction = callable.containingFunction ?: return
private fun visitAcquiredTypesOwner(element: MvAcquireTypesOwner) {
val outerFunction = element.containingFunction ?: return
if (outerFunction.isInline) return

val acquiresContext = callable.moveProject?.acquiresContext ?: return
val acquiresContext = element.moveProject?.acquiresContext ?: return
val inference = outerFunction.inference(false)

val existingTypes = acquiresContext.getFunctionTypes(outerFunction)
val existingTypeNames =
existingTypes.map { it.fullnameNoArgs() }.toSet()

val callExprTypes = acquiresContext.getCallTypes(callable, inference)
val callAcquiresTypes = when (element) {
is MvCallable -> acquiresContext.getCallTypes(element, inference)
is MvIndexExpr -> acquiresContext.getIndexExprTypes(element, inference)
else -> return
}

val existingTypeNames =
existingTypes.map { it.fullnameNoArgs() }.toSet()
val currentModule = outerFunction.module ?: return
val missingTypes =
callExprTypes.mapNotNull { acqTy ->
callAcquiresTypes.mapNotNull { acqTy ->
when (acqTy) {
is TyTypeParameter ->
acqTy.origin.takeIf { tyOrigin -> existingTypes.all { tyOrigin != it } }
Expand All @@ -60,7 +69,7 @@ class MvMissingAcquiresInspection : MvLocalInspectionTool() {
val name = outerFunction.name ?: return
val missingNames = missingTypes.mapNotNull { it.name }
holder.registerProblem(
callable,
element,
"Function '$name' is not marked as 'acquires ${missingNames.joinToString()}'",
ProblemHighlightType.GENERIC_ERROR,
AddAcquiresFix(outerFunction, missingNames)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,36 @@ import org.move.ide.inspections.fixes.RemoveAcquiresFix
import org.move.ide.presentation.fullnameNoArgs
import org.move.ide.presentation.itemDeclaredInModule
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.MvCallable
import org.move.lang.core.types.infer.AcquireTypesOwnerVisitor
import org.move.lang.core.types.infer.acquiresContext
import org.move.lang.core.types.infer.inference
import org.move.lang.core.types.infer.loweredType
import org.move.lang.moveProject


class MvUnusedAcquiresTypeInspection : MvLocalInspectionTool() {
override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor {
val annotationHolder = Holder(holder)
return object : MvVisitor() {
class MvUnusedAcquiresTypeInspection: MvLocalInspectionTool() {
override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor =
object: MvVisitor() {
override fun visitAcquiresType(o: MvAcquiresType) {
val function = o.parent as? MvFunction ?: return
val currentModule = function.module ?: return
val acquiresContext = o.moveProject?.acquiresContext ?: return
val inference = function.inference(false)

val callAcquiresTypes = mutableSetOf<String>()
for (callExpr in inference.callableTypes.keys) {
val types = acquiresContext.getCallTypes(callExpr, inference)
callAcquiresTypes.addAll(
types.map { it.fullnameNoArgs() })
val visitor = object: AcquireTypesOwnerVisitor() {
override fun visitAcquireTypesOwner(acqTypesOwner: MvAcquireTypesOwner) {
val types =
when (acqTypesOwner) {
is MvCallable -> acquiresContext.getCallTypes(acqTypesOwner, inference)
is MvIndexExpr -> acquiresContext.getIndexExprTypes(acqTypesOwner, inference)
else -> error("when is exhaustive")
}
callAcquiresTypes.addAll(types.map { it.fullnameNoArgs() })
}
}
visitor.visitElement(function)

val unusedTypeIndices = mutableListOf<Int>()
val visitedTypes = mutableSetOf<String>()
Expand All @@ -54,24 +62,21 @@ class MvUnusedAcquiresTypeInspection : MvLocalInspectionTool() {
}
if (unusedTypeIndices.size == function.acquiresPathTypes.size) {
// register whole acquiresType
annotationHolder.registerUnusedAcquires(o)
holder.registerUnusedAcquires(o)
return
}
for (idx in unusedTypeIndices) {
annotationHolder.registerUnusedAcquires(function.acquiresPathTypes[idx])
holder.registerUnusedAcquires(function.acquiresPathTypes[idx])
}
}
}
}

class Holder(val problemsHolder: ProblemsHolder) {
fun registerUnusedAcquires(ref: PsiElement) {
problemsHolder.registerProblem(
ref,
"Unused acquires clause",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveAcquiresFix(ref)
)
}
private fun ProblemsHolder.registerUnusedAcquires(ref: PsiElement) {
this.registerProblem(
ref,
"Unused acquires clause",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveAcquiresFix(ref)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import org.move.ide.inspections.fixes.ReplaceWithMethodCallFix
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.getTyItemModule
import org.move.lang.core.psi.ext.isMsl
import org.move.lang.core.psi.ext.itemModule
import org.move.lang.core.psi.ext.valueArguments
import org.move.lang.core.types.infer.foldTyTypeParameterWith
import org.move.lang.core.types.infer.inference
Expand All @@ -18,7 +18,7 @@ class ReplaceWithMethodCallInspection: MvLocalInspectionTool() {
override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor {
return object: MvVisitor() {
override fun visitCallExpr(callExpr: MvCallExpr) {
val function = callExpr.path.reference?.resolve() as? MvFunction ?: return
val function = callExpr.path.reference?.resolveWithAliases() as? MvFunction ?: return
val msl = callExpr.isMsl()
val inference = callExpr.inference(msl) ?: return

Expand All @@ -28,7 +28,7 @@ class ReplaceWithMethodCallInspection: MvLocalInspectionTool() {

val moveProject = callExpr.moveProject ?: return
val methodModule = function.module ?: return
val itemModule = moveProject.getTyItemModule(firstArgExprTy) ?: return
val itemModule = firstArgExprTy.itemModule(moveProject) ?: return
if (methodModule != itemModule) return

val selfTy = function.selfParamTy(msl)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.move.ide.inspections.compilerV2

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import org.move.ide.inspections.MvLocalInspectionTool
import org.move.ide.inspections.compilerV2.fixes.ReplaceWithIndexExprFix
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.argumentExprs
import org.move.lang.core.psi.ext.is0x1Address
import org.move.lang.core.psi.ext.isMsl
import org.move.lang.core.types.infer.inference
import org.move.lang.core.types.ty.isCopy
import org.move.lang.moveProject

class ReplaceWithIndexExprInspection: MvLocalInspectionTool() {
override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor {
return object: MvVisitor() {
override fun visitCallExpr(callExpr: MvCallExpr) {
val function = callExpr.path.reference?.resolveWithAliases() as? MvFunction ?: return
val module = function.module ?: return
val moveProject = function.moveProject ?: return
val msl = callExpr.isMsl()
val inference = callExpr.inference(msl) ?: return
val callExprRange = callExpr.textRange
// vector methods
if (inference.typeErrors.any { callExprRange.contains(it.range()) }) {
// type error inside the call expr
return
}
if (module.name == "vector" && module.is0x1Address(moveProject)) {
val parentExpr = callExpr.parent
if (function.name == "borrow" && parentExpr is MvDerefExpr) {
val receiverParamExpr = callExpr.argumentExprs.firstOrNull() ?: return
if (receiverParamExpr is MvBorrowExpr) {
val itemExpr = receiverParamExpr.expr ?: return
if (!inference.getExprType(itemExpr).isCopy) {
// cannot borrow deref without copy
return
}
}
holder.registerProblem(
parentExpr,
"Can be replaced with index expr",
ProblemHighlightType.WEAK_WARNING,
ReplaceWithIndexExprFix(parentExpr)
)
}
}
}
}
}
}
Loading

0 comments on commit 5a76d49

Please sign in to comment.