diff --git a/build.gradle.kts b/build.gradle.kts index 75171ecf0..b1855e276 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,7 +70,7 @@ version = pluginVersion plugins { id("java") kotlin("jvm") version "1.9.22" - id("org.jetbrains.intellij.platform") version "2.0.0-rc1" + id("org.jetbrains.intellij.platform") version "2.0.0" id("org.jetbrains.grammarkit") version "2022.3.2.2" id("net.saliman.properties") version "1.5.2" id("org.gradle.idea") @@ -106,7 +106,7 @@ allprojects { intellijPlatform { create(prop("platformType"), prop("platformVersion"), useInstaller = useInstaller) testFramework(TestFrameworkType.Platform) - pluginVerifier() + pluginVerifier("1.371") bundledPlugin("org.toml.lang") jetbrainsRuntimeExplicit("jbr_jcef-17.0.11-linux-x64-b1207.30") } @@ -160,7 +160,7 @@ allprojects { channels.set(listOf(publishingChannel)) } - verifyPlugin { + pluginVerification { ides { recommended() } diff --git a/gradle.properties b/gradle.properties index 4069694ba..522f940f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,6 +18,6 @@ jbrVersion=17.0.7b1000.6 propertiesPluginEnvironmentNameProperty=shortPlatformVersion # properties files # pass ORG_GRADLE_PROJECT_shortPlatformVersion environment variable to overwrite -shortPlatformVersion=242 +shortPlatformVersion=233 useInstaller=true diff --git a/src/main/grammars/MoveParser.bnf b/src/main/grammars/MoveParser.bnf index 919dd60d5..0dd885318 100644 --- a/src/main/grammars/MoveParser.bnf +++ b/src/main/grammars/MoveParser.bnf @@ -159,7 +159,7 @@ // * recovery related rules are PascalCase_with_snake_suffix: Item_recover File ::= (NamedAddressDef | Script | AddressDef | Module | ModuleSpec)* -QualPathCodeFragmentElement ::= FQModulePathIdent? TypeArgumentList? +QualPathCodeFragmentElement ::= PathImpl Attr ::= '#' '[' <> ']' { pin = 1 } AttrItem ::= IDENTIFIER (AttrItemList | AttrItemInitializer)? @@ -185,7 +185,7 @@ Script ::= SCRIPT_KW ScriptBlock { pin = 1 } ScriptBlock ::= '{' ScriptBlockItems '}' { pin = 1 implements = [ - "org.move.lang.core.psi.MvImportsOwner" + "org.move.lang.core.psi.ext.MvItemsOwner" ] } private ScriptBlockItems ::= ScriptItem* @@ -241,7 +241,7 @@ ModuleBlock ::= '{' ModuleBlockItems '}' { pin = 1 implements = [ - "org.move.lang.core.psi.MvImportsOwner" + "org.move.lang.core.psi.ext.MvItemsOwner" ] } private ModuleBlockItems ::= ModuleItem* @@ -255,6 +255,7 @@ private Item_first ::= use | public | native | fun | CONST_KW | STRUCT_KW | spec private Item_recover ::= !('}' | <> | Item_first) private ModuleItem_item ::= UseStmt +// | UseStmt | FriendDecl | StructItem | FunctionItem @@ -269,8 +270,7 @@ Const ::= Attr* CONST_KW IDENTIFIER TypeAnnotation Initializer ';' pin = "CONST_KW" implements = [ "org.move.lang.core.psi.MvQualNamedElement" - "org.move.lang.core.psi.MvNameIdentifierOwner" - "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" + "org.move.lang.core.psi.ext.MvItemElement" "org.move.lang.core.psi.MvTypeAnnotationOwner" ] mixin = "org.move.lang.core.psi.ext.MvConstMixin" @@ -289,6 +289,7 @@ fake SpecFunction ::= Attr* spec native? fun IDENTIFIER? TypeParameterList? implements = [ "org.move.lang.core.psi.MvQualNamedElement" "org.move.lang.core.psi.MvFunctionLike" + "org.move.lang.core.psi.ext.MvItemElement" "org.move.lang.core.types.infer.MvInferenceContextOwner" "org.move.lang.core.psi.ScopeMslOnlyElement" ] @@ -320,6 +321,7 @@ fake SpecInlineFunction ::= Attr* native? fun IDENTIFIER? TypeParameterList? implements = [ "org.move.lang.core.psi.MvFunctionLike" "org.move.lang.core.types.infer.MvInferenceContextOwner" + "org.move.lang.core.psi.ext.MvItemElement" "org.move.lang.core.psi.ScopeMslOnlyElement" ] mixin = "org.move.lang.core.psi.ext.MvSpecInlineFunctionMixin" @@ -355,9 +357,8 @@ fake Struct ::= Attr* native? STRUCT_KW IDENTIFIER? TypeParameterList? Abilities { implements = [ "org.move.lang.core.psi.MvQualNamedElement" - "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" "org.move.lang.core.psi.MvTypeParametersOwner" - "org.move.lang.core.psi.MvNameIdentifierOwner" + "org.move.lang.core.psi.ext.MvItemElement" ] mixin = "org.move.lang.core.psi.ext.MvStructMixin" stubClass = "org.move.lang.core.stubs.MvStructStub" @@ -389,6 +390,7 @@ fake Function ::= Attr* native? VisibilityModifier? entry? inline? implements = [ "org.move.lang.core.psi.MvQualNamedElement" "org.move.lang.core.psi.MvFunctionLike" + "org.move.lang.core.psi.ext.MvItemElement" "org.move.lang.core.types.infer.MvInferenceContextOwner" "org.move.lang.core.psi.MvModificationTrackerOwner" ] @@ -465,17 +467,17 @@ ResourceAccessItemList ::= <> ResourceAccessItem* ResourceAccessItem ::= pure | (('!')? (acquires | reads | writes) AccessSpecifierList) AccessSpecifierList ::= <> -AccessSpecifier ::= <> AddressSpecifier? +AccessSpecifier ::= <> AddressSpecifier? AddressSpecifier ::= '(' AddressSpecifierArg ')' { pin = 1 } AddressSpecifierArg ::= '*' | AddressSpecifierLit - | <> + | <> | IDENTIFIER AddressSpecifierLit ::= DIEM_ADDRESS | INTEGER_LITERAL -AddressSpecifierCallParam ::= AddressSpecifierParamPath -AddressSpecifierParamPath ::= LocalPathIdent { elementType = Path } +AddressSpecifierCallParam ::= AddressSpecifierCallParamImpl +AddressSpecifierCallParamImpl ::= PathIdent { elementType = Path } // acquires T, Record AcquiresType ::= acquires AcquiresType_items { pin = 1 } @@ -511,53 +513,32 @@ StructField ::= Attr* IDENTIFIER TypeAnnotation &(',' | '}') hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ] } -FriendDecl ::= Attr* friend FQModuleRef ';' +FriendDecl ::= Attr* friend PathImpl ';' { pin = "friend" implements = [ "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" ] } -UseStmt ::= Attr* use (ItemUseSpeck | ModuleUseSpeck) ';' { +UseStmt ::= Attr* use UseSpeck ';' { pin = "use" + extends = Stmt implements = [ "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" ] } -ModuleUseSpeck ::= (AddressRef !'::') | FQModuleRef UseAlias? -{ - name = "qual path to module" - implements = [ - "org.move.lang.core.psi.MvNamedElement" -// "org.move.lang.core.psi.MvUseSpeck" - ] - mixin = "org.move.lang.core.psi.ext.MvModuleUseSpeckMixin" -} -ItemUseSpeck ::= FQModuleRef '::' (UseItem | UseItemGroup) -{ - pin = 2 - name = "qual path to imported item" -// implements = [ -// "org.move.lang.core.psi.MvUseSpeck" -// ] -} +UseSpeck ::= PathImpl ( UseAlias | ('::' UseGroup) )? -UseItemGroup ::= '{' <>? '}' -{ - pin = 1 -} -private MultiItemUse_member_with_recovery ::= !('}' | ';' | <>) UseItem +UseGroup ::= '{' UseSpeck_with_recover* '}' { pin = 1 } +// todo: +private PathWithoutTypeArgs ::= PathImpl -UseItem ::= IDENTIFIER UseAlias? -{ - pin = 1 - name = "item name" - implements = [ - "org.move.lang.core.psi.MvNamedElement" - "org.move.lang.core.resolve.ref.MvMandatoryReferenceElement" - ] - mixin = "org.move.lang.core.psi.ext.MvUseItemMixin" +private UseSpeck_with_recover ::= !'}' UseSpeck (','|&'}') { + pin = 1 + recoverWhile = UseSpeck_recover } +private UseSpeck_recover ::= !('}' | '{' | IDENTIFIER | '::' ) + UseAlias ::= as IDENTIFIER { pin = 1 @@ -583,7 +564,7 @@ RefType ::= RefTypeStart Type } RefTypeStart ::= '&' mut? -PathType ::= Path +PathType ::= PathImpl { name = "type" } @@ -657,7 +638,7 @@ BindingPat ::= IDENTIFIER { TuplePat ::= '(' <>? ')' -StructPat ::= Path StructPatFieldsBlock +StructPat ::= PathImpl StructPatFieldsBlock StructPatFieldsBlock ::= '{' StructPatField_with_recover* '}' { pin = 1 } private StructPatField_with_recover ::= !'}' StructPatField (',' | &'}') @@ -672,7 +653,7 @@ StructPatField ::= (BindingPat !':') | (IDENTIFIER StructPatFieldBinding) { implements = [ "org.move.lang.core.resolve.ref.MvStructPatFieldReferenceElement" - "org.move.lang.core.resolve.ref.MvStructRefField" + "org.move.lang.core.psi.ext.MvStructRefField" ] mixin = "org.move.lang.core.psi.ext.MvStructPatFieldMixin" } @@ -745,7 +726,7 @@ CodeBlock ::= '{' CodeBlock_items '}' { pin = 1 implements = [ - "org.move.lang.core.psi.MvImportsOwner" + "org.move.lang.core.psi.ext.MvItemsOwner" "org.move.lang.core.psi.AnyBlock" ] mixin = "org.move.lang.core.psi.ext.MvCodeBlockMixin" @@ -903,7 +884,7 @@ VectorLitItems ::= '[' <>? ']' pin = 1 } -StructLitExpr ::= <> Path StructLitFieldsBlock +StructLitExpr ::= <> PathImpl StructLitFieldsBlock { implements = ["org.move.lang.core.psi.PathExpr"] } @@ -920,7 +901,7 @@ StructLitField ::= IDENTIFIER StructLitFieldInit? { implements = [ "org.move.lang.core.resolve.ref.MvStructFieldLitReferenceElement" - "org.move.lang.core.resolve.ref.MvStructRefField" + "org.move.lang.core.psi.ext.MvStructRefField" ] mixin = "org.move.lang.core.psi.ext.MvStructLitFieldMixin" } @@ -965,7 +946,7 @@ private AnyLitToken_first ::= HEX_INTEGER_LITERAL AddressLit ::= '@' AddressRef { pin = 1 } -CallExpr ::= (Path &'(') ValueArgumentList { +CallExpr ::= (PathImpl &'(') ValueArgumentList { pin = 1 implements = [ "org.move.lang.core.psi.PathExpr" @@ -1045,47 +1026,61 @@ IndexExpr ::= Expr IndexArg // Do not inline this rule, it breaks expression parsing private IndexArg ::= '[' Expr ']' -RefExpr ::= Path !'{' { +RefExpr ::= PathImpl !'{' { +//RefExpr ::= Path !'{' { implements = ["org.move.lang.core.psi.PathExpr"] } -Path ::= (ModulePathIdent | FQModulePathIdent | LocalPathIdent) TypeArgumentList? +//Path3Impl ::= (ModulePathIdent | FQModulePathIdent | LocalPathIdent) TypeArgumentList? +//{ +// implements = [ +// "org.move.lang.core.resolve.ref.MvPathReferenceElement" +// "org.move.lang.core.psi.ext.MvMethodOrPath" +// ] +// mixin = "org.move.lang.core.psi.ext.MvPathMixin" +//} + +fake Path ::= (Path '::')? (PathIdent | PathAddress) TypeArgumentList? { implements = [ - "org.move.lang.core.resolve.ref.MvPathReferenceElement" + "org.move.lang.core.resolve.ref.MvNameAccessChainReferenceElement" "org.move.lang.core.psi.ext.MvMethodOrPath" ] mixin = "org.move.lang.core.psi.ext.MvPathMixin" } -private LocalPathIdent ::= PATH_MODE_IDENTIFIER -private ModulePathIdent ::= ModuleRef ('::' !(PATH_MODE_IDENTIFIER '::')) PATH_MODE_IDENTIFIER -{ - pin = 2 -} -private FQModulePathIdent ::= FQModuleRef '::' PATH_MODE_IDENTIFIER -{ - pin = 1 -} -private PATH_MODE_IDENTIFIER ::= (<> IDENTIFIER) - | (<> ('*' | IDENTIFIER)) +private PathImpl ::= PathStart PathSegment* -ModuleRef ::= PATH_MODE_IDENTIFIER | FQModuleRef +PathStart ::= ((PathAddress &'::') | PathIdent) TypeArgumentList? { - implements = ["org.move.lang.core.resolve.ref.MvReferenceElement"] - mixin = "org.move.lang.core.psi.ext.MvModuleRefMixin" + name = "path" + elementType = Path } - -FQModuleRef ::= AddressRef '::' PATH_MODE_IDENTIFIER -{ +// negation for UseSpeck +left PathSegment ::= '::' !'{' PathIdent TypeArgumentList? { pin = 2 - extends = ModuleRef - implements = [ - "org.move.lang.core.resolve.ref.MvFQModuleReferenceElement" - ] - mixin = "org.move.lang.core.psi.ext.MvFQModuleRefMixin" + elementType = Path } +private PathIdent ::= PATH_MODE_IDENTIFIER +PathAddress ::= DIEM_ADDRESS + | INTEGER_LITERAL + | PLACEHOLDER_ADDRESS + | BECH32_ADDRESS + | POLKADOT_ADDRESS + +//private LocalPathIdent ::= PATH_MODE_IDENTIFIER +//private ModulePathIdent ::= ModuleRef ('::' !(PATH_MODE_IDENTIFIER '::')) PATH_MODE_IDENTIFIER +//{ +// pin = 2 +//} +//private FQModulePathIdent ::= FQModuleRef '::' PATH_MODE_IDENTIFIER +//{ +// pin = 1 +//} +private PATH_MODE_IDENTIFIER ::= (<> IDENTIFIER) + | (<> ('*' | IDENTIFIER)) + AddressRef ::= NamedAddress | DIEM_ADDRESS | INTEGER_LITERAL @@ -1135,7 +1130,8 @@ private to ::= <> private apply ::= <> private except ::= <> -ModuleSpec ::= Attr* spec FQModuleRef ModuleSpecBlock +ModuleSpec ::= Attr* spec PathImpl ModuleSpecBlock +//ModuleSpec ::= Attr* spec FQModuleRef ModuleSpecBlock { pin = "spec" name = "module spec declaration" @@ -1148,7 +1144,7 @@ ModuleSpecBlock ::= '{' ModuleSpecBlock_item_with_recover* '}' { pin = 1 implements = [ - "org.move.lang.core.psi.MvImportsOwner" + "org.move.lang.core.psi.ext.MvItemsOwner" "org.move.lang.core.psi.ScopeMslOnlyElement" "org.move.lang.core.psi.AnyBlock" ] @@ -1228,18 +1224,19 @@ ItemSpecRef ::= IDENTIFIER mixin = "org.move.lang.core.psi.ext.MvItemSpecRefMixin" } -Schema ::= (spec schema) IDENTIFIER TypeParameterList? <> { - pin = 1 +Schema ::= Attr* (spec schema) IDENTIFIER TypeParameterList? <> { + pin = 2 implements = [ "org.move.lang.core.psi.MvTypeParametersOwner" "org.move.lang.core.psi.MvQualNamedElement" - "org.move.lang.core.psi.MvNameIdentifierOwner" "org.move.lang.core.types.infer.MvInferenceContextOwner" "org.move.lang.core.psi.ScopeMslOnlyElement" + "org.move.lang.core.psi.ext.MvItemElement" ] mixin = "org.move.lang.core.psi.ext.MvSchemaMixin" stubClass = "org.move.lang.core.stubs.MvSchemaStub" elementTypeFactory = "org.move.lang.core.stubs.StubsKt.factory" + hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ] } ItemSpecBlockExpr ::= spec <> { @@ -1252,7 +1249,7 @@ SpecCodeBlock ::= '{' ItemSpecBlock_items '}' { pin = 1 implements = [ - "org.move.lang.core.psi.MvImportsOwner" + "org.move.lang.core.psi.ext.MvItemsOwner" "org.move.lang.core.psi.AnyBlock" "org.move.lang.core.psi.MslOnlyElement" ] @@ -1294,13 +1291,14 @@ SchemaFieldStmt_local ::= local BindingPat TypeAnnotation ';' { elementType = SchemaFieldStmt } -GlobalVariableStmt ::= global IDENTIFIER TypeParameterList? TypeAnnotation ('=' Expr)? ';' +GlobalVariableStmt ::= Attr* global IDENTIFIER TypeParameterList? TypeAnnotation ('=' Expr)? ';' { - pin = 1 + pin = 2 implements = [ "org.move.lang.core.psi.MvNameIdentifierOwner" "org.move.lang.core.psi.MvTypeAnnotationOwner" "org.move.lang.core.psi.MslOnlyElement" + "org.move.lang.core.psi.ext.MvItemElement" ] mixin = "org.move.lang.core.psi.ext.MvGlobalVariableMixin" } @@ -1411,7 +1409,7 @@ IfElseIncludeItem ::= if Condition SchemaLit else SchemaLit extends = IncludeItem } -SchemaLit ::= Path SchemaFieldsBlock? +SchemaLit ::= PathImpl SchemaFieldsBlock? { implements = [ "org.move.lang.core.psi.MslOnlyElement" @@ -1499,7 +1497,7 @@ ApplyExcept ::= except <> { implements = [ "org.move.lang.core.psi.MslOnlyElement" ] } -SchemaRef ::= Path ('{' <> '}')? +SchemaRef ::= PathImpl ('{' <> '}')? { implements = [ "org.move.lang.core.psi.MslOnlyElement" ] } diff --git a/src/main/kotlin/org/move/cli/MoveProject.kt b/src/main/kotlin/org/move/cli/MoveProject.kt index 6fcd74a8b..61fafdc11 100644 --- a/src/main/kotlin/org/move/cli/MoveProject.kt +++ b/src/main/kotlin/org/move/cli/MoveProject.kt @@ -1,5 +1,7 @@ package org.move.cli +import com.intellij.openapi.components.service +import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.NlsContexts.Tooltip import com.intellij.openapi.util.UserDataHolderBase @@ -12,6 +14,7 @@ import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker import org.move.cli.manifest.AptosConfigYaml import org.move.cli.manifest.MoveToml +import org.move.cli.tests.NamedAddressService import org.move.lang.MoveFile import org.move.lang.core.psi.MvModule import org.move.lang.core.types.Address @@ -20,6 +23,7 @@ import org.move.lang.index.MvNamedElementIndex import org.move.lang.toMoveFile import org.move.lang.toNioPathOrNull import org.move.openapiext.common.checkUnitTestMode +import org.move.openapiext.common.isUnitTestMode import org.move.openapiext.contentRoots import org.move.stdext.chain import org.move.stdext.iterateMoveVirtualFiles @@ -42,7 +46,7 @@ data class MoveProject( fun movePackages(): Sequence = currentPackage.wrapWithList().chain(depPackages()) fun depPackages(): List = dependencies.map { it.first }.reversed() - fun sourceFolders(): List { + fun allAccessibleMoveFolders(): List { val folders = currentPackage.moveFolders().toMutableList() val depFolders = dependencies.asReversed().flatMap { it.first.moveFolders() } @@ -88,6 +92,16 @@ data class MoveProject( return Address.Named(name, value, this) } + fun getNamedAddressTestAware(name: String): Address.Named? { + val namedAddress = getNamedAddress(name) + if (namedAddress != null) return namedAddress + if (isUnitTestMode) { + val namedAddressService = project.service() + return namedAddressService.getNamedAddress(this, name) + } + return null + } + fun getAddressNamesForValue(addressValue: String): List { val addressLit = AddressLit(addressValue) val names = mutableListOf() @@ -102,10 +116,20 @@ data class MoveProject( fun searchScope(): GlobalSearchScope { var searchScope = GlobalSearchScope.EMPTY_SCOPE - for (folder in sourceFolders()) { + for (folder in allAccessibleMoveFolders()) { val dirScope = GlobalSearchScopes.directoryScope(project, folder, true) searchScope = searchScope.uniteWith(dirScope) } + if (isUnitTestMode + && searchScope == GlobalSearchScope.EMPTY_SCOPE + ) { + // add current file to the search scope for the tests + val currentFile = + FileEditorManager.getInstance(project).selectedTextEditor?.virtualFile + if (currentFile != null) { + searchScope = searchScope.uniteWith(GlobalSearchScope.fileScope(project, currentFile)) + } + } return searchScope } @@ -120,7 +144,7 @@ data class MoveProject( val profiles: Set = this.aptosConfigYaml?.profiles.orEmpty() fun processMoveFiles(processFile: (MoveFile) -> Boolean) { - val folders = sourceFolders() + val folders = allAccessibleMoveFolders() var stopped = false for (folder in folders) { if (stopped) break @@ -134,11 +158,12 @@ data class MoveProject( } sealed class UpdateStatus(private val priority: Int) { -// object UpToDate : UpdateStatus(0) - object NeedsUpdate : UpdateStatus(1) - class UpdateFailed(@Tooltip val reason: String) : UpdateStatus(2) { + // object UpToDate : UpdateStatus(0) + object NeedsUpdate: UpdateStatus(1) + class UpdateFailed(@Tooltip val reason: String): UpdateStatus(2) { override fun toString(): String = reason } + fun merge(status: UpdateStatus): UpdateStatus = if (priority >= status.priority) this else status } diff --git a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt index 5f3e18bf2..5a6da2c89 100644 --- a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt +++ b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt @@ -127,6 +127,12 @@ fun Path?.isValidExecutable(): Boolean { fun isDebugModeEnabled(): Boolean = Registry.`is`("org.move.debug.enabled") +fun debugError(message: String) { + if (isDebugModeEnabled()) { + error(message) + } +} + fun debugErrorOrFallback(message: String, fallback: T): T { if (isDebugModeEnabled()) { error(message) diff --git a/src/main/kotlin/org/move/cli/tests/NamedAddressService.kt b/src/main/kotlin/org/move/cli/tests/NamedAddressService.kt new file mode 100644 index 000000000..8725dcf25 --- /dev/null +++ b/src/main/kotlin/org/move/cli/tests/NamedAddressService.kt @@ -0,0 +1,23 @@ +package org.move.cli.tests + +import org.move.cli.MoveProject +import org.move.lang.core.types.Address +import org.move.lang.core.types.Address.Named + +interface NamedAddressService { + fun getNamedAddress(moveProject: MoveProject, name: String): Address.Named? +} + +class NamedAddressServiceImpl: NamedAddressService { + override fun getNamedAddress(moveProject: MoveProject, name: String): Address.Named? = null +} + + +class NamedAddressServiceTestImpl: NamedAddressService { + val namedAddresses: MutableMap = mutableMapOf() + + override fun getNamedAddress(moveProject: MoveProject, name: String): Named? { + val value = namedAddresses[name] ?: return null + return Named(name, value, moveProject) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/ide/annotator/HighlightingAnnotator.kt b/src/main/kotlin/org/move/ide/annotator/HighlightingAnnotator.kt index 4a7e757f8..decacf0d5 100644 --- a/src/main/kotlin/org/move/ide/annotator/HighlightingAnnotator.kt +++ b/src/main/kotlin/org/move/ide/annotator/HighlightingAnnotator.kt @@ -62,8 +62,6 @@ class HighlightingAnnotator: MvAnnotatorBase() { if (element is MvAbility) return MvColor.ABILITY if (element is MvTypeParameter) return MvColor.TYPE_PARAMETER if (element is MvItemSpecTypeParameter) return MvColor.TYPE_PARAMETER - if (element is MvModuleRef && element.isSelfModuleRef) return MvColor.KEYWORD - if (element is MvUseItem && element.text == "Self") return MvColor.KEYWORD if (element is MvFunction) return when { element.isInline -> MvColor.INLINE_FUNCTION @@ -106,10 +104,12 @@ class HighlightingAnnotator: MvAnnotatorBase() { } private fun highlightPathElement(path: MvPath): MvColor? { + val identifierName = path.identifierName + if (identifierName == "Self") return MvColor.KEYWORD + // any qual :: access is not highlighted - if (path.isQualPath) return null + if (path.qualifier != null) return null - val identifierName = path.identifierName val pathOwner = path.parent return when (pathOwner) { is MvPathType -> { @@ -128,7 +128,7 @@ class HighlightingAnnotator: MvAnnotatorBase() { } } is MvCallExpr -> { - val item = path.reference?.resolveWithAliases() + val item = path.reference?.resolveFollowingAliases() when { item is MvSpecFunction && item.isNative @@ -145,7 +145,7 @@ class HighlightingAnnotator: MvAnnotatorBase() { is MvStructLitExpr -> MvColor.STRUCT is MvStructPat -> MvColor.STRUCT is MvRefExpr -> { - val item = path.reference?.resolve() ?: return null + val item = path.reference?.resolveFollowingAliases() ?: return null when { item is MvConst -> MvColor.CONSTANT else -> { diff --git a/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt b/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt index 769940a48..6eaedfbee 100644 --- a/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt +++ b/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt @@ -193,13 +193,13 @@ class MvErrorAnnotator: MvAnnotatorBase() { } private fun checkMethodOrPath(methodOrPath: MvMethodOrPath, holder: MvAnnotationHolder) { - val item = methodOrPath.reference?.resolveWithAliases() + val item = methodOrPath.reference?.resolveFollowingAliases() val msl = methodOrPath.isMslScope val realCount = methodOrPath.typeArguments.size val parent = methodOrPath.parent if (item == null && methodOrPath is MvPath - && methodOrPath.nullModuleRef && methodOrPath.identifierName == "vector" + && methodOrPath.qualifier == null && methodOrPath.identifierName == "vector" ) { val expectedCount = 1 if (realCount != expectedCount) { @@ -302,7 +302,7 @@ class MvErrorAnnotator: MvAnnotatorBase() { val itemLabel = qualName.editorText() val realCount = typeArgumentList.typeArgumentList.size - assert(realCount != 0) { "Should be non-zero if typeArgumentList exists" } + check(realCount != 0) { "Should be non-zero if typeArgumentList exists" } // if any type param is passed, inference is disabled, so check fully when { diff --git a/src/main/kotlin/org/move/ide/formatter/MoveFmtBlock.kt b/src/main/kotlin/org/move/ide/formatter/MoveFmtBlock.kt index 928bf241a..5754f80dc 100644 --- a/src/main/kotlin/org/move/ide/formatter/MoveFmtBlock.kt +++ b/src/main/kotlin/org/move/ide/formatter/MoveFmtBlock.kt @@ -56,7 +56,7 @@ class MoveFmtBlock( parentType == FUNCTION_PARAMETER_LIST -> chopListWrap parentType == VALUE_ARGUMENT_LIST -> chopListWrap parentType == ATTR_ITEM_LIST -> chopListWrap - parentType == USE_ITEM_GROUP -> chopListWrap + parentType == USE_GROUP -> chopListWrap else -> null } diff --git a/src/main/kotlin/org/move/ide/formatter/impl/utils.kt b/src/main/kotlin/org/move/ide/formatter/impl/utils.kt index 6f62e2283..f31ecc6a2 100644 --- a/src/main/kotlin/org/move/ide/formatter/impl/utils.kt +++ b/src/main/kotlin/org/move/ide/formatter/impl/utils.kt @@ -38,7 +38,7 @@ val BLOCK_LIKE = orSet(STRUCT_LITERAL_BLOCKS, DEF_BLOCKS) val DELIMITED_BLOCKS = orSet( PAREN_DELIMITED_BLOCKS, ANGLE_DELIMITED_BLOCKS, BRACKET_DELIMITED_BLOCKS, BLOCK_LIKE, - ts(USE_ITEM_GROUP) + ts(USE_GROUP) ) fun ASTNode?.isWhitespaceOrEmpty() = this == null || textLength == 0 || elementType == TokenType.WHITE_SPACE @@ -72,7 +72,7 @@ fun ASTNode.isDelimiterOfCurrentBlock(parent: ASTNode?): Boolean { if (parent == null) return false val parentType = parent.elementType return when (elementType) { - L_BRACE, R_BRACE -> parentType in BLOCK_LIKE || parentType == USE_ITEM_GROUP + L_BRACE, R_BRACE -> parentType in BLOCK_LIKE || parentType == USE_GROUP L_BRACK, R_BRACK -> parentType in BRACKET_DELIMITED_BLOCKS L_PAREN, R_PAREN -> parentType in PAREN_DELIMITED_BLOCKS LT, GT -> parentType in ANGLE_DELIMITED_BLOCKS diff --git a/src/main/kotlin/org/move/ide/hints/TypeParameterInfoHandler.kt b/src/main/kotlin/org/move/ide/hints/TypeParameterInfoHandler.kt index ef4a0eb34..d5b42e2a6 100644 --- a/src/main/kotlin/org/move/ide/hints/TypeParameterInfoHandler.kt +++ b/src/main/kotlin/org/move/ide/hints/TypeParameterInfoHandler.kt @@ -19,7 +19,7 @@ class TypeParameterInfoHandler : override fun calculateParameterInfo(element: MvTypeArgumentList): Array? { val parentPath = element.parent as? MvPath ?: return null - val owner = parentPath.reference?.resolveWithAliases() ?: return null + val owner = parentPath.reference?.resolveFollowingAliases() ?: return null // if zero type parameters if (owner !is MvTypeParametersOwner) return null diff --git a/src/main/kotlin/org/move/ide/inspections/AddressByValueImportInspection.kt b/src/main/kotlin/org/move/ide/inspections/AddressByValueImportInspection.kt deleted file mode 100644 index 33bf76429..000000000 --- a/src/main/kotlin/org/move/ide/inspections/AddressByValueImportInspection.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.move.ide.inspections - -import com.intellij.codeInspection.ProblemHighlightType -import com.intellij.codeInspection.ProblemsHolder -import org.move.cli.Consts -import org.move.ide.inspections.fixes.ChangeAddressNameFix -import org.move.lang.core.psi.MvModule -import org.move.lang.core.psi.MvModuleUseSpeck -import org.move.lang.core.psi.MvVisitor -import org.move.lang.core.types.Address -import org.move.lang.core.types.address -import org.move.lang.moveProject - -class AddressByValueImportInspection : MvLocalInspectionTool() { - override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor = - object : MvVisitor() { - override fun visitModuleUseSpeck(o: MvModuleUseSpeck) { - val moduleRef = o.fqModuleRef ?: return - // no error if unresolved by value (default) - val module = moduleRef.reference?.resolve() as? MvModule ?: return - - val moveProj = moduleRef.moveProject ?: return - - val refAddress = moduleRef.addressRef.address(moveProj) ?: return - if (refAddress !is Address.Named) return - if (refAddress.addressLit(moveProj)?.canonical() == Consts.ADDR_PLACEHOLDER) return - - val modAddress = module.address(moveProj) ?: return - if (modAddress !is Address.Named) return - if (modAddress.addressLit(moveProj)?.canonical() == Consts.ADDR_PLACEHOLDER) return - - if (!Address.eq(refAddress, modAddress)) { - holder.registerProblem( - moduleRef, - "Module is declared with a different address `${modAddress.name}`", - ProblemHighlightType.WEAK_WARNING, - ChangeAddressNameFix(moduleRef, modAddress.name), - ) - } - } - } -} diff --git a/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt b/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt index c7931c6d2..fe33911db 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt @@ -2,9 +2,6 @@ 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 diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt index 358fc92cf..13e576680 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt @@ -2,38 +2,36 @@ package org.move.ide.inspections import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder -import com.intellij.psi.util.descendantsOfType import org.move.cli.settings.isDebugModeEnabled import org.move.ide.inspections.imports.AutoImportFix import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* -import org.move.lang.core.resolve.ref.MvReferenceElement import org.move.lang.core.types.infer.inference import org.move.lang.core.types.ty.TyUnknown -class MvUnresolvedReferenceInspection : MvLocalInspectionTool() { +class MvUnresolvedReferenceInspection: MvLocalInspectionTool() { var ignoreWithoutQuickFix: Boolean = false override val isSyntaxOnly get() = false - private fun ProblemsHolder.registerUnresolvedReferenceError(element: MvReferenceElement) { + private fun ProblemsHolder.registerUnresolvedReferenceError(path: MvPath) { // no errors in pragmas - if (element.hasAncestor()) return + if (path.hasAncestor()) return - val candidates = AutoImportFix.findApplicableContext(element)?.candidates.orEmpty() + val candidates = AutoImportFix.findApplicableContext(path)?.candidates.orEmpty() if (candidates.isEmpty() && ignoreWithoutQuickFix) return - val referenceName = element.referenceName ?: return - val parent = element.parent + val referenceName = path.referenceName ?: return + val parent = path.parent val description = when (parent) { is MvPathType -> "Unresolved type: `$referenceName`" is MvCallExpr -> "Unresolved function: `$referenceName`" else -> "Unresolved reference: `$referenceName`" } - val highlightedElement = element.referenceNameElement ?: element - val fix = if (candidates.isNotEmpty()) AutoImportFix(element) else null + val highlightedElement = path.referenceNameElement ?: path + val fix = if (candidates.isNotEmpty()) AutoImportFix(path) else null registerProblem( highlightedElement, description, @@ -42,22 +40,22 @@ class MvUnresolvedReferenceInspection : MvLocalInspectionTool() { ) } - override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : MvVisitor() { - override fun visitModuleRef(moduleRef: MvModuleRef) { - if (moduleRef.isMslScope && !isDebugModeEnabled()) { - return - } - // skip this check, as it will be checked in MvPath visitor - if (moduleRef.ancestorStrict() != null) return - - // skip those two, checked in UseSpeck checks later - if (moduleRef.ancestorStrict() != null) return - if (moduleRef is MvFQModuleRef) return - - if (moduleRef.unresolved) { - holder.registerUnresolvedReferenceError(moduleRef) - } - } + override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object: MvVisitor() { +// override fun visitModuleRef(moduleRef: MvModuleRef) { +// if (moduleRef.isMslScope && !isDebugModeEnabled()) { +// return +// } +// // skip this check, as it will be checked in MvPath visitor +// if (moduleRef.ancestorStrict() != null) return +// +// // skip those two, checked in UseSpeck checks later +// if (moduleRef.ancestorStrict() != null) return +// if (moduleRef is MvFQModuleRef) return +// +// if (moduleRef.unresolved) { +// holder.registerUnresolvedReferenceError(moduleRef) +// } +// } override fun visitPath(path: MvPath) { // skip specs in non-dev mode, too many false-positives @@ -73,17 +71,30 @@ class MvUnresolvedReferenceInspection : MvLocalInspectionTool() { if (path.textMatches("_") && path.isInsideAssignmentLhs()) return // assert macro if (path.text == "assert") return + // attribute values are special case if (path.hasAncestor()) return - val moduleRef = path.moduleRef - if (moduleRef != null) { - if (moduleRef is MvFQModuleRef) return - if (moduleRef.unresolved) { - holder.registerUnresolvedReferenceError(moduleRef) + val qualifier = path.qualifier + if (qualifier != null + // AddressPath, should be checked here + && qualifier.pathAddress == null + ) { + if (qualifier.reference?.resolve() == null) { return } } +// val qualifier = path.qualifier +// if (qualifier) + +// val moduleRef = path.moduleRef +// if (moduleRef != null) { +// if (moduleRef is MvFQModuleRef) return +// if (moduleRef.unresolved) { +// holder.registerUnresolvedReferenceError(moduleRef) +// return +// } +// } if (path.unresolved) { holder.registerUnresolvedReferenceError(path) } @@ -184,40 +195,40 @@ class MvUnresolvedReferenceInspection : MvLocalInspectionTool() { } } - override fun visitModuleUseSpeck(o: MvModuleUseSpeck) { - val moduleRef = o.fqModuleRef ?: return - if (!moduleRef.resolvable) { - val refNameElement = moduleRef.referenceNameElement ?: return - holder.registerProblem( - refNameElement, - "Unresolved reference: `${refNameElement.text}`", - ProblemHighlightType.LIKE_UNKNOWN_SYMBOL - ) - } - } - - override fun visitItemUseSpeck(o: MvItemUseSpeck) { - val moduleRef = o.fqModuleRef - if (!moduleRef.resolvable) { - val refNameElement = moduleRef.referenceNameElement ?: return - holder.registerProblem( - refNameElement, - "Unresolved reference: `${refNameElement.text}`", - ProblemHighlightType.LIKE_UNKNOWN_SYMBOL - ) - return - } - val useItems = o.descendantsOfType() - for (useItem in useItems) { - if (!useItem.resolvable) { - val refNameElement = useItem.referenceNameElement - holder.registerProblem( - refNameElement, - "Unresolved reference: `${refNameElement.text}`", - ProblemHighlightType.LIKE_UNKNOWN_SYMBOL - ) - } - } - } +// override fun visitModuleUseSpeck(o: MvModuleUseSpeck) { +// val moduleRef = o.fqModuleRef ?: return +// if (!moduleRef.resolvable) { +// val refNameElement = moduleRef.referenceNameElement ?: return +// holder.registerProblem( +// refNameElement, +// "Unresolved reference: `${refNameElement.text}`", +// ProblemHighlightType.LIKE_UNKNOWN_SYMBOL +// ) +// } +// } + +// override fun visitItemUseSpeck(o: MvItemUseSpeck) { +// val moduleRef = o.fqModuleRef +// if (!moduleRef.resolvable) { +// val refNameElement = moduleRef.referenceNameElement ?: return +// holder.registerProblem( +// refNameElement, +// "Unresolved reference: `${refNameElement.text}`", +// ProblemHighlightType.LIKE_UNKNOWN_SYMBOL +// ) +// return +// } +// val useItems = o.descendantsOfType() +// for (useItem in useItems) { +// if (!useItem.resolvable) { +// val refNameElement = useItem.referenceNameElement +// holder.registerProblem( +// refNameElement, +// "Unresolved reference: `${refNameElement.text}`", +// ProblemHighlightType.LIKE_UNKNOWN_SYMBOL +// ) +// } +// } +// } } } diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnusedImportInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnusedImportInspection.kt index 8248ca887..fa8d1da7f 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnusedImportInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnusedImportInspection.kt @@ -4,11 +4,12 @@ import com.intellij.codeInsight.daemon.HighlightDisplayKey import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.project.Project import com.intellij.profile.codeInspection.InspectionProjectProfileManager -import org.move.ide.inspections.imports.ImportAnalyzer +import org.move.ide.inspections.imports.ImportAnalyzer2 -class MvUnusedImportInspection : MvLocalInspectionTool() { +class MvUnusedImportInspection: MvLocalInspectionTool() { - override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = ImportAnalyzer(holder) + override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = ImportAnalyzer2(holder) +// override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = ImportAnalyzer(holder) @Suppress("CompanionObjectInExtension") companion object { diff --git a/src/main/kotlin/org/move/ide/inspections/PhantomTypeParameterInspection.kt b/src/main/kotlin/org/move/ide/inspections/PhantomTypeParameterInspection.kt index 67fb91632..6fa282567 100644 --- a/src/main/kotlin/org/move/ide/inspections/PhantomTypeParameterInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/PhantomTypeParameterInspection.kt @@ -31,7 +31,7 @@ class PhantomTypeParameterInspection : MvLocalInspectionTool() { // stop if empty if (path.typeArguments.isEmpty()) continue // determine phantom status of every argument, drop if phantom - val outerStruct = path.reference?.resolveWithAliases() as? MvStruct ?: continue + val outerStruct = path.reference?.resolveFollowingAliases() as? MvStruct ?: continue for ((i, typeArg) in path.typeArguments.withIndex()) { val outerTypeParam = outerStruct.typeParameters.getOrNull(i) ?: continue if (outerTypeParam.isPhantom) { diff --git a/src/main/kotlin/org/move/ide/inspections/RedundantQualifiedPathInspection.kt b/src/main/kotlin/org/move/ide/inspections/RedundantQualifiedPathInspection.kt deleted file mode 100644 index d923a8e1c..000000000 --- a/src/main/kotlin/org/move/ide/inspections/RedundantQualifiedPathInspection.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.move.ide.inspections - -import com.intellij.codeInspection.LocalQuickFix -import com.intellij.codeInspection.ProblemDescriptor -import com.intellij.codeInspection.ProblemHighlightType -import com.intellij.codeInspection.ProblemsHolder -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.TextRange -import org.move.lang.core.psi.* - -class RedundantQualifiedPathInspection : MvLocalInspectionTool() { - - override val isSyntaxOnly: Boolean get() = true - - override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor = - object : MvVisitor() { - override fun visitPath(path: MvPath) { - val pathText = path.text - .replace(path.typeArgumentList?.text.orEmpty(), "") - .replace(Regex("\\s"), "") - val item = path.reference?.resolveWithAliases() ?: return - - val importsOwner = path.containingScript?.scriptBlock - ?: path.containingModule?.moduleBlock - ?: return - val shortestPathText = importsOwner.shortestPathText(item) ?: return - // if aliases are involved, could lead to bugs - if (!pathText.endsWith(shortestPathText)) return - - val diff = pathText.length - shortestPathText.length - if (diff > 0) { - if (pathText.substring(0, diff) == "Self::") return - val range = TextRange.from(0, diff) - holder.registerProblem( - path, - "Redundant qualifier", - ProblemHighlightType.LIKE_UNUSED_SYMBOL, - range, - object : LocalQuickFix { - override fun getFamilyName(): String = "Remove redundant qualifier" - - override fun applyFix( - project: Project, - descriptor: ProblemDescriptor - ) { - val newPath = project.psiFactory.path(shortestPathText) - descriptor.psiElement.replace(newPath) - } - - }) - } - } - } -} diff --git a/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt b/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt index 02a500a5f..714c96e7d 100644 --- a/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt @@ -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?.resolveWithAliases() as? MvFunction ?: return + val function = callExpr.path.reference?.resolveFollowingAliases() as? MvFunction ?: return val msl = callExpr.isMsl() val inference = callExpr.inference(msl) ?: return diff --git a/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt b/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt index 20127d940..887f66f83 100644 --- a/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt @@ -16,7 +16,7 @@ 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 function = callExpr.path.reference?.resolveFollowingAliases() as? MvFunction ?: return val module = function.module ?: return val moveProject = function.moveProject ?: return val msl = callExpr.isMsl() diff --git a/src/main/kotlin/org/move/ide/inspections/fixes/ChangeAddressNameFix.kt b/src/main/kotlin/org/move/ide/inspections/fixes/ChangeAddressNameFix.kt deleted file mode 100644 index 3a9f70732..000000000 --- a/src/main/kotlin/org/move/ide/inspections/fixes/ChangeAddressNameFix.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.move.ide.inspections.fixes - -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiFile -import org.move.ide.inspections.DiagnosticFix -import org.move.lang.core.psi.MvFQModuleRef -import org.move.lang.core.psi.MvModule -import org.move.lang.core.psi.psiFactory -import org.move.lang.core.types.address -import org.move.lang.moveProject - -class ChangeAddressNameFix( - moduleRef: MvFQModuleRef, - val newAddressRefName: String -) : DiagnosticFix(moduleRef) { - - override fun getText(): String = "Change address to `$newAddressRefName`" - override fun getFamilyName(): String = "Change address ref" - - override fun stillApplicable(project: Project, file: PsiFile, element: MvFQModuleRef): Boolean { - return element.addressRef.namedAddress?.referenceName != newAddressRefName - } - - override fun invoke(project: Project, file: PsiFile, element: MvFQModuleRef) { - val ref = element - - // resolve by value - val mod = ref.reference?.resolve() as? MvModule ?: return - val proj = mod.moveProject ?: return - - val modAddressRef = mod.addressRef ?: return - if (ref.addressRef.address(proj) != mod.address(proj)) { - val newAddressRef = project.psiFactory.addressRef(modAddressRef.text) - ref.addressRef.replace(newAddressRef) - } - } -} diff --git a/src/main/kotlin/org/move/ide/inspections/imports/AutoImportFix.kt b/src/main/kotlin/org/move/ide/inspections/imports/AutoImportFix.kt index 0ef032ced..3fbbda648 100644 --- a/src/main/kotlin/org/move/ide/inspections/imports/AutoImportFix.kt +++ b/src/main/kotlin/org/move/ide/inspections/imports/AutoImportFix.kt @@ -9,20 +9,19 @@ import org.move.ide.inspections.DiagnosticFix import org.move.ide.utils.imports.ImportCandidate import org.move.ide.utils.imports.ImportCandidateCollector import org.move.ide.utils.imports.import -import org.move.lang.core.psi.* +import org.move.lang.core.psi.MvElement +import org.move.lang.core.psi.MvPath +import org.move.lang.core.psi.MvUseSpeck +import org.move.lang.core.psi.MvUseStmt import org.move.lang.core.psi.ext.ancestorStrict -import org.move.lang.core.psi.ext.asSmartPointer import org.move.lang.core.psi.ext.hasAncestor import org.move.lang.core.psi.ext.importCandidateNamespaces -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.letStmtScope -import org.move.lang.core.resolve.ref.MvReferenceElement +import org.move.lang.core.psi.ext.qualifier import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility import org.move.openapiext.runWriteCommandAction -class AutoImportFix(element: MvReferenceElement): DiagnosticFix(element), - HighPriorityAction { +class AutoImportFix(element: MvPath): DiagnosticFix(element), + HighPriorityAction { private var isConsumed: Boolean = false @@ -32,11 +31,11 @@ class AutoImportFix(element: MvReferenceElement): DiagnosticFix()) return @@ -75,16 +74,16 @@ class AutoImportFix(element: MvReferenceElement): DiagnosticFix() != null) return null - if (refElement is MvPath && refElement.moduleRef != null) return null + fun findApplicableContext(path: MvPath): Context? { + if (path.reference == null) return null + if (path.resolvable) return null + if (path.ancestorStrict() != null) return null + if (path.qualifier != null) return null // TODO: no auto-import if name in scope, but cannot be resolved - val refName = refElement.referenceName ?: return null - val importContext = ImportContext.from(refElement) + val refName = path.referenceName ?: return null + val importContext = ImportContext.from(path) val candidates = ImportCandidateCollector.getImportCandidates(importContext, refName) return Context(candidates) @@ -94,38 +93,41 @@ class AutoImportFix(element: MvReferenceElement): DiagnosticFix, - val visibilities: Set, - val contextScopeInfo: ContextScopeInfo, + val pathElement: MvPath, + val ns: Set, +// val visibilities: Set, +// val contextScopeInfo: ContextScopeInfo, ) { companion object { fun from( - contextElement: MvReferenceElement, - namespaces: Set, - visibilities: Set, - contextScopeInfo: ContextScopeInfo + pathElement: MvPath, + ns: Set, +// visibilities: Set, +// contextScopeInfo: ContextScopeInfo ): ImportContext { - return ImportContext(contextElement, namespaces, visibilities, contextScopeInfo) + return ImportContext(pathElement, ns) +// return ImportContext(pathElement, ns, visibilities, contextScopeInfo) } - fun from(refElement: MvReferenceElement): ImportContext { - val ns = refElement.importCandidateNamespaces() - val vs = if (refElement.containingScript != null) { - setOf(Visibility.Public, Visibility.PublicScript) - } else { - val module = refElement.containingModule - if (module != null) { - setOf(Visibility.Public, Visibility.PublicFriend(module.asSmartPointer())) - } else { - setOf(Visibility.Public) - } - } - val contextScopeInfo = ContextScopeInfo( - letStmtScope = refElement.letStmtScope, - refItemScopes = refElement.refItemScopes, - ) - return ImportContext(refElement, ns, vs, contextScopeInfo) + fun from(path: MvPath): ImportContext { + val ns = path.importCandidateNamespaces() +// val vs = +// if (path.containingScript != null) { +// setOf(Visibility.Public, Visibility.PublicScript) +// } else { +// val module = path.containingModule +// if (module != null) { +// setOf(Visibility.Public, Visibility.PublicFriend(module.asSmartPointer())) +// } else { +// setOf(Visibility.Public) +// } +// } +// val contextScopeInfo = ContextScopeInfo( +// letStmtScope = path.letStmtScope, +// refItemScopes = path.refItemScopes, +// ) + return ImportContext(path, ns) +// return ImportContext(path, ns, vs, contextScopeInfo) } } } diff --git a/src/main/kotlin/org/move/ide/inspections/imports/BasePathType.kt b/src/main/kotlin/org/move/ide/inspections/imports/BasePathType.kt new file mode 100644 index 000000000..2f2d2df16 --- /dev/null +++ b/src/main/kotlin/org/move/ide/inspections/imports/BasePathType.kt @@ -0,0 +1,75 @@ +package org.move.ide.inspections.imports + +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.* + +// classifies foo of `foo::bar::baz` +sealed class BasePathType { + data object Address: BasePathType() + data class Module(val moduleName: String): BasePathType() + data class Item(val itemName: String): BasePathType() +} + +fun MvPath.basePathType(): BasePathType? { + val rootPath = this.rootPath() + val qualifier = rootPath.qualifier + // foo + if (qualifier == null) { + return rootPath.referenceName?.let { BasePathType.Item(it) } + } + + // 0x1::m + if (qualifier.pathAddress != null) return BasePathType.Address + + val qualifierBase = qualifier.qualifier + // aptos_framework::m::foo + if (qualifierBase != null) { + return BasePathType.Address + } + + // todo: `aptos_framework::m`, + // first resolve aptos_framework into the NamedAddress, then return the BasePathType.Address + + // m::foo + return qualifier.referenceName?.let { BasePathType.Module(it) } +} + +// only Main/Test for now +val MvElement.usageScope: NamedItemScope + get() { + var parentElement = this.parent + while (parentElement != null) { +// if (parentElement is MslOnlyElement) return ItemScope.MAIN + if (parentElement is MvDocAndAttributeOwner && parentElement.hasTestOnlyAttr) { + return NamedItemScope.TEST + } + if (parentElement is MvDocAndAttributeOwner && parentElement.hasVerifyOnlyAttr) { + return NamedItemScope.VERIFY + } + if (parentElement is MvFunction && parentElement.hasTestAttr) { + return NamedItemScope.TEST + } + parentElement = parentElement.parent + } + return NamedItemScope.MAIN + } + +// only Main/Test for now +val MvUseStmt.declaredItemScope: NamedItemScope + get() { + if (this.hasTestOnlyAttr) { + return NamedItemScope.TEST + } + var parentElement = this.parent + while (parentElement != null) { +// if (parentElement is MslOnlyElement) return ItemScope.MAIN + if (parentElement is MvDocAndAttributeOwner && parentElement.hasTestOnlyAttr) { + return NamedItemScope.TEST + } + if (parentElement is MvFunction && parentElement.hasTestAttr) { + return NamedItemScope.TEST + } + parentElement = parentElement.parent + } + return NamedItemScope.MAIN + } diff --git a/src/main/kotlin/org/move/ide/inspections/imports/ImportAnalyzer.kt b/src/main/kotlin/org/move/ide/inspections/imports/ImportAnalyzer.kt index e9ae838a0..d1d44de70 100644 --- a/src/main/kotlin/org/move/ide/inspections/imports/ImportAnalyzer.kt +++ b/src/main/kotlin/org/move/ide/inspections/imports/ImportAnalyzer.kt @@ -2,103 +2,77 @@ package org.move.ide.inspections.imports import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder -import org.move.ide.inspections.imports.PathStart.Companion.pathStart +import org.move.ide.inspections.imports.UseItemType.* import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* import org.move.stdext.chain -private val MvImportsOwner.useSpecks: List - get() { - val specks = mutableListOf() - for (stmt in this.useStmtList) { - specks.addAll(stmt.useSpecks) - } - return specks - } - -private val MvImportsOwner.importOwnerWithSiblings: List - get() { - return when (this) { - is MvModuleBlock -> { - // add all module spec blocks - listOf(this).chain(this.module.allModuleSpecBlocks()).toList() - } - is MvModuleSpecBlock -> { - // add module block - val moduleBlock = this.moduleSpec.moduleItem?.moduleBlock - if (moduleBlock != null) { - listOf(moduleBlock, this) - } else { - listOf(this) - } - } - else -> listOf(this) - } - } - -class ImportAnalyzer(val holder: ProblemsHolder): MvVisitor() { +class ImportAnalyzer2(val holder: ProblemsHolder): MvVisitor() { override fun visitModuleBlock(o: MvModuleBlock) = analyzeImportsOwner(o) - override fun visitScriptBlock(o: MvScriptBlock) = analyzeImportsOwner(o) - override fun visitModuleSpecBlock(o: MvModuleSpecBlock) = analyzeImportsOwner(o) - fun analyzeImportsOwner(importsOwner: MvImportsOwner) { + fun analyzeImportsOwner(importsOwner: MvItemsOwner) { analyzeUseStmtsForScope(importsOwner, NamedItemScope.TEST) analyzeUseStmtsForScope(importsOwner, NamedItemScope.MAIN) } - private fun analyzeUseStmtsForScope(rootImportOwner: MvImportsOwner, itemScope: NamedItemScope) { - val allSpecksHit = mutableSetOf() - val rootImportOwnerWithSiblings = rootImportOwner.importOwnerWithSiblings - val reachablePaths = - rootImportOwnerWithSiblings - .flatMap { it.descendantsOfType() } - .mapNotNull { path -> path.pathStart?.let { Pair(path, it) } } - .filter { it.second.usageScope == itemScope } - for ((path, start) in reachablePaths) { - for (importOwner in path.ancestorsOfType()) { - val useSpecks = - importOwner.importOwnerWithSiblings - .flatMap { it.useSpecks } - .filter { it.scope == itemScope } - val speckHit = - when (start) { - is PathStart.Module -> - useSpecks - .filter { it is UseSpeck.Module || it is UseSpeck.SelfModule } + private fun analyzeUseStmtsForScope(rootItemsOwner: MvItemsOwner, itemScope: NamedItemScope) { + val allUseItemsHit = mutableSetOf() + val rootItemOwnerWithSiblings = rootItemsOwner.itemsOwnerWithSiblings + + val paths = rootItemOwnerWithSiblings + .flatMap { it.descendantsOfType() } + .filter { it.basePath() == it } + .filter { it.usageScope == itemScope } + .filter { !it.hasAncestor() } + + for (path in paths) { + val basePathType = path.basePathType() + for (itemsOwner in path.ancestorsOfType()) { + val useItems = + itemsOwner.itemsOwnerWithSiblings + .flatMap { it.useItems }.filter { it.scope == itemScope } + + val useItemHit = + when (basePathType) { + is BasePathType.Item -> { + useItems.filter { it.type == ITEM } // only hit first encountered to remove duplicates - .firstOrNull { it.nameOrAlias == start.modName } - is PathStart.Item -> - useSpecks.filterIsInstance() + .firstOrNull { it.nameOrAlias == basePathType.itemName } + } + is BasePathType.Module -> { + useItems.filter { it.type == MODULE || it.type == SELF_MODULE } // only hit first encountered to remove duplicates - .firstOrNull { it.nameOrAlias == start.itemName } - // PathStart.Address is fq path, and doesn't participate in imports + .firstOrNull { it.nameOrAlias == basePathType.moduleName } + } + // BasePathType.Address is fq path, and doesn't participate in imports else -> null } - if (speckHit != null) { - allSpecksHit.add(speckHit) + + if (useItemHit != null) { + allUseItemsHit.add(useItemHit) break } } } // includes self - val reachableImportOwners = rootImportOwner.descendantsOfTypeOrSelf() - for (importsOwner in reachableImportOwners) { - val scopeUseStmts = importsOwner.useStmtList.filter { it.declaredItemScope == itemScope } + val reachableItemsOwners = rootItemsOwner.descendantsOfTypeOrSelf() + for (itemsOwner in reachableItemsOwners) { + val scopeUseStmts = itemsOwner.useStmtList.filter { it.declaredItemScope == itemScope } for (useStmt in scopeUseStmts) { - val unusedSpecks = useStmt.useSpecks.toSet() - allSpecksHit - holder.registerStmtSpeckError(useStmt, unusedSpecks) + val unusedUseItems = useStmt.useItems.toSet() - allUseItemsHit + holder.registerStmtSpeckError2(useStmt, unusedUseItems) } } } } -private fun ProblemsHolder.registerStmtSpeckError(useStmt: MvUseStmt, specks: Set) { - val moduleSpecks = specks.filterIsInstance() - if (moduleSpecks.isNotEmpty()) { +fun ProblemsHolder.registerStmtSpeckError2(useStmt: MvUseStmt, useItems: Set) { + val moduleUseItems = useItems.filter { it.type == MODULE } + if (moduleUseItems.isNotEmpty()) { this.registerProblem( useStmt, "Unused use item", @@ -107,8 +81,7 @@ private fun ProblemsHolder.registerStmtSpeckError(useStmt: MvUseStmt, specks: Se return } - val itemSpecks = specks - if (useStmt.useSpecks.size == itemSpecks.size) { + if (useStmt.useItems.size == useItems.size) { // all inner speck types are covered, highlight complete useStmt this.registerProblem( useStmt, @@ -116,14 +89,9 @@ private fun ProblemsHolder.registerStmtSpeckError(useStmt: MvUseStmt, specks: Se ProblemHighlightType.LIKE_UNUSED_SYMBOL ) } else { - for (itemUseSpeck in itemSpecks) { - val useItem = when (itemUseSpeck) { - is UseSpeck.SelfModule -> itemUseSpeck.useItem - is UseSpeck.Item -> itemUseSpeck.useItem - else -> continue - } + for (useItem in useItems) { this.registerProblem( - useItem, + useItem.useSpeck, "Unused use item", ProblemHighlightType.LIKE_UNUSED_SYMBOL ) @@ -131,3 +99,23 @@ private fun ProblemsHolder.registerStmtSpeckError(useStmt: MvUseStmt, specks: Se } } +val MvItemsOwner.itemsOwnerWithSiblings: List + get() { + return when (this) { + is MvModuleBlock -> { + // add all module spec blocks + listOf(this).chain(this.module.allModuleSpecBlocks()).toList() + } + is MvModuleSpecBlock -> { + // add module block + val moduleBlock = this.moduleSpec.moduleItem?.moduleBlock + if (moduleBlock != null) { + listOf(moduleBlock, this) + } else { + listOf(this) + } + } + else -> listOf(this) + } + } + diff --git a/src/main/kotlin/org/move/ide/inspections/imports/PathStart.kt b/src/main/kotlin/org/move/ide/inspections/imports/PathStart.kt deleted file mode 100644 index 4e2b83efc..000000000 --- a/src/main/kotlin/org/move/ide/inspections/imports/PathStart.kt +++ /dev/null @@ -1,81 +0,0 @@ -package org.move.ide.inspections.imports - -import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.MvDocAndAttributeOwner -import org.move.lang.core.psi.ext.hasTestAttr -import org.move.lang.core.psi.ext.hasTestOnlyAttr - -// only Main/Test for now -val MvPath.pathUsageScope: NamedItemScope - get() { - var parentElement = this.parent - while (parentElement != null) { -// if (parentElement is MslOnlyElement) return ItemScope.MAIN - if (parentElement is MvDocAndAttributeOwner && parentElement.hasTestOnlyAttr) { - return NamedItemScope.TEST - } - if (parentElement is MvFunction && parentElement.hasTestAttr) { - return NamedItemScope.TEST - } - parentElement = parentElement.parent - } - return NamedItemScope.MAIN - } - -// only Main/Test for now -val MvUseStmt.declaredItemScope: NamedItemScope - get() { - if (this.hasTestOnlyAttr) { - return NamedItemScope.TEST - } - var parentElement = this.parent - while (parentElement != null) { -// if (parentElement is MslOnlyElement) return ItemScope.MAIN - if (parentElement is MvDocAndAttributeOwner && parentElement.hasTestOnlyAttr) { - return NamedItemScope.TEST - } - if (parentElement is MvFunction && parentElement.hasTestAttr) { - return NamedItemScope.TEST - } - parentElement = parentElement.parent - } - return NamedItemScope.MAIN - } - -sealed class PathStart(open val usageScope: NamedItemScope) { - data class Address( - val addressRef: MvAddressRef, - override val usageScope: NamedItemScope - ): PathStart(usageScope) - - data class Module( - val modName: String, - val moduleRef: MvModuleRef, - override val usageScope: NamedItemScope - ): PathStart(usageScope) - - data class Item( - val itemName: String, - override val usageScope: NamedItemScope - ): PathStart(usageScope) - - companion object { - val MvPath.pathStart: PathStart? - get() { - val usageScope = this.pathUsageScope - val pathModuleRef = this.moduleRef - if (pathModuleRef != null) { - if (pathModuleRef is MvFQModuleRef) { - return Address(pathModuleRef.addressRef, usageScope) - } else { - val modName = pathModuleRef.referenceName ?: return null - return Module(modName, pathModuleRef, usageScope) - } - } else { - val itemName = this.referenceName ?: return null - return Item(itemName, usageScope) - } - } - } -} - diff --git a/src/main/kotlin/org/move/ide/inspections/imports/UseItem.kt b/src/main/kotlin/org/move/ide/inspections/imports/UseItem.kt new file mode 100644 index 000000000..4305a93af --- /dev/null +++ b/src/main/kotlin/org/move/ide/inspections/imports/UseItem.kt @@ -0,0 +1,57 @@ +package org.move.ide.inspections.imports + +import org.move.cli.settings.debugError +import org.move.ide.inspections.imports.UseItemType.* +import org.move.lang.core.psi.MvUseSpeck +import org.move.lang.core.psi.MvUseStmt +import org.move.lang.core.psi.NamedItemScope +import org.move.lang.core.psi.ext.MvItemsOwner +import org.move.lang.core.resolve2.PathKind +import org.move.lang.core.resolve2.pathKind +import org.move.lang.core.resolve2.util.forEachLeafSpeck + +enum class UseItemType { + MODULE, SELF_MODULE, ITEM; +} + +data class UseItem( + val useSpeck: MvUseSpeck, + val nameOrAlias: String, + val type: UseItemType, + val scope: NamedItemScope +) + +val MvItemsOwner.useItems: List + get() = this.useStmtList.flatMap { it.useItems } + +val MvUseStmt.useItems: List + get() { + val items = mutableListOf() + val stmtItemScope = this.declaredItemScope + this.forEachLeafSpeck { path, useAlias -> + val useSpeck = path.parent as MvUseSpeck + val nameOrAlias = useAlias?.name ?: path.referenceName ?: return@forEachLeafSpeck false + val pathKind = path.pathKind() + when (pathKind) { + is PathKind.QualifiedPath.Module -> + items.add(UseItem(useSpeck, nameOrAlias, MODULE, stmtItemScope)) + is PathKind.QualifiedPath.ModuleItem -> { + debugError("not reachable, must be a bug") + return@forEachLeafSpeck false + } + is PathKind.QualifiedPath -> { + if (pathKind.path.referenceName == "Self") { + val moduleName = + useAlias?.name ?: pathKind.qualifier.referenceName ?: return@forEachLeafSpeck false + items.add(UseItem(useSpeck, moduleName, SELF_MODULE, stmtItemScope)) + } else { + items.add(UseItem(useSpeck, nameOrAlias, ITEM, stmtItemScope)) + } + } + else -> return@forEachLeafSpeck false + } + false + } + + return items + } diff --git a/src/main/kotlin/org/move/ide/intentions/RemoveCurlyBracesIntention.kt b/src/main/kotlin/org/move/ide/intentions/RemoveCurlyBracesIntention.kt index 5f79728ed..efeaf1ccf 100644 --- a/src/main/kotlin/org/move/ide/intentions/RemoveCurlyBracesIntention.kt +++ b/src/main/kotlin/org/move/ide/intentions/RemoveCurlyBracesIntention.kt @@ -3,51 +3,72 @@ package org.move.ide.intentions import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement -import org.move.lang.core.psi.MvUseItemGroup +import org.move.lang.core.psi.MvUseSpeck import org.move.lang.core.psi.MvUseStmt import org.move.lang.core.psi.ext.ancestorStrict import org.move.lang.core.psi.ext.endOffset import org.move.lang.core.psi.ext.startOffset import org.move.lang.core.psi.psiFactory -class RemoveCurlyBracesIntention : MvElementBaseIntentionAction() { +class RemoveCurlyBracesIntention: MvElementBaseIntentionAction() { override fun getText(): String = "Remove curly braces" override fun getFamilyName(): String = text - data class Context(val itemUseGroup: MvUseItemGroup) + data class Context(val useSpeck: MvUseSpeck) override fun findApplicableContext(project: Project, editor: Editor, element: PsiElement): Context? { val useStmt = element.ancestorStrict() ?: return null - val useItemGroup = useStmt.itemUseSpeck?.useItemGroup ?: return null - if (useItemGroup.useItemList.size > 1) return null - return Context(useItemGroup) + val useSpeck = useStmt.useSpeck ?: return null + val useGroup = useSpeck.useGroup ?: return null + if (useGroup.useSpeckList.size > 1) return null + return Context(useSpeck) } override fun invoke(project: Project, editor: Editor, ctx: Context) { - val itemUseGroup = ctx.itemUseGroup + val useSpeck = ctx.useSpeck // Save the cursor position, adjusting for curly brace removal val caret = editor.caretModel.offset val newOffset = when { - caret < itemUseGroup.startOffset -> caret - caret < itemUseGroup.endOffset -> caret - 1 + caret < useSpeck.startOffset -> caret + caret < useSpeck.endOffset -> caret - 1 else -> caret - 2 } - itemUseGroup.removeCurlyBraces() + useSpeck.removeCurlyBraces() editor.caretModel.moveToOffset(newOffset) } } -private fun MvUseItemGroup.removeCurlyBraces() { +private fun MvUseSpeck.removeCurlyBraces() { val psiFactory = this.project.psiFactory - val itemUse = this.useItemList.singleOrNull() ?: return - val refName = itemUse.referenceName - val aliasName = itemUse.useAlias?.name + val useGroup = this.useGroup ?: return + val itemUseSpeck = useGroup.useSpeckList.singleOrNull() ?: return - var newText = refName - if (aliasName != null) { - newText += " as $aliasName" + val newPath = psiFactory.path("0x1::dummy::call") + + val itemIdentifier = itemUseSpeck.path.identifier ?: return + // copy identifier + newPath.identifier?.replace(itemIdentifier.copy()) + // copy module path + newPath.path?.replace(this.path.copy()) + + val dummyUseSpeck = psiFactory.useSpeck("0x1::dummy::call as mycall") + dummyUseSpeck.path.replace(newPath) + + val useAlias = itemUseSpeck.useAlias + if (useAlias != null) { + dummyUseSpeck.useAlias?.replace(useAlias) + } else { + dummyUseSpeck.useAlias?.delete() } - val newItemUse = psiFactory.useItem(newText) - this.replace(newItemUse) + + +// val aliasName = itemUseSpeck.useAlias?.name +// +// var newText = refName +// if (aliasName != null) { +// newText += " as $aliasName" +// } +// val newItemUse = psiFactory.useSpeckForGroup(newText) + this.replace(dummyUseSpeck) } diff --git a/src/main/kotlin/org/move/ide/refactoring/MvImportOptimizer.kt b/src/main/kotlin/org/move/ide/refactoring/MvImportOptimizer.kt index 7b36293b0..598106157 100644 --- a/src/main/kotlin/org/move/ide/refactoring/MvImportOptimizer.kt +++ b/src/main/kotlin/org/move/ide/refactoring/MvImportOptimizer.kt @@ -5,11 +5,10 @@ import com.intellij.codeInspection.ProblemsHolder import com.intellij.lang.ImportOptimizer import com.intellij.psi.* import org.move.ide.inspections.MvUnusedImportInspection -import org.move.ide.inspections.imports.ImportAnalyzer +import org.move.ide.inspections.imports.ImportAnalyzer2 import org.move.ide.utils.imports.COMPARATOR_FOR_ITEMS_IN_USE_GROUP import org.move.ide.utils.imports.UseStmtWrapper import org.move.lang.MoveFile -import org.move.lang.MvElementTypes.L_BRACE import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* import org.move.stdext.withNext @@ -27,10 +26,10 @@ class MvImportOptimizer : ImportOptimizer { } val holder = ProblemsHolder(InspectionManager.getInstance(file.project), file, false) - val importVisitor = ImportAnalyzer(holder) + val importVisitor = ImportAnalyzer2(holder) object : PsiRecursiveElementVisitor() { override fun visitElement(element: PsiElement) { - if (element is MvImportsOwner) { + if (element is MvItemsOwner) { importVisitor.analyzeImportsOwner(element) } else { super.visitElement(element) @@ -45,11 +44,11 @@ class MvImportOptimizer : ImportOptimizer { (useElement.nextSibling as? PsiWhiteSpace)?.delete() useElement.delete() } - is MvUseItem -> { + is MvUseSpeck -> { // remove whitespace following comma, if first position in a group - val useItemGroup = useElement.parent as? MvUseItemGroup - if (useItemGroup != null - && useItemGroup.useItemList.firstOrNull() == useElement + val useGroup = useElement.parent as? MvUseGroup + if (useGroup != null + && useGroup.useSpeckList.firstOrNull() == useElement ) { val followingComma = useElement.getNextNonCommentSibling() followingComma?.rightSiblings @@ -66,9 +65,9 @@ class MvImportOptimizer : ImportOptimizer { val useStmtsWithOwner = file.descendantsOfType().groupBy { it.parent } for ((stmtOwner, useStmts) in useStmtsWithOwner.entries) { useStmts.forEach { useStmt -> - useStmt.itemUseSpeck?.let { + useStmt.useSpeck?.let { removeCurlyBracesIfPossible(it, psiFactory) - it.useItemGroup?.sortUseItems() + it.useGroup?.sortUseSpecks() } } if (stmtOwner is MvModuleBlock) { @@ -77,82 +76,52 @@ class MvImportOptimizer : ImportOptimizer { } } - private fun mergeItemGroups(useStmtOwner: MvImportsOwner) { - val psiFactory = useStmtOwner.project.psiFactory - val leftBrace = useStmtOwner.findFirstChildByType(L_BRACE) ?: return + /** Returns true if successfully removed, e.g. `use aaa::{bbb};` -> `use aaa::bbb;` */ + private fun removeCurlyBracesIfPossible(rootUseSpeck: MvUseSpeck, psiFactory: MvPsiFactory) { + val itemUseSpeck = rootUseSpeck.useGroup?.asTrivial ?: return - val useStmts = useStmtOwner.useStmtList - useStmts - .groupBy { Pair(it.fqModuleText, it.hasTestOnlyAttr) } - .forEach { (key, stmts) -> - val (fqModuleText, isTestOnly) = key - if (fqModuleText == null) return@forEach + val newUseSpeck = psiFactory.useSpeck("0x1::dummy::call") + val newUseSpeckPath = newUseSpeck.path + newUseSpeckPath.path?.replace(rootUseSpeck.path) + itemUseSpeck.path.identifier?.let { newUseSpeckPath.identifier?.replace(it) } - // special case: if single stmt and import like `use 0x1::Main::Self;`, change to `0x1::Main` -// if (stmts.size == 1) { -// val stmt = stmts.single() -// val useItem = stmt.childUseItems.singleOrNull()?.takeIf { it.text == "Self" } -// if (useItem != null) { -// val newStmt = psiFactory.useStmt(fqModuleText, isTestOnly) -// stmt.replace(newStmt) -// } -// return@forEach -// } - - val useItemNames = mutableListOf() - if (stmts.any { it.moduleUseSpeck != null }) { - useItemNames.add("Self") - } - useItemNames.addAll(stmts.flatMap { it.childUseItems }.map { it.text }) - val newStmt = - psiFactory.useStmt( - "$fqModuleText::{${useItemNames.joinToString(", ")}}", - isTestOnly - ) - useStmtOwner.addAfter(newStmt, leftBrace) - stmts.forEach { it.delete() } - } - } - - companion object { - fun MvUseItemGroup.sortUseItems() { - val sortedList = useItemList - .sortedWith(COMPARATOR_FOR_ITEMS_IN_USE_GROUP) - .map { it.copy() } - useItemList.zip(sortedList).forEach { it.first.replace(it.second) } + val useAlias = itemUseSpeck.useAlias + if (useAlias != null) { + newUseSpeck.add(useAlias) } - /** Returns true if successfully removed, e.g. `use aaa::{bbb};` -> `use aaa::bbb;` */ - private fun removeCurlyBracesIfPossible(useSpeck: MvItemUseSpeck, psiFactory: MvPsiFactory) { - val useItemText = useSpeck.useItemGroup?.asTrivial?.text ?: return - val fqModuleText = useSpeck.fqModuleRef.text - val newUseSpeck = psiFactory.itemUseSpeck(fqModuleText, useItemText) - useSpeck.replace(newUseSpeck) - } + rootUseSpeck.replace(newUseSpeck) + } - private fun reorderUseStmtsIntoGroups(useScope: MvImportsOwner) { - val useStmts = useScope.useStmtList - val first = useScope.childrenOfType() - .firstOrNull { it !is MvAttr && it !is PsiComment } ?: return - val psiFactory = useScope.project.psiFactory - val sortedUses = useStmts - .asSequence() - .map { UseStmtWrapper(it) } - .sorted() - for ((useWrapper, nextUseWrapper) in sortedUses.withNext()) { - val addedUseItem = useScope.addBefore(useWrapper.useStmt, first) - useScope.addAfter(psiFactory.createNewline(), addedUseItem) - val addNewLine = - useWrapper.packageGroupLevel != nextUseWrapper?.packageGroupLevel + private fun reorderUseStmtsIntoGroups(useScope: MvItemsOwner) { + val useStmts = useScope.useStmtList + val first = useScope.childrenOfType() + .firstOrNull { it !is MvAttr && it !is PsiComment } ?: return + val psiFactory = useScope.project.psiFactory + val sortedUses = useStmts + .asSequence() + .map { UseStmtWrapper(it) } + .sorted() + for ((useWrapper, nextUseWrapper) in sortedUses.withNext()) { + val addedUseItem = useScope.addBefore(useWrapper.useStmt, first) + useScope.addAfter(psiFactory.createNewline(), addedUseItem) + val addNewLine = + useWrapper.packageGroupLevel != nextUseWrapper?.packageGroupLevel // && (nextUseWrapper != null || useScope is MvModuleBlock) - if (addNewLine) { - useScope.addAfter(psiFactory.createNewline(), addedUseItem) - } - } - useStmts.forEach { - (it.nextSibling as? PsiWhiteSpace)?.delete() - it.delete() + if (addNewLine) { + useScope.addAfter(psiFactory.createNewline(), addedUseItem) } } + useStmts.forEach { + (it.nextSibling as? PsiWhiteSpace)?.delete() + it.delete() + } + } + + private fun MvUseGroup.sortUseSpecks() { + val sortedList = useSpeckList + .sortedWith(COMPARATOR_FOR_ITEMS_IN_USE_GROUP) + .map { it.copy() } + useSpeckList.zip(sortedList).forEach { it.first.replace(it.second) } } } diff --git a/src/main/kotlin/org/move/ide/search/MvUsageTypeProvider.kt b/src/main/kotlin/org/move/ide/search/MvUsageTypeProvider.kt index ef705a82d..21c6aaf96 100644 --- a/src/main/kotlin/org/move/ide/search/MvUsageTypeProvider.kt +++ b/src/main/kotlin/org/move/ide/search/MvUsageTypeProvider.kt @@ -11,9 +11,8 @@ import com.intellij.usages.impl.rules.UsageType import com.intellij.usages.impl.rules.UsageTypeProviderEx import org.move.lang.core.psi.MvAddressRef import org.move.lang.core.psi.MvExpr -import org.move.lang.core.psi.MvFQModuleRef -object MvUsageTypeProvider : UsageTypeProviderEx { +object MvUsageTypeProvider: UsageTypeProviderEx { // Instantiate each UsageType only once, so that the equality check in UsageTypeGroup.equals() works correctly // private val TYPE_REFERENCE = UsageType { "type reference" } @@ -46,9 +45,8 @@ object MvUsageTypeProvider : UsageTypeProviderEx { } override fun getUsageType(element: PsiElement, targets: Array): UsageType? { -// val refinedElement = element?.findExpansionElements()?.firstOrNull()?.parent ?: element val parent = element.parent ?: return null - if (element is MvFQModuleRef) return MODULE +// if (element is MvFQModuleRef) return MODULE return when (parent) { is MvExpr -> EXPR is MvAddressRef -> ADDRESS_REF diff --git a/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt b/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt index 1ab179473..14b48bca5 100644 --- a/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt +++ b/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt @@ -16,7 +16,7 @@ class MvStructureViewTreeElement(val element: NavigatablePsiElement): StructureV val isPublic: Boolean get() { return when (element) { - is MvFunction -> element.visibility != FunctionVisibility.PRIVATE + is MvFunction -> element.isPublic is MvConst -> false else -> true } diff --git a/src/main/kotlin/org/move/ide/utils/FunctionSignature.kt b/src/main/kotlin/org/move/ide/utils/FunctionSignature.kt index 7b6796aeb..1232b1d52 100644 --- a/src/main/kotlin/org/move/ide/utils/FunctionSignature.kt +++ b/src/main/kotlin/org/move/ide/utils/FunctionSignature.kt @@ -44,11 +44,11 @@ data class FunctionSignature( fun resolve(callable: MvCallable): FunctionSignature? = when (callable) { is MvCallExpr -> { - val function = callable.path.reference?.resolveWithAliases() as? MvFunction + val function = callable.path.reference?.resolveFollowingAliases() as? MvFunction function?.getSignature() } is MvMethodCall -> { - val function = callable.reference.resolveWithAliases() as? MvFunction + val function = callable.reference.resolveFollowingAliases() as? MvFunction function?.getSignature() } else -> null diff --git a/src/main/kotlin/org/move/ide/utils/imports/ImportCandidate.kt b/src/main/kotlin/org/move/ide/utils/imports/ImportCandidate.kt index 8547d8035..5c0face34 100644 --- a/src/main/kotlin/org/move/ide/utils/imports/ImportCandidate.kt +++ b/src/main/kotlin/org/move/ide/utils/imports/ImportCandidate.kt @@ -1,88 +1,6 @@ package org.move.ide.utils.imports -import org.move.ide.inspections.imports.ImportContext -import org.move.lang.MoveFile -import org.move.lang.core.psi.MvNamedElement import org.move.lang.core.psi.MvQualNamedElement -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.MatchingProcessor -import org.move.lang.core.resolve.processModuleInnerItems -import org.move.lang.core.resolve.processQualItem -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility import org.move.lang.core.types.ItemQualName -import org.move.lang.index.MvNamedElementIndex -import org.move.lang.moveProject -import org.move.openapiext.common.checkUnitTestMode -import org.move.openapiext.common.isUnitTestMode data class ImportCandidate(val element: MvQualNamedElement, val qualName: ItemQualName) - -object ImportCandidateCollector { - fun getImportCandidates( - context: ImportContext, - targetName: String, - itemFilter: (MvQualNamedElement) -> Boolean = { true } - ): List { - val (contextElement, namespaces, visibilities, itemVis) = context - - val project = contextElement.project - val moveProject = contextElement.moveProject ?: return emptyList() - val searchScope = moveProject.searchScope() - - val allItems = mutableListOf() - if (isUnitTestMode) { - // always add current file in tests - val currentFile = contextElement.containingFile as? MoveFile ?: return emptyList() - - val items = mutableListOf() - processFileItemsForUnitTests(currentFile, namespaces, visibilities, itemVis) { - if (it.element is MvQualNamedElement && it.name == targetName) { - items.add(it.element) - } - false - } -// return elements - -// val items = currentFile.qualifiedItems(targetName, namespaces, visibilities, itemVis) - allItems.addAll(items) - } - - MvNamedElementIndex - .processElementsByName(project, targetName, searchScope) { element -> - processQualItem(element, namespaces, visibilities, itemVis) { - val entryElement = it.element - if (entryElement !is MvQualNamedElement) return@processQualItem false - if (it.name == targetName) { - allItems.add(entryElement) - } - false - } - true - } - - return allItems - .filter(itemFilter) - .mapNotNull { item -> item.qualName?.let { ImportCandidate(item, it) } } - } -} - -private fun processFileItemsForUnitTests( - file: MoveFile, - namespaces: Set, - visibilities: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, -): Boolean { - checkUnitTestMode() - for (module in file.modules()) { - if ( - Namespace.MODULE in namespaces - && processor.match(contextScopeInfo, module) - ) { - return true - } - if (processModuleInnerItems(module, namespaces, visibilities, contextScopeInfo, processor)) return true - } - return false -} diff --git a/src/main/kotlin/org/move/ide/utils/imports/ImportCandidateCollector.kt b/src/main/kotlin/org/move/ide/utils/imports/ImportCandidateCollector.kt new file mode 100644 index 000000000..42e649bce --- /dev/null +++ b/src/main/kotlin/org/move/ide/utils/imports/ImportCandidateCollector.kt @@ -0,0 +1,89 @@ +package org.move.ide.utils.imports + +import com.intellij.codeInsight.completion.CompletionParameters +import com.intellij.codeInsight.completion.PrefixMatcher +import com.intellij.psi.PsiElement +import org.move.ide.inspections.imports.ImportContext +import org.move.lang.core.psi.MvQualNamedElement +import org.move.lang.core.resolve.VisibilityStatus.Visible +import org.move.lang.core.resolve2.createFilter +import org.move.lang.core.resolve2.namespace +import org.move.lang.core.resolve2.visInfo +import org.move.lang.index.MvNamedElementIndex +import org.move.lang.moveProject + +object ImportCandidateCollector { + + fun getImportCandidates(context: ImportContext, targetName: String): List { + val (pathElement, namespaces) = context + + val project = pathElement.project + val moveProject = pathElement.moveProject ?: return emptyList() + val searchScope = moveProject.searchScope() + + val allItems = mutableListOf() +// if (isUnitTestMode) { +// // always add current file in tests +// val currentFile = pathElement.containingFile as? MoveFile ?: return emptyList() +// val items = mutableListOf() +// processFileItemsForUnitTests(currentFile, namespaces, visibilities, itemVis, createProcessor { +// val element = it.element +// if (element is MvQualNamedElement && it.name == targetName) { +// items.add(element) +// } +// }) +// allItems.addAll(items) +// } + + MvNamedElementIndex + .processElementsByName(project, targetName, searchScope) { element -> + val elementNs = element.namespace + if (elementNs !in namespaces) return@processElementsByName true + val visibilityFilter = element.visInfo().createFilter() + + val visibilityStatus = visibilityFilter.filter(pathElement, namespaces) + if (visibilityStatus != Visible) return@processElementsByName true + + if (element !is MvQualNamedElement) return@processElementsByName true + if (element.name == targetName) { + allItems.add(element) + } + +// processQualItem(element, namespaces, visibilities, itemVis) { +// val entryElement = it.element +// if (entryElement !is MvQualNamedElement) return@processQualItem true +// if (it.name == targetName) { +// allItems.add(entryElement) +// } +// false +// } + true + } + + return allItems +// .filter(itemFilter) + .mapNotNull { item -> item.qualName?.let { ImportCandidate(item, it) } } + } + + fun getCompletionCandidates( + parameters: CompletionParameters, + prefixMatcher: PrefixMatcher, + processedPathNames: Set, + importContext: ImportContext, + itemFilter: (PsiElement) -> Boolean = { true } + ): List { + val project = parameters.position.project + val keys = hashSetOf().apply { + val names = MvNamedElementIndex.getAllKeys(project) + addAll(names) + removeAll(processedPathNames) + } + + return prefixMatcher.sortMatching(keys) + .flatMap { + getImportCandidates(importContext, it) + .distinctBy { it.element } + .filter { itemFilter(it.element) } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/ide/utils/imports/ImportUtils.kt b/src/main/kotlin/org/move/ide/utils/imports/ImportUtils.kt index d933b52a4..5faf7931a 100644 --- a/src/main/kotlin/org/move/ide/utils/imports/ImportUtils.kt +++ b/src/main/kotlin/org/move/ide/utils/imports/ImportUtils.kt @@ -1,9 +1,7 @@ package org.move.ide.utils.imports import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.childrenOfType -import org.move.lang.core.psi.ext.hasTestOnlyAttr -import org.move.lang.core.psi.ext.names +import org.move.lang.core.psi.ext.* import org.move.lang.core.types.ItemQualName import org.move.openapiext.checkWriteAccessAllowed @@ -22,7 +20,7 @@ fun ImportCandidate.import(context: MvElement) { insertionScope.insertUseItem(qualName, insertTestOnly) } -private fun MvImportsOwner.insertUseItem(usePath: ItemQualName, testOnly: Boolean) { +private fun MvItemsOwner.insertUseItem(usePath: ItemQualName, testOnly: Boolean) { if (tryInsertingIntoExistingUseStmt(this, usePath, testOnly)) return @@ -41,62 +39,125 @@ private fun MvImportsOwner.insertUseItem(usePath: ItemQualName, testOnly: Boolea } private fun tryInsertingIntoExistingUseStmt( - mod: MvImportsOwner, - usePath: ItemQualName, + mod: MvItemsOwner, + itemQualName: ItemQualName, testOnly: Boolean ): Boolean { - if (usePath.moduleName == null) return false + if (itemQualName.moduleName == null) return false val psiFactory = mod.project.psiFactory - return mod - .useStmtList - .filter { it.hasTestOnlyAttr == testOnly } - .mapNotNull { it.itemUseSpeck } - .any { tryGroupWithItemSpeck(psiFactory, it, usePath) } + val useStmts = mod.useStmtList.filter { it.hasTestOnlyAttr == testOnly } + return useStmts + .any { tryGroupWithItemSpeck(psiFactory, it, itemQualName) } } private fun tryGroupWithItemSpeck( - psiFactory: MvPsiFactory, itemUseSpeck: MvItemUseSpeck, usePath: ItemQualName + psiFactory: MvPsiFactory, useStmt: MvUseStmt, itemQualName: ItemQualName ): Boolean { - val fqModuleName = usePath.editorModuleFqName() ?: error("checked in the upper level") - if (!itemUseSpeck.fqModuleRef.textMatches(fqModuleName)) return false + val rootUseSpeck = useStmt.useSpeck ?: return false - val itemName = usePath.itemName - if (itemName in itemUseSpeck.names()) return true + val useGroup = rootUseSpeck.useGroup + // 0x1::m -> module imports does not support groups, can't insert + if (useGroup == null && rootUseSpeck.path.length < 3) return false - val useItem = psiFactory.useItem(itemName) - val useItemGroup = itemUseSpeck.useItemGroup - if (useItemGroup != null) { + // searching for the statement with the same module qualifier + val itemModulePath = itemQualName.editorModuleFqName() ?: error("moduleName cannot be zero") + if (useGroup == null) { + val modulePath = rootUseSpeck.path.qualifier ?: return false + if (!modulePath.textMatches(itemModulePath)) return false + } else { + val modulePath = rootUseSpeck.path + if (!modulePath.textMatches(itemModulePath)) return false + } + + val itemName = itemQualName.itemName + val newUseSpeck = psiFactory.useSpeckForGroup(itemName) + + if (useGroup == null) { + // 0x1::dummy::{} + val useSpeckWithGroup = psiFactory.useSpeckWithEmptyUseGroup() + + // [0x1::m]::item + // ^ qualifier + val qualifier = rootUseSpeck.path.qualifier ?: return false + // 0x1::dummy::{} -> 0x1::m::{} + useSpeckWithGroup.path.replace(qualifier) + + // 0x1::m::{} -> 0x1::m::{item as dummy} + val existingItemName = rootUseSpeck.path.referenceName ?: return false + val groupItemUseSpeck = psiFactory.useSpeckForGroupWithDummyAlias(existingItemName) + + // 0x1::m::{item as dummy} -> 0x1::m::{item as myitem} + val existingUseAlias = rootUseSpeck.useAlias + if (existingUseAlias != null) { + groupItemUseSpeck.useAlias?.replace(existingUseAlias) + } else { + groupItemUseSpeck.useAlias?.delete() + } + + val newUseGroup = useSpeckWithGroup.useGroup!! + newUseGroup.addAfter(groupItemUseSpeck, newUseGroup.lBrace) + + // 0x1::m::{item as myitem} -> 0x1::m::{item as myitem, } + val comma = newUseGroup.addBefore(psiFactory.createComma(), newUseGroup.rBrace) + + // 0x1::m::{item as myitem, } -> 0x1::m::{item as myitem, item2} + newUseGroup.addAfter(newUseSpeck, comma) + + useSpeckWithGroup.useGroup?.replace(newUseGroup) + useStmt.useSpeck?.replace(useSpeckWithGroup) + } else { // add after the last item - val itemList = useItemGroup.useItemList - if (itemList.isEmpty()) { + val useSpeckList = useGroup.useSpeckList + if (useSpeckList.isEmpty()) { // use 0x1::m::{}; - useItemGroup.addAfter(useItem, useItemGroup.lBrace) + useGroup.addAfter(newUseSpeck, useGroup.lBrace) } else { // use 0x1::m::{item1} -> use 0x1::m::{item1, item2} - val lastItem = itemList.last() - useItemGroup.addAfter( - useItem, - useItemGroup.addAfter(psiFactory.createComma(), lastItem) - ) + val lastItem = useSpeckList.last() + val newComma = useGroup.addAfter(psiFactory.createComma(), lastItem) + useGroup.addAfter(newUseSpeck, newComma) } - } else { - val existingItem = itemUseSpeck.useItem ?: return true + } - val existingItemCopy = existingItem.copy() - val itemGroup = existingItem.replace(psiFactory.useItemGroup(listOf())) as MvUseItemGroup +// val useItems = useStmt.useItems +// if (useItems.all { it.type == MODULE }) return false +// +// val itemUseItem = useItems.firstOrNull() ?: return false +// val itemUseSpeck = itemUseItem.useSpeck +// val qualifier = itemUseSpeck.qualifier ?: itemUseSpeck.path.qualifier ?: return false +// +// val fqModuleName = usePath.editorModuleFqName() ?: error("checked in the upper level") +// if (!qualifier.textMatches(fqModuleName)) return false - val comma = itemGroup.addAfter( - psiFactory.createComma(), - itemGroup.addAfter(existingItemCopy, itemGroup.lBrace) - ) - itemGroup.addAfter(useItem, comma) - } +// val itemName = usePath.itemName +// val useStmtNames = useItems.map { it.nameOrAlias } +// if (itemName in useStmtNames) return true + +// val useGroup = itemUseSpeck.useGroup +// val useSpeck = psiFactory.useSpeckForGroup(itemName) +// if (useGroup != null) { +// // add after the last item +// val useSpeckList = useGroup.useSpeckList +// if (useSpeckList.isEmpty()) { +// // use 0x1::m::{}; +// useGroup.addAfter(useSpeck, useGroup.lBrace) +// } else { +// // use 0x1::m::{item1} -> use 0x1::m::{item1, item2} +// val lastItem = useSpeckList.last() +// useGroup.addAfter( +// useSpeck, +// useGroup.addAfter(psiFactory.createComma(), lastItem) +// ) +// } +// } else { +// } return true } -private val List.lastElement: T? get() = maxByOrNull { it.textOffset } +private val List.lastElement: T? get() = maxByOrNull { it.textOffset } -private fun insertUseStmtAtTheCorrectLocation(mod: MvImportsOwner, useStmt: MvUseStmt): Boolean { +@Suppress("SameReturnValue") +private fun insertUseStmtAtTheCorrectLocation(mod: MvItemsOwner, useStmt: MvUseStmt): Boolean { val psiFactory = MvPsiFactory(mod.project) val newline = psiFactory.createNewline() val useStmts = mod.childrenOfType().map(::UseStmtWrapper) diff --git a/src/main/kotlin/org/move/ide/utils/imports/UseStmtWrapper.kt b/src/main/kotlin/org/move/ide/utils/imports/UseStmtWrapper.kt index 556e61464..9b1474c3b 100644 --- a/src/main/kotlin/org/move/ide/utils/imports/UseStmtWrapper.kt +++ b/src/main/kotlin/org/move/ide/utils/imports/UseStmtWrapper.kt @@ -5,13 +5,23 @@ package org.move.ide.utils.imports -import org.move.lang.core.psi.MvAddressRef -import org.move.lang.core.psi.MvUseItem +import org.move.cli.MoveProject +import org.move.lang.core.psi.MvUseSpeck import org.move.lang.core.psi.MvUseStmt -import org.move.lang.core.psi.ext.* +import org.move.lang.core.psi.ext.basePath +import org.move.lang.core.psi.ext.hasTestOnlyAttr +import org.move.lang.core.psi.ext.hasVerifyOnlyAttr +import org.move.lang.core.psi.ext.isSelf +import org.move.lang.moveProject -class UseStmtWrapper(val useStmt: MvUseStmt) : Comparable { - private val addr: MvAddressRef? get() = this.useStmt.addressRef +class UseStmtWrapper(val useStmt: MvUseStmt): Comparable { + private val namedAddress: String? + get() { + val useSpeck = useStmt.useSpeck ?: return null + val base = useSpeck.path.basePath() + if (base.pathAddress != null) return null + return base.identifier?.text + } // `use` order: // 1. Standard library (stdlib) @@ -24,35 +34,41 @@ class UseStmtWrapper(val useStmt: MvUseStmt) : Comparable { val packageGroupLevel: Int = when { this.useStmt.hasTestOnlyAttr -> 5 this.useStmt.hasVerifyOnlyAttr -> 6 - else -> this.addr?.useGroupLevel ?: -1 + else -> getBetweenGroupsLevel(this.namedAddress, useStmt.moveProject) } - val addressLevel: Int = when (this.addr?.namedAddress?.referenceName?.lowercase()) { + override fun compareTo(other: UseStmtWrapper): Int = + compareValuesBy( + this, + other, + { it.packageGroupLevel }, + { getWithinGroupLevel(it.namedAddress) }, + { it.useStmt.useSpeck?.text ?: "" } + ) +} + +val COMPARATOR_FOR_ITEMS_IN_USE_GROUP: Comparator = + compareBy { !it.isSelf }.thenBy { it.path.referenceName?.lowercase() } + +private fun getWithinGroupLevel(namedAddress: String?): Int = + when (namedAddress?.lowercase()) { "std" -> 0 "aptos_std" -> 1 "aptos_framework" -> 2 "aptos_token" -> 3 else -> 4 } -// val packageGroupLevel: Int = when { -// -// basePath?.self != null -> 6 -// basePath?.`super` != null -> 5 -// basePath?.crate != null -> 4 -// else -> when (basePath?.reference?.resolve()?.containingCrate?.origin) { -// PackageOrigin.WORKSPACE -> 3 -// PackageOrigin.DEPENDENCY -> 2 -// PackageOrigin.STDLIB, PackageOrigin.STDLIB_DEPENDENCY -> 1 -// null -> 3 -// } -// } - override fun compareTo(other: UseStmtWrapper): Int = - compareValuesBy( - this, other, - { it.packageGroupLevel }, { it.addressLevel }, { it.useStmt.useSpeckText } - ) -} +private fun getBetweenGroupsLevel(namedAddress: String?, moveProject: MoveProject?): Int { + // sort to the end if not a named address + if (namedAddress == null) return 4 -val COMPARATOR_FOR_ITEMS_IN_USE_GROUP: Comparator = - compareBy { !it.isSelf }.thenBy { it.referenceName.lowercase() } + val name = namedAddress.lowercase() + val currentPackageAddresses = + moveProject?.currentPackageAddresses()?.keys.orEmpty().map { it.lowercase() } + return when (name) { + "std", "aptos_std", "aptos_framework", "aptos_token" -> 1 + !in currentPackageAddresses -> 2 + else -> 3 + } +} diff --git a/src/main/kotlin/org/move/lang/MoveFile.kt b/src/main/kotlin/org/move/lang/MoveFile.kt index 054c7caeb..668aea168 100644 --- a/src/main/kotlin/org/move/lang/MoveFile.kt +++ b/src/main/kotlin/org/move/lang/MoveFile.kt @@ -4,7 +4,6 @@ import com.intellij.extapi.psi.PsiFileBase import com.intellij.openapi.fileTypes.FileType import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.ex.temp.TempFileSystem import com.intellij.psi.FileViewProvider import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile diff --git a/src/main/kotlin/org/move/lang/MoveParserDefinition.kt b/src/main/kotlin/org/move/lang/MoveParserDefinition.kt index 2317eaac1..10d9bc741 100644 --- a/src/main/kotlin/org/move/lang/MoveParserDefinition.kt +++ b/src/main/kotlin/org/move/lang/MoveParserDefinition.kt @@ -63,6 +63,6 @@ class MoveParserDefinition : ParserDefinition { /** * Should be increased after any change of parser rules */ - const val PARSER_VERSION: Int = LEXER_VERSION + 49 + const val PARSER_VERSION: Int = LEXER_VERSION + 50 } } diff --git a/src/main/kotlin/org/move/lang/core/completion/CommonCompletionContributor.kt b/src/main/kotlin/org/move/lang/core/completion/CommonCompletionContributor.kt index 90626d122..fdf13423c 100644 --- a/src/main/kotlin/org/move/lang/core/completion/CommonCompletionContributor.kt +++ b/src/main/kotlin/org/move/lang/core/completion/CommonCompletionContributor.kt @@ -10,29 +10,28 @@ import org.move.lang.core.psi.MvModule class CommonCompletionContributor : CompletionContributor() { init { extend(CompletionType.BASIC, PrimitiveTypesCompletionProvider) - extend(CompletionType.BASIC, NamesCompletionProvider) - extend(CompletionType.BASIC, FunctionsCompletionProvider) - extend(CompletionType.BASIC, SchemasCompletionProvider) +// extend(CompletionType.BASIC, NamesCompletionProvider) +// extend(CompletionType.BASIC, FunctionsCompletionProvider) +// extend(CompletionType.BASIC, SchemasCompletionProvider) extend(CompletionType.BASIC, SpecItemCompletionProvider) - extend(CompletionType.BASIC, AddressesCompletionProvider) + + // addresses + extend(CompletionType.BASIC, NamedAddressInUseStmtCompletionProvider) + extend(CompletionType.BASIC, NamedAddressAtValueExprCompletionProvider) extend(CompletionType.BASIC, AddressInModuleDeclCompletionProvider) - extend(CompletionType.BASIC, TypesCompletionProvider) - extend(CompletionType.BASIC, ImportsCompletionProvider) - extend(CompletionType.BASIC, ModulesCompletionProvider) - extend(CompletionType.BASIC, FQModuleCompletionProvider) + +// extend(CompletionType.BASIC, TypesCompletionProvider) +// extend(CompletionType.BASIC, ImportsCompletionProvider) +// extend(CompletionType.BASIC, ModulesCompletionProvider) +// extend(CompletionType.BASIC, FQModuleCompletionProvider) extend(CompletionType.BASIC, StructFieldsCompletionProvider) extend(CompletionType.BASIC, StructPatCompletionProvider) extend(CompletionType.BASIC, SchemaFieldsCompletionProvider) - extend( - CompletionType.BASIC, - MvPsiPatterns.ability(), - AbilitiesCompletionProvider - ) - extend( - CompletionType.BASIC, - MvPsiPatterns.refExpr(), - BoolsCompletionProvider - ) + extend(CompletionType.BASIC, MvPathCompletionProvider2) + + extend(CompletionType.BASIC, MvPsiPatterns.ability(), AbilitiesCompletionProvider) + extend(CompletionType.BASIC, MvPsiPatterns.refExpr(), BoolsCompletionProvider) + extend(CompletionType.BASIC, MacrosCompletionProvider) extend(CompletionType.BASIC, VectorLiteralCompletionProvider) extend(CompletionType.BASIC, MethodOrFieldCompletionProvider) diff --git a/src/main/kotlin/org/move/lang/core/completion/LookupElements.kt b/src/main/kotlin/org/move/lang/core/completion/LookupElements.kt index d9a48df98..4b247524a 100644 --- a/src/main/kotlin/org/move/lang/core/completion/LookupElements.kt +++ b/src/main/kotlin/org/move/lang/core/completion/LookupElements.kt @@ -11,7 +11,7 @@ import com.intellij.psi.util.PsiTreeUtil import org.move.ide.presentation.text import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* -import org.move.lang.core.resolve.ContextScopeInfo +import org.move.lang.core.resolve2.ref.ResolutionContext import org.move.lang.core.types.infer.* import org.move.lang.core.types.ty.Ty import org.move.lang.core.types.ty.TyUnknown @@ -54,103 +54,17 @@ fun MvNamedElement.createLookupElementWithIcon(): LookupElementBuilder { .withLookupString(this.name ?: "") } -@Suppress("UnusedReceiverParameter") -fun MvModule.createSelfLookup(): LookupElement { - return LookupElementBuilder - .create("Self") - .withBoldness(true) -} - -fun MvNamedElement.getLookupElementBuilder( - completionCtx: CompletionContext, - subst: Substitution = emptySubstitution, - structAsType: Boolean = false -): LookupElementBuilder { - val lookupElementBuilder = this.createLookupElementWithIcon() - val msl = completionCtx.isMsl() - return when (this) { - is MvFunction -> { - val signature = FuncSignature.fromFunction(this, msl).substitute(subst) - if (completionCtx.contextElement is MvMethodOrField) { - lookupElementBuilder - .withTailText(signature.paramsText()) - .withTypeText(signature.retTypeText()) - } else { - lookupElementBuilder - .withTailText(this.signatureText) - .withTypeText(this.outerFileName) - } - } - is MvSpecFunction -> lookupElementBuilder - .withTailText(this.parameters.joinToSignature()) - .withTypeText(this.returnType?.type?.text ?: "()") - - is MvModule -> lookupElementBuilder - .withTailText(this.addressRef()?.let { " ${it.text}" } ?: "") - .withTypeText(this.containingFile?.name) - - is MvStruct -> { - val tailText = if (structAsType) "" else " { ... }" - lookupElementBuilder - .withTailText(tailText) - .withTypeText(this.containingFile?.name) - } - - is MvStructField -> { - val fieldTy = this.type?.loweredType(msl)?.substitute(subst) ?: TyUnknown - lookupElementBuilder - .withTypeText(fieldTy.text(false)) - } - is MvConst -> { -// val msl = this.isMslOnlyItem - val constTy = this.type?.loweredType(msl) ?: TyUnknown - lookupElementBuilder - .withTypeText(constTy.text(true)) - } - - is MvBindingPat -> { -// val msl = this.isMslOnlyItem - val bindingInference = this.inference(msl) - // race condition sometimes happens, when file is too big, inference is not finished yet - val ty = bindingInference?.getPatTypeOrUnknown(this) ?: TyUnknown - lookupElementBuilder - .withTypeText(ty.text(true)) - } - - is MvSchema -> lookupElementBuilder - .withTypeText(this.containingFile?.name) - - // we need to do the resolve here and in the next one to get the underlying item, - // but it should be cached in the most cases - is MvModuleUseSpeck -> { - this.fqModuleRef?.reference?.resolve() - ?.getLookupElementBuilder(completionCtx, subst, structAsType) - ?: lookupElementBuilder - } - - is MvUseItem -> { - this.reference.resolve() - ?.getLookupElementBuilder(completionCtx, subst, structAsType) - ?: lookupElementBuilder - } - - else -> lookupElementBuilder - } -} - data class CompletionContext( val contextElement: MvElement, - val contextScopeInfo: ContextScopeInfo, + val msl: Boolean, val expectedTy: Ty? = null, -) { - fun isMsl(): Boolean = contextScopeInfo.isMslScope -} - + val resolutionCtx: ResolutionContext? = null, + val structAsType: Boolean = false +) fun MvNamedElement.createLookupElement( completionContext: CompletionContext, subst: Substitution = emptySubstitution, - structAsType: Boolean = false, priority: Double = DEFAULT_PRIORITY, insertHandler: InsertHandler = DefaultInsertHandler(completionContext), ): LookupElement { @@ -158,7 +72,7 @@ fun MvNamedElement.createLookupElement( this.getLookupElementBuilder( completionContext, subst = subst, - structAsType = structAsType + structAsType = completionContext.structAsType ) .withInsertHandler(insertHandler) .withPriority(priority) @@ -226,9 +140,11 @@ open class DefaultInsertHandler(val completionCtx: CompletionContext? = null): I item: LookupElement ) { val document = context.document - when (element) { is MvFunctionLike -> { + // no suffix for imports + if (completionCtx?.resolutionCtx?.isUseSpeck == true) return + val isMethodCall = context.getElementOfType() != null val requiresExplicitTypes = element.requiresExplicitlyProvidedTypeArguments(completionCtx) @@ -289,6 +205,83 @@ open class DefaultInsertHandler(val completionCtx: CompletionContext? = null): I } } +private fun MvNamedElement.getLookupElementBuilder( + completionCtx: CompletionContext, + subst: Substitution = emptySubstitution, + structAsType: Boolean = false +): LookupElementBuilder { + val lookupElementBuilder = this.createLookupElementWithIcon() + val msl = completionCtx.msl + return when (this) { + is MvFunction -> { + val signature = FuncSignature.fromFunction(this, msl).substitute(subst) + if (completionCtx.contextElement is MvMethodOrField) { + lookupElementBuilder + .withTailText(signature.paramsText()) + .withTypeText(signature.retTypeText()) + } else { + lookupElementBuilder + .withTailText(this.signatureText) + .withTypeText(this.outerFileName) + } + } + is MvSpecFunction -> lookupElementBuilder + .withTailText(this.parameters.joinToSignature()) + .withTypeText(this.returnType?.type?.text ?: "()") + + is MvModule -> lookupElementBuilder + .withTailText(this.addressRef()?.let { " ${it.text}" } ?: "") + .withTypeText(this.containingFile?.name) + + is MvStruct -> { + val tailText = if (structAsType) "" else " { ... }" + lookupElementBuilder + .withTailText(tailText) + .withTypeText(this.containingFile?.name) + } + + is MvStructField -> { + val fieldTy = this.type?.loweredType(msl)?.substitute(subst) ?: TyUnknown + lookupElementBuilder + .withTypeText(fieldTy.text(false)) + } + is MvConst -> { +// val msl = this.isMslOnlyItem + val constTy = this.type?.loweredType(msl) ?: TyUnknown + lookupElementBuilder + .withTypeText(constTy.text(true)) + } + + is MvBindingPat -> { +// val msl = this.isMslOnlyItem + val bindingInference = this.inference(msl) + // race condition sometimes happens, when file is too big, inference is not finished yet + val ty = bindingInference?.getPatTypeOrUnknown(this) ?: TyUnknown + lookupElementBuilder + .withTypeText(ty.text(true)) + } + + is MvSchema -> lookupElementBuilder + .withTypeText(this.containingFile?.name) + + // we need to do the resolve here and in the next one to get the underlying item, + // but it should be cached in the most cases +// is MvModuleUseSpeck -> { +// this.fqModuleRef?.reference?.resolve() +// ?.getLookupElementBuilder(completionCtx, subst, structAsType) +// ?: lookupElementBuilder +// } + +// is MvUseItem -> { +// this.reference.resolve() +// ?.getLookupElementBuilder(completionCtx, subst, structAsType) +// ?: lookupElementBuilder +// } + + else -> lookupElementBuilder + } +} + // When a user types `(` while completion, // `com.intellij.codeInsight.completion.DefaultCharFilter` invokes completion with selected item. // And if we insert `()` for the item (for example, function), a user get double parentheses diff --git a/src/main/kotlin/org/move/lang/core/completion/LookupElements2.kt b/src/main/kotlin/org/move/lang/core/completion/LookupElements2.kt new file mode 100644 index 000000000..f6a4e9742 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/completion/LookupElements2.kt @@ -0,0 +1,92 @@ +package org.move.lang.core.completion + +import com.intellij.codeInsight.completion.InsertHandler +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import org.move.ide.presentation.text +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.MvMethodOrField +import org.move.lang.core.psi.ext.addressRef +import org.move.lang.core.psi.ext.joinToSignature +import org.move.lang.core.psi.ext.outerFileName +import org.move.lang.core.resolve.ScopeEntry +import org.move.lang.core.types.infer.* +import org.move.lang.core.types.ty.TyUnknown + +fun createLookupElement( + scopeEntry: ScopeEntry, + completionContext: CompletionContext, + subst: Substitution = emptySubstitution, + priority: Double = DEFAULT_PRIORITY, + insertHandler: InsertHandler = DefaultInsertHandler(completionContext) +): LookupElement { + val element = scopeEntry.element + val lookup = element.getLookupElementBuilder(completionContext, scopeEntry.name, subst) + .withInsertHandler(insertHandler) + .withPriority(priority) + val props = getLookupElementProperties(element, subst, completionContext) + return lookup.toMvLookupElement(properties = props) +} + +private fun MvNamedElement.getLookupElementBuilder( + context: CompletionContext, + scopeName: String, + subst: Substitution = emptySubstitution, +): LookupElementBuilder { + val msl = context.msl + val base = LookupElementBuilder.createWithSmartPointer(scopeName, this) + .withIcon(this.getIcon(0)) + return when (this) { + is MvFunction -> { + val signature = FuncSignature.fromFunction(this, msl).substitute(subst) + if (context.contextElement is MvMethodOrField) { + base + .withTailText(signature.paramsText()) + .withTypeText(signature.retTypeText()) + } else { + base + .withTailText(this.signatureText) + .withTypeText(this.outerFileName) + } + } + is MvSpecFunction -> base + .withTailText(this.parameters.joinToSignature()) + .withTypeText(this.returnType?.type?.text ?: "()") + + is MvModule -> base + .withTailText(this.addressRef()?.let { " ${it.text}" } ?: "") + .withTypeText(this.containingFile?.name) + + is MvStruct -> { + val tailText = if (context.structAsType) "" else " { ... }" + base + .withTailText(tailText) + .withTypeText(this.containingFile?.name) + } + + is MvStructField -> { + val fieldTy = this.type?.loweredType(msl)?.substitute(subst) ?: TyUnknown + base + .withTypeText(fieldTy.text(false)) + } + is MvConst -> { + val constTy = this.type?.loweredType(msl) ?: TyUnknown + base + .withTypeText(constTy.text(true)) + } + + is MvBindingPat -> { + val bindingInference = this.inference(msl) + // race condition sometimes happens, when file is too big, inference is not finished yet + val ty = bindingInference?.getPatTypeOrUnknown(this) ?: TyUnknown + base + .withTypeText(ty.text(true)) + } + + is MvSchema -> base + .withTypeText(this.containingFile?.name) + + else -> base + } +} + diff --git a/src/main/kotlin/org/move/lang/core/completion/MvLookupElement.kt b/src/main/kotlin/org/move/lang/core/completion/MvLookupElement.kt index 28188a662..a594b24f9 100644 --- a/src/main/kotlin/org/move/lang/core/completion/MvLookupElement.kt +++ b/src/main/kotlin/org/move/lang/core/completion/MvLookupElement.kt @@ -60,7 +60,7 @@ fun getLookupElementProperties( var props = LookupElementProperties() val expectedTy = context.expectedTy if (expectedTy != null) { - val msl = context.isMsl() + val msl = context.msl val declaredTy = when (element) { is MvFunctionLike -> element.declaredType(msl).retType diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/FQModuleCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/FQModuleCompletionProvider.kt index e3144266f..22d3b4dd2 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/FQModuleCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/FQModuleCompletionProvider.kt @@ -1,57 +1,39 @@ package org.move.lang.core.completion.providers -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.patterns.ElementPattern -import com.intellij.patterns.PlatformPatterns -import com.intellij.psi.PsiElement -import com.intellij.util.ProcessingContext -import org.move.lang.core.completion.CompletionContext -import org.move.lang.core.completion.createLookupElement -import org.move.lang.core.psi.MvFQModuleRef -import org.move.lang.core.psi.refItemScopes -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.letStmtScope -import org.move.lang.core.resolve.processFQModuleRef -import org.move.lang.core.types.Address -import org.move.lang.core.types.address -import org.move.lang.core.withParent -import org.move.lang.moveProject - -object FQModuleCompletionProvider: MvCompletionProvider() { - override val elementPattern: ElementPattern - get() = - PlatformPatterns.psiElement() - .withParent() - - override fun addCompletions( - parameters: CompletionParameters, - context: ProcessingContext, - result: CompletionResultSet, - ) { - val directParent = parameters.position.parent - val fqModuleRef = - directParent as? MvFQModuleRef - ?: directParent.parent as MvFQModuleRef - if (parameters.position !== fqModuleRef.referenceNameElement) return - - val contextScopeInfo = ContextScopeInfo( - letStmtScope = fqModuleRef.letStmtScope, - refItemScopes = fqModuleRef.refItemScopes, - ) - val completionContext = CompletionContext(fqModuleRef, contextScopeInfo) - - val moveProj = fqModuleRef.moveProject - val positionAddress = fqModuleRef.addressRef.address(moveProj) - - processFQModuleRef(fqModuleRef) { - val module = it.element - val moduleAddress = module.address(moveProj) - if (Address.eq(positionAddress, moduleAddress)) { - val lookup = module.createLookupElement(completionContext) - result.addElement(lookup) - } - false - } - } -} +//object FQModuleCompletionProvider: MvCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = +// PlatformPatterns.psiElement() +// .withParent() +// +// override fun addCompletions( +// parameters: CompletionParameters, +// context: ProcessingContext, +// result: CompletionResultSet, +// ) { +// val directParent = parameters.position.parent +// val fqModuleRef = +// directParent as? MvFQModuleRef +// ?: directParent.parent as MvFQModuleRef +// if (parameters.position !== fqModuleRef.referenceNameElement) return +// +// val contextScopeInfo = ContextScopeInfo( +// letStmtScope = fqModuleRef.letStmtScope, +// refItemScopes = fqModuleRef.refItemScopes, +// ) +// val completionContext = CompletionContext(fqModuleRef, contextScopeInfo) +// +// val moveProj = fqModuleRef.moveProject +// val positionAddress = fqModuleRef.addressRef.address(moveProj) +// +// processFQModuleRef(fqModuleRef) { +// val module = it.element +// val moduleAddress = module.address(moveProj) +// if (Address.eq(positionAddress, moduleAddress)) { +// val lookup = module.createLookupElement(completionContext) +// result.addElement(lookup) +// } +// false +// } +// } +//} diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/ImportsCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/ImportsCompletionProvider.kt index a71efc7a9..872455647 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/ImportsCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/ImportsCompletionProvider.kt @@ -1,72 +1,48 @@ package org.move.lang.core.completion.providers -import com.intellij.codeInsight.completion.BasicInsertHandler -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.patterns.ElementPattern -import com.intellij.patterns.PlatformPatterns -import com.intellij.psi.PsiElement -import com.intellij.util.ProcessingContext -import org.move.lang.core.completion.CompletionContext -import org.move.lang.core.completion.createLookupElement -import org.move.lang.core.completion.createSelfLookup -import org.move.lang.core.psi.MvModule -import org.move.lang.core.psi.MvUseItem -import org.move.lang.core.psi.MvUseItemGroup -import org.move.lang.core.psi.ext.isSelfModuleRef -import org.move.lang.core.psi.ext.itemUseSpeck -import org.move.lang.core.psi.ext.names -import org.move.lang.core.psi.refItemScopes -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.letStmtScope -import org.move.lang.core.resolve.processModuleItems -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility -import org.move.lang.core.withParent - -object ImportsCompletionProvider: MvCompletionProvider() { - override val elementPattern: ElementPattern - get() = PlatformPatterns - .psiElement().withParent() - - override fun addCompletions( - parameters: CompletionParameters, - context: ProcessingContext, - result: CompletionResultSet - ) { - val itemImport = parameters.position.parent as MvUseItem - if (parameters.position !== itemImport.referenceNameElement) return - - val moduleRef = itemImport.itemUseSpeck.fqModuleRef - val referredModule = moduleRef.reference?.resolve() as? MvModule - ?: return - - val p = itemImport.parent - if (p is MvUseItemGroup && "Self" !in p.names) { - result.addElement(referredModule.createSelfLookup()) - } - - val vs = when { - moduleRef.isSelfModuleRef -> setOf(Visibility.Internal) - else -> Visibility.visibilityScopesForElement(itemImport) - } - val ns = setOf(Namespace.NAME, Namespace.TYPE, Namespace.FUNCTION) - val contextScopeInfo = - ContextScopeInfo( - letStmtScope = itemImport.letStmtScope, - refItemScopes = itemImport.refItemScopes, - ) - - val completionContext = CompletionContext(itemImport, contextScopeInfo) - processModuleItems(referredModule, ns, vs, contextScopeInfo) { - result.addElement( - it.element.createLookupElement( - completionContext, - insertHandler = BasicInsertHandler(), - structAsType = true - ) - ) - false - } - } -} +//object ImportsCompletionProvider: MvCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = PlatformPatterns +// .psiElement().withParent() +// +// override fun addCompletions( +// parameters: CompletionParameters, +// context: ProcessingContext, +// result: CompletionResultSet +// ) { +// val itemImport = parameters.position.parent as MvUseItem +// if (parameters.position !== itemImport.referenceNameElement) return +// +// val moduleRef = itemImport.itemUseSpeck.fqModuleRef +// val referredModule = moduleRef.reference?.resolve() as? MvModule +// ?: return +// +// val p = itemImport.parent +// if (p is MvUseItemGroup && "Self" !in p.names) { +// result.addElement(referredModule.createSelfLookup()) +// } +// +// val vs = when { +// moduleRef.isSelfModuleRef -> setOf(Visibility.Internal) +// else -> Visibility.visibilityScopesForElement(itemImport) +// } +// val ns = setOf(Namespace.NAME, Namespace.TYPE, Namespace.FUNCTION) +// val contextScopeInfo = +// ContextScopeInfo( +// letStmtScope = itemImport.letStmtScope, +// refItemScopes = itemImport.refItemScopes, +// ) +// +// val completionContext = CompletionContext(itemImport, contextScopeInfo) +// processModuleItems(referredModule, ns, vs, contextScopeInfo) { +// result.addElement( +// it.element.createLookupElement( +// completionContext, +// insertHandler = BasicInsertHandler(), +// structAsType = true +// ) +// ) +// false +// } +// } +//} diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/MethodOrFieldCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/MethodOrFieldCompletionProvider.kt index de732feb9..de5c37753 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/MethodOrFieldCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/MethodOrFieldCompletionProvider.kt @@ -9,11 +9,14 @@ import com.intellij.util.ProcessingContext import org.jetbrains.annotations.VisibleForTesting import org.move.lang.core.completion.CompletionContext import org.move.lang.core.completion.createLookupElement -import org.move.lang.core.psi.ext.* -import org.move.lang.core.psi.refItemScopes +import org.move.lang.core.psi.MvFunction +import org.move.lang.core.psi.ext.MvMethodOrField +import org.move.lang.core.psi.ext.getFieldVariants +import org.move.lang.core.psi.ext.inferReceiverTy +import org.move.lang.core.psi.ext.isMsl import org.move.lang.core.psi.tyInfers -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.letStmtScope +import org.move.lang.core.resolve.createProcessor +import org.move.lang.core.resolve2.processMethodResolveVariants import org.move.lang.core.types.infer.InferenceContext import org.move.lang.core.types.infer.substitute import org.move.lang.core.types.ty.TyFunction @@ -44,13 +47,14 @@ object MethodOrFieldCompletionProvider: MvCompletionProvider() { fun addMethodOrFieldVariants(element: MvMethodOrField, result: CompletionResultSet) { val msl = element.isMsl() val receiverTy = element.inferReceiverTy(msl).knownOrNull() ?: return - val scopeInfo = ContextScopeInfo( - letStmtScope = element.letStmtScope, - refItemScopes = element.refItemScopes, - ) +// val scopeInfo = ContextScopeInfo( +// letStmtScope = element.letStmtScope, +// refItemScopes = element.refItemScopes, +// ) val expectedTy = getExpectedTypeForEnclosingPathOrDotExpr(element, msl) - val ctx = CompletionContext(element, scopeInfo, expectedTy) + val ctx = CompletionContext(element, msl, expectedTy) +// val ctx = CompletionContext(element, scopeInfo, msl, expectedTy) val structTy = receiverTy.derefIfNeeded() as? TyStruct if (structTy != null) { @@ -63,23 +67,26 @@ object MethodOrFieldCompletionProvider: MvCompletionProvider() { result.addElement(lookupElement) } } - getMethodVariants(element, receiverTy, msl) - .forEach { (_, function) -> - val subst = function.tyInfers - val declaredFuncTy = function.declaredType(msl).substitute(subst) as TyFunction - val declaredSelfTy = declaredFuncTy.paramTypes.first() - val autoborrowedReceiverTy = - TyReference.autoborrow(receiverTy, declaredSelfTy) - ?: error("unreachable, references always compatible") - val inferenceCtx = InferenceContext(msl) - inferenceCtx.combineTypes(declaredSelfTy, autoborrowedReceiverTy) + processMethodResolveVariants(element, receiverTy, ctx.msl, createProcessor { e -> + val function = e.element as? MvFunction ?: return@createProcessor + val subst = function.tyInfers + val declaredFuncTy = function.declaredType(msl).substitute(subst) as TyFunction + val declaredSelfTy = declaredFuncTy.paramTypes.first() + val autoborrowedReceiverTy = + TyReference.autoborrow(receiverTy, declaredSelfTy) + ?: error("unreachable, references always compatible") - val lookupElement = function.createLookupElement( + val inferenceCtx = InferenceContext(msl) + inferenceCtx.combineTypes(declaredSelfTy, autoborrowedReceiverTy) + + result.addElement( + createLookupElement( + e, ctx, subst = inferenceCtx.resolveTypeVarsIfPossible(subst) ) - result.addElement(lookupElement) - } + ) + }) } } \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/ModulesCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/ModulesCompletionProvider.kt index 991cbcd23..24599d4dc 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/ModulesCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/ModulesCompletionProvider.kt @@ -1,87 +1,65 @@ package org.move.lang.core.completion.providers -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.patterns.ElementPattern -import com.intellij.psi.PsiElement -import com.intellij.util.ProcessingContext -import org.move.ide.inspections.imports.ImportContext -import org.move.lang.core.MvPsiPatterns -import org.move.lang.core.completion.CompletionContext -import org.move.lang.core.completion.IMPORTED_MODULE_PRIORITY -import org.move.lang.core.completion.UNIMPORTED_ITEM_PRIORITY -import org.move.lang.core.completion.createLookupElement -import org.move.lang.core.psi.MvPath -import org.move.lang.core.psi.containingModule -import org.move.lang.core.psi.containingModuleSpec -import org.move.lang.core.psi.ext.equalsTo -import org.move.lang.core.psi.refItemScopes -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.letStmtScope -import org.move.lang.core.resolve.processItems -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility - -object ModulesCompletionProvider: MvCompletionProvider() { - override val elementPattern: ElementPattern - get() = - MvPsiPatterns.path() - - override fun addCompletions( - parameters: CompletionParameters, - context: ProcessingContext, - result: CompletionResultSet, - ) { - val maybePath = parameters.position.parent - val refElement = - maybePath as? MvPath ?: maybePath.parent as MvPath - - if (parameters.position !== refElement.referenceNameElement) return - if (refElement.moduleRef != null) return - - val processedNames = mutableSetOf() - val namespaces = setOf(Namespace.MODULE) - val contextScopeInfo = - ContextScopeInfo( - letStmtScope = refElement.letStmtScope, - refItemScopes = refElement.refItemScopes, - ) - val completionCtx = CompletionContext(refElement, contextScopeInfo) - processItems(refElement, namespaces, contextScopeInfo) { (name, element) -> - result.addElement( - element.createLookupElement(completionCtx, priority = IMPORTED_MODULE_PRIORITY) - ) - processedNames.add(name) - false - } - - // disable auto-import in module specs for now - if (refElement.containingModuleSpec != null) return - - val path = parameters.originalPosition?.parent as? MvPath ?: return - val importContext = - ImportContext.from( - path, - namespaces, - setOf(Visibility.Public), - contextScopeInfo - ) - val containingMod = path.containingModule - val candidates = getImportCandidates(parameters, result, processedNames, importContext, - itemFilter = { - containingMod != null && !it.equalsTo( - containingMod - ) - }) - candidates.forEach { candidate -> - val lookupElement = - candidate.element.createLookupElement( - completionCtx, - structAsType = Namespace.TYPE in importContext.namespaces, - priority = UNIMPORTED_ITEM_PRIORITY, - insertHandler = ImportInsertHandler(parameters, candidate) - ) - result.addElement(lookupElement) - } - } -} +//object ModulesCompletionProvider2: MvCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = +// MvPsiPatterns.path() +// +// override fun addCompletions( +// parameters: CompletionParameters, +// context: ProcessingContext, +// result: CompletionResultSet, +// ) { +// val maybePath = parameters.position.parent +// val refElement = +// maybePath as? MvPath ?: maybePath.parent as MvPath +// +// if (parameters.position !== refElement.referenceNameElement) return +// if (refElement.moduleRef != null) return +// +// val processedNames = mutableSetOf() +// val namespaces = setOf(Namespace.MODULE) +// val contextScopeInfo = +// ContextScopeInfo( +// letStmtScope = refElement.letStmtScope, +// refItemScopes = refElement.refItemScopes, +// ) +// val completionCtx = CompletionContext(refElement, contextScopeInfo) +// processItems(refElement, namespaces, contextScopeInfo) { (name, element) -> +// result.addElement( +// element.createLookupElement(completionCtx, priority = IMPORTED_MODULE_PRIORITY) +// ) +// processedNames.add(name) +// false +// } +// +// // disable auto-import in module specs for now +// if (refElement.containingModuleSpec != null) return +// +// val path = parameters.originalPosition?.parent as? MvPath ?: return +// val importContext = +// ImportContext.from( +// path, +// namespaces, +// setOf(Visibility.Public), +// contextScopeInfo +// ) +// val containingMod = path.containingModule +// val candidates = getCompletionCandidates(parameters, result.prefixMatcher, processedNames, importContext, +// itemFilter = { +// containingMod != null && !it.equalsTo( +// containingMod +// ) +// }) +// candidates.forEach { candidate -> +// val lookupElement = +// candidate.element.createLookupElement( +// completionCtx, +// structAsType = Namespace.TYPE in importContext.ns, +// priority = UNIMPORTED_ITEM_PRIORITY, +// insertHandler = ImportInsertHandler(parameters, candidate) +// ) +// result.addElement(lookupElement) +// } +// } +//} diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/MvCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/MvCompletionProvider.kt index e012e77c4..a4d94bf6d 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/MvCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/MvCompletionProvider.kt @@ -2,51 +2,24 @@ package org.move.lang.core.completion.providers import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.CompletionProvider -import com.intellij.codeInsight.completion.CompletionResultSet import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.lookup.LookupElement import com.intellij.patterns.ElementPattern import com.intellij.psi.PsiElement -import org.move.ide.inspections.imports.ImportContext import org.move.ide.utils.imports.ImportCandidate -import org.move.ide.utils.imports.ImportCandidateCollector import org.move.ide.utils.imports.import import org.move.lang.core.completion.DefaultInsertHandler import org.move.lang.core.completion.getElementOfType import org.move.lang.core.psi.MvElement -import org.move.lang.index.MvNamedElementIndex -abstract class MvCompletionProvider : CompletionProvider() { +abstract class MvCompletionProvider: CompletionProvider() { abstract val elementPattern: ElementPattern - - protected fun getImportCandidates( - parameters: CompletionParameters, - result: CompletionResultSet, - processedPathNames: Set, - importContext: ImportContext, - itemFilter: (PsiElement) -> Boolean = { true } - ): List { - val project = parameters.position.project - val keys = hashSetOf().apply { - val names = MvNamedElementIndex.getAllKeys(project) - addAll(names) - removeAll(processedPathNames) - } - - return result.prefixMatcher.sortMatching(keys) - .flatMap { - ImportCandidateCollector - .getImportCandidates(importContext, it) - .distinctBy { it.element } - .filter { itemFilter(it.element) } - } - } } class ImportInsertHandler( val parameters: CompletionParameters, private val candidate: ImportCandidate -) : DefaultInsertHandler() { +): DefaultInsertHandler() { override fun handleInsert(element: MvElement, context: InsertionContext, item: LookupElement) { super.handleInsert(element, context, item) diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider.kt index 35dd00cb6..0dc7f4228 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider.kt @@ -1,207 +1,213 @@ package org.move.lang.core.completion.providers -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.patterns.ElementPattern -import com.intellij.patterns.StandardPatterns -import com.intellij.psi.PsiElement -import com.intellij.util.ProcessingContext -import org.move.ide.inspections.imports.ImportContext -import org.move.lang.core.MvPsiPatterns -import org.move.lang.core.completion.CompletionContext -import org.move.lang.core.completion.UNIMPORTED_ITEM_PRIORITY -import org.move.lang.core.completion.createLookupElement -import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.ancestors -import org.move.lang.core.psi.ext.endOffset -import org.move.lang.core.psi.ext.isMslScope -import org.move.lang.core.psi.ext.isSelfModuleRef -import org.move.lang.core.resolve.* -import org.move.lang.core.resolve.ref.MvReferenceElement -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility -import org.move.lang.core.types.infer.inferExpectedTy -import org.move.lang.core.types.infer.inference -import org.move.lang.core.types.ty.Ty -import org.move.lang.core.types.ty.TyUnknown - -abstract class MvPathCompletionProvider: MvCompletionProvider() { - - abstract val namespace: Namespace - - open fun pathScopeInfo(pathElement: MvPath): ContextScopeInfo = - ContextScopeInfo( - letStmtScope = pathElement.letStmtScope, - refItemScopes = pathElement.refItemScopes, - ) - - final override fun addCompletions( - parameters: CompletionParameters, - context: ProcessingContext, - result: CompletionResultSet - ) { - val maybePath = parameters.position.parent - val pathElement = maybePath as? MvPath ?: maybePath.parent as MvPath - - if (parameters.position !== pathElement.referenceNameElement) return - - val moduleRef = pathElement.moduleRef - val namespaces = setOf(this.namespace) - val pathScopeInfo = pathScopeInfo(pathElement) - val msl = pathElement.isMslScope - val expectedTy = getExpectedTypeForEnclosingPathOrDotExpr(pathElement, msl) - - val structAsType = this.namespace == Namespace.TYPE - val ctx = CompletionContext( - pathElement, - pathScopeInfo, - expectedTy - ) - - if (moduleRef != null) { - val module = moduleRef.reference?.resolveWithAliases() as? MvModule - ?: return - val vs = when { - moduleRef.isSelfModuleRef -> setOf(Visibility.Internal) - else -> Visibility.visibilityScopesForElement(pathElement) - } - processModuleItems(module, namespaces, vs, pathScopeInfo) { - val lookup = - it.element.createLookupElement(ctx, structAsType = structAsType) - result.addElement(lookup) - false - } - return - } - - val processedNames = mutableSetOf() - processItems(pathElement, namespaces, pathScopeInfo) { (name, element) -> - if (processedNames.contains(name)) { - return@processItems false - } - processedNames.add(name) - result.addElement( - element.createLookupElement( - ctx, - structAsType = structAsType, - priority = element.completionPriority - ) - ) - false - } - - // disable auto-import in module specs for now - if (pathElement.containingModuleSpec != null) return - - val originalPathElement = parameters.originalPosition?.parent as? MvPath ?: return - val importContext = - ImportContext.from( - originalPathElement, - namespaces, - setOf(Visibility.Public), - pathScopeInfo - ) - val candidates = getImportCandidates( - parameters, - result, - processedNames, - importContext, - ) - candidates.forEach { candidate -> - val lookupElement = candidate.element.createLookupElement( - ctx, - structAsType = structAsType, - priority = UNIMPORTED_ITEM_PRIORITY, - insertHandler = ImportInsertHandler(parameters, candidate) - ) - result.addElement(lookupElement) - } - } -} - -object NamesCompletionProvider: MvPathCompletionProvider() { - override val elementPattern: ElementPattern - get() = - MvPsiPatterns.path() - .andNot(MvPsiPatterns.pathType()) - .andNot(MvPsiPatterns.schemaLit()) - - override val namespace: Namespace get() = Namespace.NAME - -// override fun itemVis(pathElement: MvPath): ItemVis { -// return ItemVis( -// setOf(Namespace.NAME), -// Visibility.none(), -// mslLetScope = pathElement.mslLetScope, -// itemScopes = pathElement.itemScopes, +//fun interface CompletionFilter { +// fun removeEntry(entry: ScopeEntry, ctx: PathResolutionContext): Boolean +//} + +//abstract class MvPathCompletionProvider: MvCompletionProvider() { +// +// abstract val namespaces: Set +// +// open val completionFilters: List = emptyList() +// +// open fun pathScopeInfo(pathElement: MvPath): ContextScopeInfo = +// ContextScopeInfo( +// letStmtScope = pathElement.letStmtScope, +// refItemScopes = pathElement.refItemScopes, // ) -// } -} - -object FunctionsCompletionProvider: MvPathCompletionProvider() { - override val elementPattern: ElementPattern - get() = - MvPsiPatterns.path() - .andNot(MvPsiPatterns.pathType()) - .andNot(MvPsiPatterns.schemaLit()) - - override val namespace: Namespace get() = Namespace.FUNCTION - -// override fun itemVis(pathElement: MvPath): ItemVis { -// return ItemVis( -// setOf(Namespace.FUNCTION), -// Visibility.none(), -// mslLetScope = pathElement.mslLetScope, -// itemScopes = pathElement.itemScopes, +// +// final override fun addCompletions( +// parameters: CompletionParameters, +// context: ProcessingContext, +// result: CompletionResultSet +// ) { +// val maybePath = parameters.position.parent +// val pathElement = maybePath as? MvPath ?: maybePath.parent as MvPath +// +// if (parameters.position !== pathElement.referenceNameElement) return +// +// val qualifier = pathElement.qualifier +// +// val contextScopeInfo = pathScopeInfo(pathElement) +// val msl = pathElement.isMslScope +// val expectedTy = getExpectedTypeForEnclosingPathOrDotExpr(pathElement, msl) +// val structAsType = Namespace.TYPE in this.namespaces +// +// val completionContext = CompletionContext( +// pathElement, +// contextScopeInfo, +// expectedTy, +// ) +// +// var completionCollector = createProcessor { e -> +// val element = e.element as? MvNamedElement ?: return@createProcessor +// val lookup = +// element.createLookupElement( +// completionContext, +// structAsType = structAsType, +// priority = element.completionPriority +// ) +// result.addElement(lookup) +// } +// +// if (qualifier != null) { +// val resolvedQualifier = qualifier.reference?.resolveFollowingAliases() +// when (resolvedQualifier) { +// is MvModule -> { +// val moduleBlock = resolvedQualifier.moduleBlock +// if (moduleBlock != null) { +// processItemDeclarations(moduleBlock, this.namespaces, completionCollector) +// } +// } +// } +// return +// } +// +//// if (moduleRef != null) { +//// val module = moduleRef.reference?.resolveWithAliases() as? MvModule +//// ?: return +//// val vs = when { +//// moduleRef.isSelfModuleRef -> setOf(Visibility.Internal) +//// else -> Visibility.visibilityScopesForElement(pathElement) +//// } +//// processModuleItems(module, namespaces, vs, contextScopeInfo) { +//// val lookup = +//// it.element.createLookupElement(ctx, structAsType = structAsType) +//// result.addElement(lookup) +//// false +//// } +//// return +//// } +// +// val processedNames = mutableSetOf() +// completionCollector = completionCollector.wrapWithFilter { e -> +// if (processedNames.contains(e.name)) { +// return@wrapWithFilter false +// } +// processedNames.add(e.name) +// true +// } +// +// val resolutionCtx = PathResolutionContext(pathElement, contextScopeInfo) +// +// // custom filters +// completionCollector = completionCollector.wrapWithFilter { +// for (completionFilter in this.completionFilters) { +// if (!completionFilter.removeEntry(it, resolutionCtx)) return@wrapWithFilter false +// } +// true +// } +// +// val ctx = PathResolutionContext(pathElement, contextScopeInfo) +// processNestedScopesUpwards(pathElement, this.namespaces, ctx, completionCollector) +// +//// processItems(pathElement, namespaces, contextScopeInfo) { (name, element) -> +//// if (processedNames.contains(name)) { +//// return@processItems false +//// } +//// processedNames.add(name) +//// result.addElement( +//// element.createLookupElement( +//// completionContext, +//// structAsType = structAsType, +//// priority = element.completionPriority +//// ) +//// ) +//// false +//// } +// +// // disable auto-import in module specs for now +// if (pathElement.containingModuleSpec != null) return +// +// val originalPathElement = parameters.originalPosition?.parent as? MvPath ?: return +// val importContext = +// ImportContext.from( +// originalPathElement, +// this.namespaces, +// setOf(Visibility.Public), +// contextScopeInfo +// ) +// val candidates = getCompletionCandidates( +// parameters, +// result.prefixMatcher, +// processedNames, +// importContext, // ) +// candidates.forEach { candidate -> +// val entry = SimpleScopeEntry(candidate.qualName.itemName, candidate.element, namespaces) +// for (completionFilter in completionFilters) { +// if (!completionFilter.removeEntry(entry, resolutionCtx)) return@forEach +// } +// val lookupElement = candidate.element.createLookupElement( +// completionContext, +// structAsType = structAsType, +// priority = UNIMPORTED_ITEM_PRIORITY, +// insertHandler = ImportInsertHandler(parameters, candidate) +// ) +// result.addElement(lookupElement) +// } // } -} - -object TypesCompletionProvider: MvPathCompletionProvider() { - override val elementPattern: ElementPattern - get() = MvPsiPatterns.pathType() - - override val namespace: Namespace get() = Namespace.TYPE - -// override fun itemVis(pathElement: MvPath): ItemVis { -// return ItemVis( -// setOf(Namespace.TYPE), -// Visibility.none(), -// mslLetScope = pathElement.mslLetScope, -// itemScopes = pathElement.itemScopes, +//} + +//object NamesCompletionProvider: MvPathCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = +// MvPsiPatterns.path() +// .andNot(MvPsiPatterns.pathType()) +// .andNot(MvPsiPatterns.schemaLit()) +// +// override val namespaces: Set get() = EnumSet.of(Namespace.NAME) +//} + +//object FunctionsCompletionProvider: MvPathCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = +// MvPsiPatterns.path() +// .andNot(MvPsiPatterns.pathType()) +// .andNot(MvPsiPatterns.schemaLit()) +// +// override val namespaces: Set get() = EnumSet.of(Namespace.FUNCTION) +//} + +//object TypesCompletionProvider: MvPathCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = MvPsiPatterns.pathType() +// +// override val namespaces: Set get() = EnumSet.of(Namespace.TYPE) +//} + +//object SchemasCompletionProvider: MvPathCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = +// StandardPatterns.or( +// MvPsiPatterns.schemaLit(), MvPsiPatterns.pathInsideIncludeStmt() +// ) +// +// override val namespaces: Set get() = EnumSet.of(Namespace.SCHEMA) +// +// override fun pathScopeInfo(pathElement: MvPath): ContextScopeInfo { +// return ContextScopeInfo( +// letStmtScope = LetStmtScope.EXPR_STMT, +// refItemScopes = pathElement.refItemScopes, // ) // } -} - -object SchemasCompletionProvider: MvPathCompletionProvider() { - override val elementPattern: ElementPattern - get() = - StandardPatterns.or( - MvPsiPatterns.schemaLit(), MvPsiPatterns.pathInsideIncludeStmt() - ) - - override val namespace: Namespace get() = Namespace.SCHEMA - - override fun pathScopeInfo(pathElement: MvPath): ContextScopeInfo { - return ContextScopeInfo( - letStmtScope = LetStmtScope.EXPR_STMT, - refItemScopes = pathElement.refItemScopes, - ) - } -} - -fun getExpectedTypeForEnclosingPathOrDotExpr(element: MvReferenceElement, msl: Boolean): Ty? { - for (ancestor in element.ancestors) { - if (element.endOffset < ancestor.endOffset) continue - if (element.endOffset > ancestor.endOffset) break - when (ancestor) { - is MvPathType, - is MvRefExpr, - is MvDotExpr -> { - val inference = (ancestor as MvElement).inference(msl) ?: return TyUnknown - return inferExpectedTy(ancestor, inference) - } - } - } - return null -} +//} + +//object ModulesCompletionProvider: MvPathCompletionProvider() { +// override val elementPattern: ElementPattern +// get() = +// MvPsiPatterns.path() +// .andNot(MvPsiPatterns.pathType()) +// .andNot(MvPsiPatterns.schemaLit()) +// +// override val namespaces: Set get() = EnumSet.of(Namespace.MODULE) +// +// override val completionFilters: List +// get() = listOf( +// // filter out the current module +// CompletionFilter { e, ctx -> +// if (e.element is MvModule) +// return@CompletionFilter ctx.containingModule?.let { e.element.equalsTo(it) } ?: true +// true +// }) +//} diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider2.kt b/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider2.kt new file mode 100644 index 000000000..5e615c097 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/completion/providers/MvPathCompletionProvider2.kt @@ -0,0 +1,181 @@ +package org.move.lang.core.completion.providers + +import com.intellij.codeInsight.completion.CompletionParameters +import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.patterns.ElementPattern +import com.intellij.psi.PsiElement +import com.intellij.util.ProcessingContext +import org.move.ide.inspections.imports.ImportContext +import org.move.ide.utils.imports.ImportCandidateCollector +import org.move.lang.core.MvPsiPatterns.path +import org.move.lang.core.completion.CompletionContext +import org.move.lang.core.completion.UNIMPORTED_ITEM_PRIORITY +import org.move.lang.core.completion.createLookupElement +import org.move.lang.core.completion.getOriginalOrSelf +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.* +import org.move.lang.core.resolve.* +import org.move.lang.core.resolve.ref.MvReferenceElement +import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.resolve.ref.Namespace.* +import org.move.lang.core.resolve2.PathKind +import org.move.lang.core.resolve2.pathKind +import org.move.lang.core.resolve2.ref.ResolutionContext +import org.move.lang.core.resolve2.ref.processPathResolveVariants +import org.move.lang.core.types.infer.inferExpectedTy +import org.move.lang.core.types.infer.inference +import org.move.lang.core.types.ty.Ty +import org.move.lang.core.types.ty.TyUnknown + +fun interface CompletionFilter { + fun removeEntry(entry: ScopeEntry, ctx: ResolutionContext): Boolean +} + +object MvPathCompletionProvider2: MvCompletionProvider() { + override val elementPattern: ElementPattern get() = path() + + override fun addCompletions( + parameters: CompletionParameters, + context: ProcessingContext, + result: CompletionResultSet + ) { + val maybePath = parameters.position.parent + val pathElement = maybePath as? MvPath ?: maybePath.parent as MvPath + if (parameters.position !== pathElement.referenceNameElement) return + + val parentElement = pathElement.rootPath().parent + val resolutionCtx = ResolutionContext(pathElement, isCompletion = true) + val msl = pathElement.isMslScope + val expectedTy = getExpectedTypeForEnclosingPathOrDotExpr(pathElement, msl) + + val useGroup = resolutionCtx.useSpeck?.parent as? MvUseGroup + val existingNames = useGroup?.names.orEmpty().toSet() + val ns = buildSet { + val pathKind = pathElement.pathKind() + if (resolutionCtx.isUseSpeck) { + when (pathKind) { + is PathKind.QualifiedPath.Module -> add(MODULE) + is PathKind.QualifiedPath -> addAll(Namespace.items()) + else -> {} + } + } else { + if (pathKind is PathKind.UnqualifiedPath) { + add(MODULE) + } + when (parentElement) { + is MvPathType -> add(TYPE) + is MvSchemaLit -> add(SCHEMA) + else -> { + add(NAME) + add(FUNCTION) + } + } + } + } + val structAsType = TYPE in ns + + val completionContext = CompletionContext( + pathElement, + msl, + expectedTy, + resolutionCtx = resolutionCtx, + structAsType, + ) + addVariants( + pathElement, parameters, completionContext, ns, result, + CompletionFilter { e, ctx -> + // skip existing items, only non-empty for use groups + if (e.name in existingNames) return@CompletionFilter true + + // drop Self completion for non-UseGroup items + if (e.name == "Self" && useGroup == null) return@CompletionFilter true + + // filter out current module, skips processing (return true) + val element = e.element.getOriginalOrSelf() + if (element is MvModule) { + val containingModule = ctx.containingModule?.getOriginalOrSelf() + if (containingModule != null) { + return@CompletionFilter containingModule.equalsTo(element) + } + } + false + } + ) + } + + fun addVariants( + pathElement: MvPath, + parameters: CompletionParameters, + completionContext: CompletionContext, + ns: Set, + result: CompletionResultSet, + completionFilter: CompletionFilter? = null + ) { + val resolutionCtx = completionContext.resolutionCtx ?: error("always non-null in path completion") + val processedNames = mutableSetOf() + collectCompletionVariants(result, completionContext) { + val processor = it + .wrapWithFilter { e -> + // custom completion filters + if (completionFilter != null) { + if (completionFilter.removeEntry(e, resolutionCtx)) return@wrapWithFilter false + } + + // drop already visited items + if (processedNames.contains(e.name)) return@wrapWithFilter false + processedNames.add(e.name) + + // drop invisible items + if (!e.isVisibleFrom(pathElement)) return@wrapWithFilter false + + true + } + val pathKind = pathElement.pathKind(overwriteNs = ns) + processPathResolveVariants(resolutionCtx, pathKind, processor) + } + + // disable auto-import in module specs for now + if (pathElement.containingModuleSpec != null) return + + // no out-of-scope completions for use specks + if (pathElement.isUseSpeck) return + + val originalPathElement = parameters.originalPosition?.parent as? MvPath ?: return + val importContext = ImportContext.from(originalPathElement, ns) + val candidates = + ImportCandidateCollector.getCompletionCandidates( + parameters, + result.prefixMatcher, + processedNames, + importContext, + ) + candidates.forEach { candidate -> + val entry = SimpleScopeEntry(candidate.qualName.itemName, candidate.element, ns) + if (completionFilter != null) { + if (completionFilter.removeEntry(entry, resolutionCtx)) return@forEach + } + val lookupElement = candidate.element.createLookupElement( + completionContext, + priority = UNIMPORTED_ITEM_PRIORITY, + insertHandler = ImportInsertHandler(parameters, candidate) + ) + result.addElement(lookupElement) + } + } +} + +fun getExpectedTypeForEnclosingPathOrDotExpr(element: MvReferenceElement, msl: Boolean): Ty? { + for (ancestor in element.ancestors) { + if (element.endOffset < ancestor.endOffset) continue + if (element.endOffset > ancestor.endOffset) break + when (ancestor) { + is MvPathType, + is MvRefExpr, + is MvDotExpr -> { + val inference = (ancestor as MvElement).inference(msl) ?: return TyUnknown + return inferExpectedTy(ancestor, inference) + } + } + } + return null +} diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/AddressesCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/NamedAddressInUseStmtCompletionProvider.kt similarity index 62% rename from src/main/kotlin/org/move/lang/core/completion/providers/AddressesCompletionProvider.kt rename to src/main/kotlin/org/move/lang/core/completion/providers/NamedAddressInUseStmtCompletionProvider.kt index ed5e72166..a2339234c 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/AddressesCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/NamedAddressInUseStmtCompletionProvider.kt @@ -11,14 +11,16 @@ import com.intellij.psi.PsiElement import com.intellij.util.ProcessingContext import org.move.cli.AddressVal import org.move.ide.MoveIcons +import org.move.lang.core.MvPsiPatterns import org.move.lang.core.completion.alreadyHasColonColon import org.move.lang.core.psi.MvModule import org.move.lang.core.psi.MvNamedAddress +import org.move.lang.core.psi.MvUseStmt import org.move.lang.core.psiElement import org.move.lang.core.withParent import org.move.lang.moveProject -object AddressInModuleDeclCompletionProvider : MvCompletionProvider() { +object AddressInModuleDeclCompletionProvider: MvCompletionProvider() { override val elementPattern: ElementPattern get() = PlatformPatterns .psiElement() @@ -30,8 +32,7 @@ object AddressInModuleDeclCompletionProvider : MvCompletionProvider() { context: ProcessingContext, result: CompletionResultSet ) { - val element = parameters.position - val moveProject = element.moveProject ?: return + val moveProject = parameters.position.moveProject ?: return val addresses = moveProject.addressValues() for ((name, value) in addresses.entries.sortedBy { it.key }) { val lookup = LookupElementBuilder @@ -49,7 +50,7 @@ object AddressInModuleDeclCompletionProvider : MvCompletionProvider() { } } -object AddressesCompletionProvider : MvCompletionProvider() { +object NamedAddressAtValueExprCompletionProvider: MvCompletionProvider() { override val elementPattern: ElementPattern get() = PlatformPatterns .psiElement().withParent() @@ -58,6 +59,37 @@ object AddressesCompletionProvider : MvCompletionProvider() { .withSuperParent(3, psiElement()) ) + override fun addCompletions( + parameters: CompletionParameters, + context: ProcessingContext, + result: CompletionResultSet + ) { + val moveProject = parameters.position.moveProject ?: return + val declaredNamedAddresses = moveProject.addresses().values + for ((name, addressVal) in declaredNamedAddresses.entries.sortedBy { it.key }) { + val lookup = addressVal.createCompletionLookupElement(name) + result.addElement(lookup) + } + } +} + +object NamedAddressInUseStmtCompletionProvider: MvCompletionProvider() { + override val elementPattern: ElementPattern + get() = MvPsiPatterns.path() + .and( + PlatformPatterns.psiElement() + // use path::ident:: + // ^ should not exist + .andNot( + PlatformPatterns.psiElement().afterLeaf("::") + ) + // use [ident::] + // ^ path (1) + // ^ use speck (2) + // ^ use stmt (3) + .withSuperParent(3, psiElement()) + ) + override fun addCompletions( parameters: CompletionParameters, context: ProcessingContext, @@ -65,8 +97,8 @@ object AddressesCompletionProvider : MvCompletionProvider() { ) { val element = parameters.position val moveProject = element.moveProject ?: return - val addresses = moveProject.addressValues() - for ((name, addressVal) in addresses.entries.sortedBy { it.key }) { + val declaredNamedAddresses = moveProject.addresses().values + for ((name, addressVal) in declaredNamedAddresses.entries.sortedBy { it.key }) { val lookup = addressVal.createCompletionLookupElement(name) result.addElement(lookup) } diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/SchemaFieldsCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/SchemaFieldsCompletionProvider.kt index 0a397bc6f..0712d200b 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/SchemaFieldsCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/SchemaFieldsCompletionProvider.kt @@ -7,20 +7,20 @@ import com.intellij.patterns.PlatformPatterns import com.intellij.psi.PsiElement import com.intellij.util.ProcessingContext import org.move.lang.core.completion.CompletionContext -import org.move.lang.core.completion.createLookupElement +import org.move.lang.core.completion.getOriginalOrSelf import org.move.lang.core.psi.MvSchemaLitField -import org.move.lang.core.psi.ext.fieldBindings -import org.move.lang.core.psi.ext.fieldNames -import org.move.lang.core.psi.ext.schema +import org.move.lang.core.psi.ext.fields +import org.move.lang.core.psi.ext.isMsl +import org.move.lang.core.psi.ext.processSchemaLitFieldResolveVariants import org.move.lang.core.psi.ext.schemaLit -import org.move.lang.core.resolve.ContextScopeInfo +import org.move.lang.core.resolve.collectCompletionVariants +import org.move.lang.core.resolve.wrapWithFilter import org.move.lang.core.withParent object SchemaFieldsCompletionProvider: MvCompletionProvider() { override val elementPattern: ElementPattern - get() = PlatformPatterns - .psiElement() - .withParent() + get() = + PlatformPatterns.psiElement().withParent() override fun addCompletions( parameters: CompletionParameters, @@ -28,16 +28,17 @@ object SchemaFieldsCompletionProvider: MvCompletionProvider() { result: CompletionResultSet ) { val pos = parameters.position - val element = pos.parent as? MvSchemaLitField ?: return - val schemaLit = element.schemaLit ?: return - val schema = schemaLit.schema ?: return - val providedFieldNames = schemaLit.fieldNames + val literalField = pos.parent as? MvSchemaLitField ?: return - val completionCtx = CompletionContext(element, ContextScopeInfo.msl()) - for (fieldBinding in schema.fieldBindings.filter { it.name !in providedFieldNames }) { - result.addElement( - fieldBinding.createLookupElement(completionCtx) - ) + val schemaLit = literalField.schemaLit?.getOriginalOrSelf() ?: return + val existingFieldNames = schemaLit.fields + .filter { !it.textRange.contains(pos.textOffset) } + .map { it.referenceName } + + val completionCtx = CompletionContext(literalField, literalField.isMsl()) + collectCompletionVariants(result, completionCtx) { + val processor = it.wrapWithFilter { e -> e.name !in existingFieldNames } + processSchemaLitFieldResolveVariants(literalField, processor) } } } diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt index 6a5ca8c68..738ecd0de 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt @@ -12,7 +12,6 @@ import org.move.lang.core.completion.CompletionContext import org.move.lang.core.completion.createLookupElement import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* -import org.move.lang.core.resolve.ContextScopeInfo import org.move.lang.core.withParent import org.move.lang.core.withSuperParent @@ -39,7 +38,8 @@ object StructFieldsCompletionProvider: MvCompletionProvider() { if (element is MvBindingPat) element = element.parent as MvElement - val completionCtx = CompletionContext(element, ContextScopeInfo.default()) + val completionCtx = CompletionContext(element, element.isMsl()) +// val completionCtx = CompletionContext(element, ContextScopeInfo.default()) when (element) { is MvStructPatField -> { val structPat = element.structPat diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/StructPatCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/StructPatCompletionProvider.kt index de0aaf57a..6bd099935 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/StructPatCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/StructPatCompletionProvider.kt @@ -7,17 +7,14 @@ import com.intellij.patterns.PlatformPatterns import com.intellij.psi.PsiElement import com.intellij.util.ProcessingContext import org.move.lang.core.completion.CompletionContext -import org.move.lang.core.completion.createLookupElement import org.move.lang.core.psi.MvBindingPat import org.move.lang.core.psi.MvLetStmt import org.move.lang.core.psi.containingModule -import org.move.lang.core.psi.namedItemScopes +import org.move.lang.core.psi.ext.isMsl import org.move.lang.core.psiElement -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.LetStmtScope -import org.move.lang.core.resolve.processModuleItems +import org.move.lang.core.resolve.collectCompletionVariants import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility +import org.move.lang.core.resolve2.processItemDeclarations import org.move.lang.core.withParent object StructPatCompletionProvider: MvCompletionProvider() { @@ -34,22 +31,13 @@ object StructPatCompletionProvider: MvCompletionProvider() { ) { val bindingPat = parameters.position.parent as MvBindingPat val module = bindingPat.containingModule ?: return + val completionCtx = CompletionContext(bindingPat, bindingPat.isMsl()) - val namespaces = setOf(Namespace.TYPE) - val contextScopeInfo = - ContextScopeInfo( - letStmtScope = LetStmtScope.NONE, - refItemScopes = bindingPat.namedItemScopes, - ) - val completionCtx = CompletionContext(bindingPat, contextScopeInfo) - processModuleItems(module, namespaces, setOf(Visibility.Internal), contextScopeInfo) { - val lookup = - it.element.createLookupElement(completionCtx) - result.addElement(lookup) - false - + val moduleBlock = module.moduleBlock + if (moduleBlock != null) { + collectCompletionVariants(result, completionCtx) { + processItemDeclarations(moduleBlock, setOf(Namespace.TYPE), it) + } } } - - } diff --git a/src/main/kotlin/org/move/lang/core/psi/MvElement.kt b/src/main/kotlin/org/move/lang/core/psi/MvElement.kt index 7e7c4e4a8..0c32b2801 100644 --- a/src/main/kotlin/org/move/lang/core/psi/MvElement.kt +++ b/src/main/kotlin/org/move/lang/core/psi/MvElement.kt @@ -37,7 +37,7 @@ val MvElement.containingModule: MvModule? get() = ancestorStrict() val MvElement.containingModuleSpec: MvModuleSpec? get() = ancestorStrict() -val MvElement.containingImportsOwner get() = ancestorOrSelf() +val MvElement.containingItemsOwner get() = ancestorOrSelf() //val MvElement.containingModuleOrScript: MvElement? // get() { diff --git a/src/main/kotlin/org/move/lang/core/psi/MvImportsOwner.kt b/src/main/kotlin/org/move/lang/core/psi/MvImportsOwner.kt deleted file mode 100644 index 0d32486aa..000000000 --- a/src/main/kotlin/org/move/lang/core/psi/MvImportsOwner.kt +++ /dev/null @@ -1,101 +0,0 @@ -package org.move.lang.core.psi - -import com.intellij.psi.util.CachedValuesManager.getProjectPsiDependentCache -import org.move.lang.core.psi.ext.addressRef -import org.move.lang.core.psi.ext.isSelf - -interface MvImportsOwner: MvElement { - val useStmtList: List -} - -fun MvImportsOwner.items(): Sequence { - return generateSequence(firstChild) { it.nextSibling } - .filterIsInstance() - .filter { it !is MvAttr } -} - -fun MvImportsOwner.moduleUseItems(): List = - listOf( - moduleUseSpecksNoAliases(), - moduleUseSpecksAliases(), - selfModuleUseItemNoAliases(), - selfModuleUseItemAliases(), - ).flatten() - -fun MvImportsOwner.moduleUseSpecksNoAliases(): List = - moduleUseSpecks() - .filter { it.useAlias == null } - -fun MvImportsOwner.moduleUseSpecksAliases(): List = - moduleUseSpecks().mapNotNull { it.useAlias } - - -private fun MvImportsOwner.moduleUseSpecks(): List { - return getProjectPsiDependentCache(this) { - useStmtList.mapNotNull { it.moduleUseSpeck } - } -} - -fun MvImportsOwner.psiUseItems(): List { - return getProjectPsiDependentCache(this) { importsOwner -> - importsOwner - .useStmtList - .mapNotNull { it.itemUseSpeck } - .flatMap { - val item = it.useItem - if (item != null) { - listOf(item) - } else - it.useItemGroup?.useItemList.orEmpty() - } - - } -} - -fun MvImportsOwner.allUseItems(): List = - listOf( - useItemsNoAliases(), - useItemsAliases(), - ).flatten() - -fun MvImportsOwner.useItemsNoAliases(): List = - psiUseItems() - .filter { !it.isSelf } - .filter { it.useAlias == null } - -fun MvImportsOwner.useItemsAliases(): List = - psiUseItems() - .filter { !it.isSelf } - .mapNotNull { it.useAlias } - -fun MvImportsOwner.selfModuleUseItemNoAliases(): List = - psiUseItems() - .filter { it.isSelf && it.useAlias == null } - -fun MvImportsOwner.selfModuleUseItemAliases(): List = - psiUseItems() - .filter { it.isSelf } - .mapNotNull { it.useAlias } - -fun MvImportsOwner.shortestPathText(item: MvNamedElement): String? { - val itemName = item.name ?: return null - // local - if (this == item.containingImportsOwner) return itemName - - for (useItem in this.useItemsNoAliases()) { - val importedItem = useItem.reference.resolve() ?: continue - if (importedItem == item) { - return itemName - } - } - val module = item.containingModule ?: return null - val moduleName = module.name ?: return null - for (moduleImport in this.moduleUseSpecksNoAliases()) { - val importedModule = moduleImport.fqModuleRef?.reference?.resolve() ?: continue - if (importedModule == module) { - return "$moduleName::$itemName" - } - } - val addressName = module.addressRef()?.text ?: return null - return "$addressName::$moduleName::$itemName" -} diff --git a/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt b/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt index e3df70df8..acf5b8d59 100644 --- a/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt +++ b/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt @@ -53,11 +53,6 @@ class MvPsiFactory(val project: Project) { createFromText("module $text {}")?.nameIdentifier ?: error("Failed to create identifier: `$text`") -// fun createColon(): PsiElement = -// const("const C: u8 = 1;") -// .descendantOfTypeStrict()!! -// .getChild(MvElementTypes.COLON)!! - fun createComma(): PsiElement = createFromText( """ @@ -81,9 +76,8 @@ class MvPsiFactory(val project: Project) { createFromText("module 0x1::_DummyModule { $text }") ?: error("Failed to create const") - @Suppress("InvalidModuleDeclaration") - fun builtinConst(text: String): MvConst = - createFromText("module spec_builtins { $text }") ?: error("Failed to create const") + fun specBuiltinConst(text: String): MvConst = + createFromText("module 0x0::spec_builtins { $text }") ?: error("Failed to create const") inline fun expr(text: String): T = createFromText("module 0x1::_DummyModule { fun call() { let _ = $text; } }") @@ -104,40 +98,26 @@ class MvPsiFactory(val project: Project) { ?: error("Failed to create an item import from text: `$speckText`") } - fun itemUseSpeck(fqModuleText: String, useItem: String): MvItemUseSpeck { - return createFromText("module 0x1::_DummyModule { use $fqModuleText::$useItem; }") - ?: error("Cannot create item use speck") -// return if (names.size == 1) { -// createFromText("module 0x1::_DummyModule { use $fqModuleText::$useItem; }") -// ?: error("") -// } else { -// val namesText = names.joinToString(", ", "{", "}") -// createFromText("module 0x1::_DummyModule { use $fqModuleText::$namesText; }") -// ?: error("") -// } + fun useSpeckWithEmptyUseGroup(): MvUseSpeck { + return createFromText("module 0x1::_DummyModule { use 0x1::dummy::{}; }") + ?: error("Failed to create a use speck") } - fun itemUseSpeck(fqModuleText: String, names: List): MvItemUseSpeck { - assert(names.isNotEmpty()) - return if (names.size == 1) { - createFromText("module 0x1::_DummyModule { use $fqModuleText::${names.first()}; }") - ?: error("") - } else { - val namesText = names.joinToString(", ", "{", "}") - createFromText("module 0x1::_DummyModule { use $fqModuleText::$namesText; }") - ?: error("") - } + fun useSpeck(text: String): MvUseSpeck { + return createFromText("module 0x1::_DummyModule { use $text; }") + ?: error("Failed to create an item import from text: `$text`") } - fun useItemGroup(items: List): MvUseItemGroup { - val allItemsText = items.joinToString(", ") - return createFromText("module 0x1::_DummyModule { use 0x1::Module::{$allItemsText}; }") - ?: error("Failed to create an item import from text: `$allItemsText`") + fun useSpeckForGroup(text: String): MvUseSpeck { + val useGroup = createFromText("module 0x1::_DummyModule { use 0x1::Module::{$text}; }") + ?: error("Failed to create an item import from text: `$text`") + return useGroup.useSpeckList.first() } - fun useItem(text: String): MvUseItem { - return createFromText("module 0x1::_DummyModule { use 0x1::Module::$text; }") + fun useSpeckForGroupWithDummyAlias(text: String): MvUseSpeck { + val useGroup = createFromText("module 0x1::_DummyModule { use 0x1::Module::{$text as dummy}; }") ?: error("Failed to create an item import from text: `$text`") + return useGroup.useSpeckList.first() } fun acquires(text: String): MvAcquiresType { diff --git a/src/main/kotlin/org/move/lang/core/psi/NamedItemScope.kt b/src/main/kotlin/org/move/lang/core/psi/NamedItemScope.kt index 82f78e395..60b8bef2f 100644 --- a/src/main/kotlin/org/move/lang/core/psi/NamedItemScope.kt +++ b/src/main/kotlin/org/move/lang/core/psi/NamedItemScope.kt @@ -11,6 +11,15 @@ enum class NamedItemScope { TEST, VERIFY; + val isTest get() = this == TEST + + fun shrinkScope(adjustmentScope: NamedItemScope): NamedItemScope { + if (this == MAIN) { + return adjustmentScope + } + return this + } + companion object { fun all(): Set = setOf(MAIN, TEST, VERIFY) } diff --git a/src/main/kotlin/org/move/lang/core/psi/PathExpr.kt b/src/main/kotlin/org/move/lang/core/psi/PathExpr.kt index 6ef30b302..0ed77ecf3 100644 --- a/src/main/kotlin/org/move/lang/core/psi/PathExpr.kt +++ b/src/main/kotlin/org/move/lang/core/psi/PathExpr.kt @@ -1,5 +1,5 @@ package org.move.lang.core.psi interface PathExpr : MvElement { - val path: MvPath + val path: MvPath get() = error("unreachable") } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvAddressRef.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvAddressRef.kt index 281e5cde7..56c28500e 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvAddressRef.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvAddressRef.kt @@ -1,21 +1,5 @@ package org.move.lang.core.psi.ext import org.move.lang.core.psi.MvAddressRef -import org.move.lang.moveProject val MvAddressRef.normalizedText: String get() = this.text.lowercase() - -val MvAddressRef.useGroupLevel: Int - get() { - // sort to the end if not a named address - if (this.namedAddress == null) return 4 - - val name = this.namedAddress?.text.orEmpty().lowercase() - val currentPackageAddresses = - this.moveProject?.currentPackageAddresses()?.keys.orEmpty().map { it.lowercase() } - return when (name) { - "std", "aptos_std", "aptos_framework", "aptos_token" -> 1 - !in currentPackageAddresses -> 2 - else -> 3 - } - } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvAttrItem.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvAttrItem.kt index eb771539c..f76b354a7 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvAttrItem.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvAttrItem.kt @@ -8,6 +8,8 @@ import org.move.lang.core.resolve.ref.MvPolyVariantReferenceCached val MvAttrItem.attr: MvAttr? get() = this.parent as? MvAttr +val MvAttrItem.isAbortCode: Boolean get() = this.identifier.textMatches("abort_code") + class AttrItemReferenceImpl( element: MvAttrItem, val ownerFunction: MvFunction diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvElement.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvElement.kt index 9a7241946..adfaebc40 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvElement.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvElement.kt @@ -31,7 +31,7 @@ val MvNamedElement.isMslOnlyItem: Boolean var element: PsiElement? = this while (element != null) { // use items always non-msl, otherwise import resolution doesn't work correctly - if (element is MvUseItem) return false +// if (element is MvUseItem) return false // module items if (element is MvModule @@ -49,8 +49,6 @@ val MvNamedElement.isMslOnlyItem: Boolean val MvMethodOrPath.isMslScope get() = this.isMslInner() -val MvModuleRef.isMslScope: Boolean get() = this.isMslInner() - fun PsiElement.isMsl(): Boolean = isMslInner() //fun PsiElement.isMslLegacy(): Boolean { // return CachedValuesManager.getProjectPsiDependentCache(this) { @@ -79,7 +77,7 @@ private fun PsiElement.isMslInner(): Boolean { var element: PsiElement? = it while (element != null) { // use items always non-msl, otherwise import resolution doesn't work correctly - if (element is MvUseItem) return@getProjectPsiDependentCache false + if (element is MvUseSpeck) return@getProjectPsiDependentCache false // module items if (element is MvModule diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvFQModuleRef.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvFQModuleRef.kt index 5167ed1e7..291e055b2 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvFQModuleRef.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvFQModuleRef.kt @@ -1,21 +1,13 @@ package org.move.lang.core.psi.ext -import com.intellij.lang.ASTNode -import com.intellij.psi.PsiElement -import org.move.lang.MvElementTypes -import org.move.lang.core.psi.MvElementImpl -import org.move.lang.core.psi.MvFQModuleRef -import org.move.lang.core.resolve.ref.MvFQModuleReference -import org.move.lang.core.resolve.ref.MvFQModuleReferenceImpl - -abstract class MvFQModuleRefMixin(node: ASTNode) : MvElementImpl(node), - MvFQModuleRef { - override val identifier: PsiElement? - get() = findChildByType(MvElementTypes.IDENTIFIER) - - override fun getReference(): MvFQModuleReference { - return MvFQModuleReferenceImpl(this) - } - - override fun getMul(): PsiElement? = null -} +//abstract class MvFQModuleRefMixin(node: ASTNode) : MvElementImpl(node), +// MvFQModuleRef { +// override val identifier: PsiElement? +// get() = findChildByType(MvElementTypes.IDENTIFIER) +// +// override fun getReference(): MvFQModuleReference { +// return MvFQModuleReferenceImpl(this) +// } +// +// override fun getMul(): PsiElement? = null +//} diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt index 73baf52ef..1cdc86d74 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt @@ -50,6 +50,9 @@ val MvFunction.isEntry: Boolean return stub?.isEntry ?: this.isChildExists(MvElementTypes.ENTRY) } +val MvFunction.isPublicScript: Boolean + get() = this.visibilityModifier?.isPublicScript ?: false + val MvFunction.isInline: Boolean get() = this.isChildExists(MvElementTypes.INLINE) val MvFunction.isView: Boolean diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvItemElement.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemElement.kt new file mode 100644 index 000000000..64a4b9329 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemElement.kt @@ -0,0 +1,5 @@ +package org.move.lang.core.psi.ext + +import org.move.lang.core.psi.MvNameIdentifierOwner + +interface MvItemElement: MvVisibilityOwner, MvDocAndAttributeOwner, MvNameIdentifierOwner \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvItemSpecBlock.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemSpecBlock.kt index 964d65e06..6bd6eee04 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvItemSpecBlock.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemSpecBlock.kt @@ -8,15 +8,15 @@ fun MvSpecCodeBlock.builtinSpecConsts(): List { return CachedValuesManager.getProjectPsiDependentCache(this) { val psiFactory = it.project.psiFactory listOf( - psiFactory.builtinConst( + psiFactory.specBuiltinConst( "const MAX_U256: u256 = " + "115792089237316195423570985008687907853269984665640564039457584007913129639935;" ), - psiFactory.builtinConst("const MAX_U128: u128 = 340282366920938463463374607431768211455;"), - psiFactory.builtinConst("const MAX_U64: u64 = 18446744073709551615;"), - psiFactory.builtinConst("const MAX_U32: u32 = 4294967295;"), - psiFactory.builtinConst("const MAX_U16: u16 = 65535;"), - psiFactory.builtinConst("const MAX_U8: u8 = 255;"), + psiFactory.specBuiltinConst("const MAX_U128: u128 = 340282366920938463463374607431768211455;"), + psiFactory.specBuiltinConst("const MAX_U64: u64 = 18446744073709551615;"), + psiFactory.specBuiltinConst("const MAX_U32: u32 = 4294967295;"), + psiFactory.specBuiltinConst("const MAX_U16: u16 = 65535;"), + psiFactory.specBuiltinConst("const MAX_U8: u8 = 255;"), ) } } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvItemUseSpeck.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemUseSpeck.kt deleted file mode 100644 index 87d4a0413..000000000 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvItemUseSpeck.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.move.lang.core.psi.ext - -import org.move.lang.core.psi.MvItemUseSpeck - -fun MvItemUseSpeck.names(): List = - this.useItemGroup?.names ?: listOfNotNull(this.useItem?.name) diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvItemsOwner.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemsOwner.kt new file mode 100644 index 000000000..270d2201b --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvItemsOwner.kt @@ -0,0 +1,35 @@ +package org.move.lang.core.psi.ext + +import org.move.lang.core.psi.* +import org.move.stdext.buildList + +interface MvItemsOwner: MvElement { + val useStmtList: List get() = emptyList() +} + +fun MvItemsOwner.items(): Sequence { + return generateSequence(firstChild) { it.nextSibling } + .filterIsInstance() + .filter { it !is MvAttr } +} + +val MvItemsOwner.itemElements: List + get() { + return this.items().filterIsInstance().toList() + } + +val MvModule.innerSpecItems: List + get() { + val module = this + return buildList { + addAll(module.allModuleSpecs() + .map { + it.moduleItemSpecs() + .flatMap { spec -> spec.itemSpecBlock?.globalVariables().orEmpty() } + } + .flatten()) + addAll(module.specInlineFunctions()) + } + } + +fun MvItemsOwner.allUseItems(): List = emptyList() \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt index 1734b6982..3395740bc 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt @@ -7,12 +7,11 @@ import org.move.lang.core.psi.* import org.move.lang.core.resolve.ScopeItem import org.move.lang.core.resolve.ref.MvPolyVariantReference import org.move.lang.core.resolve.ref.MvPolyVariantReferenceBase -import org.move.lang.core.resolve.ref.Visibility import org.move.lang.core.types.address -import org.move.lang.core.types.infer.foldTyTypeParameterWith import org.move.lang.core.types.infer.inference -import org.move.lang.core.types.ty.* -import org.move.lang.moveProject +import org.move.lang.core.types.ty.Ty +import org.move.lang.core.types.ty.TyStruct +import org.move.lang.core.types.ty.TyVector import org.move.stdext.wrapWithList typealias MatchSequence = Sequence> @@ -41,29 +40,6 @@ fun MvModule.is0x1Address(moveProject: MoveProject): Boolean { return moduleAddress == "0x00000000000000000000000000000001" } -fun getMethodVariants(element: MvMethodOrField, receiverTy: Ty, msl: Boolean): MatchSequence { - val moveProject = element.moveProject ?: return emptySequence() - val receiverTyItemModule = receiverTy.itemModule(moveProject) ?: return emptySequence() - - val elementScopes = Visibility.visibilityScopesForElement(element).toMutableSet() - if (element.containingModule == receiverTyItemModule) { - elementScopes.add(Visibility.Internal) - } - val functions = - elementScopes - .flatMap { elementScope -> receiverTyItemModule.functionsVisibleInScope(elementScope) } - .filter { - val selfTy = it.selfParamTy(msl) ?: return@filter false - // need to use TyVar here, loweredType() erases them - val selfTyWithTyVars = - selfTy.foldTyTypeParameterWith { tp -> TyInfer.TyVar(tp) } - TyReference.isCompatibleWithAutoborrow(receiverTy, selfTyWithTyVars, msl) - } - return functions - .filter { it.name != null } - .map { ScopeItem(it.name!!, it) }.asSequence() -} - class MvMethodCallReferenceImpl( element: MvMethodCall ): diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt index c1a5adb74..6ff04f33a 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt @@ -6,11 +6,9 @@ import com.intellij.navigation.ItemPresentation import com.intellij.openapi.project.Project import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.util.CachedValuesManager.getProjectPsiDependentCache -import org.move.cli.containingMovePackage -import org.move.cli.settings.moveSettings import org.move.ide.MoveIcons +import org.move.lang.core.completion.getOriginalOrSelf import org.move.lang.core.psi.* -import org.move.lang.core.resolve.ref.Visibility import org.move.lang.core.stubs.MvFunctionStub import org.move.lang.core.stubs.MvModuleStub import org.move.lang.core.stubs.MvStructStub @@ -28,26 +26,14 @@ fun MvModule.hasTestFunctions(): Boolean = this.testFunctions().isNotEmpty() fun MvModule.addressRef(): MvAddressRef? = this.addressRef ?: (this.ancestorStrict())?.addressRef -data class FQModule(val address: Address, val name: String) - -fun MvModule.fqModule(): FQModule? { - val moveProj = this.moveProject ?: return null - val address = this.address(moveProj) ?: return null - val name = this.name ?: return null - return FQModule(address, name) -} - -val MvModule.declaredFriendModules: Set +val MvModule.friendModules: Set get() { - val block = this.moduleBlock ?: return emptySet() - val friendModuleRefs = block.friendDeclList.mapNotNull { it.fqModuleRef } - val moveProj = block.moveProject - - val friends = mutableSetOf() - for (moduleRef in friendModuleRefs) { - val address = moduleRef.addressRef.address(moveProj) ?: continue - val identifier = moduleRef.identifier?.text ?: continue - friends.add(FQModule(address, identifier)) + val moduleBlock = this.moduleBlock ?: return emptySet() + val friendModulePaths = moduleBlock.friendDeclList.mapNotNull { it.path } + val friends = mutableSetOf() + for (modulePath in friendModulePaths) { + val module = modulePath.reference?.resolveFollowingAliases() as? MvModule ?: continue + friends.add(module) } return friends } @@ -60,15 +46,19 @@ fun MvModule.allFunctions(): List { fun MvModule.allNonTestFunctions(): List = // allFunctions().filter { f -> !f.isTest } - getProjectPsiDependentCache(this) { - it.allFunctions().filter { f -> !f.hasTestAttr } - } + this.allFunctions().filter { f -> !f.hasTestAttr } +// getProjectPsiDependentCache(this) { +// } fun MvModule.testFunctions(): List = getProjectPsiDependentCache(this) { it.allFunctions().filter { f -> f.hasTestAttr } } +val MvModule.isBuiltins: Boolean get() = this.name == "builtins" && (this.address(null)?.is0x0 ?: false) +val MvModule.isSpecBuiltins: Boolean + get() = this.name == "spec_builtins" && (this.address(null)?.is0x0 ?: false) + fun MvModule.builtinFunctions(): List { return getProjectPsiDependentCache(this) { val text = """ @@ -95,43 +85,6 @@ fun MvModule.builtinFunctions(): List { } } -fun MvModule.functionsVisibleInScope(visibility: Visibility): List { - return when (visibility) { - is Visibility.Public -> - allNonTestFunctions() - .filter { it.visibility == FunctionVisibility.PUBLIC } - is Visibility.PublicScript -> - allNonTestFunctions() - .filter { it.visibility == FunctionVisibility.PUBLIC_SCRIPT } - is Visibility.PublicFriend -> { - val friendFunctions = - allNonTestFunctions().filter { it.visibility == FunctionVisibility.PUBLIC_FRIEND } - if (friendFunctions.isEmpty()) return emptyList() - - val currentModule = visibility.currentModule.element - if (currentModule != null - && currentModule.fqModule() in this.declaredFriendModules - ) { - friendFunctions - } else { - emptyList() - } - } - is Visibility.PublicPackage -> { - if (!project.moveSettings.enablePublicPackage) { - return emptyList() - } - val modulePackage = this.containingMovePackage - if (visibility.originPackage == modulePackage) { - this.allNonTestFunctions() - } else { - emptyList() - } - } - is Visibility.Internal -> allNonTestFunctions() - } -} - fun MvModule.entryFunctions(): List = this.allFunctions().filter { it.isEntry } fun MvModule.viewFunctions(): List = this.allFunctions().filter { it.isView } @@ -196,7 +149,7 @@ val MvModuleBlock.module: MvModule get() = this.parent as MvModule //// this.childrenOfType() //// .filter { it.itemSpecRef?.moduleKw != null } -val MvModuleSpec.moduleItem: MvModule? get() = this.fqModuleRef?.reference?.resolve() as? MvModule +val MvModuleSpec.moduleItem: MvModule? get() = this.path?.reference?.resolve() as? MvModule val MvModuleSpecBlock.moduleSpec: MvModuleSpec get() = this.parent as MvModuleSpec @@ -222,36 +175,40 @@ fun MvModuleSpec.specInlineFunctions(): List = this.moduleItemSpecs().flatMap { it.specInlineFunctions() } fun MvModule.allModuleSpecs(): List { - return getProjectPsiDependentCache(this) { - val moveProject = it.moveProject ?: return@getProjectPsiDependentCache emptyList() - val moduleName = it.name ?: return@getProjectPsiDependentCache emptyList() - val file = it.containingMoveFile ?: return@getProjectPsiDependentCache emptyList() - - val searchScope = moveProject.searchScope() - val moduleSpecs = file.moduleSpecs() + - MvModuleSpecIndex.getElementsByModuleName(it.project, moduleName, searchScope) - if (moduleSpecs.isEmpty()) return@getProjectPsiDependentCache emptyList() - - val currentModule = it.fqModule() ?: return@getProjectPsiDependentCache emptyList() - moduleSpecs - .filter { moduleSpec -> - val module = moduleSpec.fqModuleRef?.reference?.resolve() as? MvModule ?: return@filter false - currentModule == module.fqModule() - } - .toList() - } + val moveProject = this.moveProject ?: return emptyList() + val moduleName = this.name ?: return emptyList() + + val searchScope = moveProject.searchScope() + // all `spec 0x1::m {}` for the current module + val moduleSpecs = MvModuleSpecIndex.getElementsByModuleName(this.project, moduleName, searchScope) + if (moduleSpecs.isEmpty()) return emptyList() + +// val currentModule = this.fqModule() ?: return emptyList() + return moduleSpecs + .filter { moduleSpec -> + val specModule = moduleSpec.moduleItem ?: return@filter false + isModulesEqual(this, specModule) +// currentModule == specModule.fqModule() + } + .toList() +// return getProjectPsiDependentCache(this) { +// } } fun MvModule.allModuleSpecBlocks(): List { return this.allModuleSpecs().mapNotNull { it.moduleSpecBlock } } -abstract class MvModuleMixin : MvStubbedNamedElementImpl, - MvModule { +fun isModulesEqual(left: MvModule, right: MvModule): Boolean { + return left.getOriginalOrSelf() == right.getOriginalOrSelf() +} + +abstract class MvModuleMixin: MvStubbedNamedElementImpl, + MvModule { - constructor(node: ASTNode) : super(node) + constructor(node: ASTNode): super(node) - constructor(stub: MvModuleStub, nodeType: IStubElementType<*, *>) : super(stub, nodeType) + constructor(stub: MvModuleStub, nodeType: IStubElementType<*, *>): super(stub, nodeType) override fun getIcon(flags: Int): Icon = MoveIcons.MODULE diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleRef.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleRef.kt index f4623a808..e9ab788f5 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleRef.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleRef.kt @@ -1,23 +1,15 @@ package org.move.lang.core.psi.ext -import com.intellij.lang.ASTNode -import org.move.lang.core.psi.MvElementImpl -import org.move.lang.core.psi.MvFQModuleRef -import org.move.lang.core.psi.MvModuleRef -import org.move.lang.core.psi.containingModule -import org.move.lang.core.resolve.ref.MvModuleReferenceImpl -import org.move.lang.core.resolve.ref.MvPolyVariantReference +//val MvModuleRef.isSelfModuleRef: Boolean +// get() = +// this !is MvFQModuleRef +// && this.referenceName == "Self" +// && this.containingModule != null -val MvModuleRef.isSelfModuleRef: Boolean - get() = - this !is MvFQModuleRef - && this.referenceName == "Self" - && this.containingModule != null - -abstract class MvModuleRefMixin(node: ASTNode) : MvElementImpl(node), MvModuleRef { - - override fun getReference(): MvPolyVariantReference? = MvModuleReferenceImpl(this) -} +//abstract class MvModuleRefMixin(node: ASTNode) : MvElementImpl(node), MvModuleRef { +// +// override fun getReference(): MvPolyVariantReference? = MvModuleReferenceImpl(this) +//} //abstract class MvImportedModuleRefMixin(node: ASTNode) : MvReferenceElementImpl(node), // MvImportedModuleRef { diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleUseSpeck.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleUseSpeck.kt deleted file mode 100644 index 1eb0f84dc..000000000 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvModuleUseSpeck.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.move.lang.core.psi.ext - -import com.intellij.lang.ASTNode -import com.intellij.psi.PsiElement -import org.move.ide.MoveIcons -import org.move.lang.core.psi.MvModuleUseSpeck -import org.move.lang.core.psi.impl.MvNamedElementImpl -import javax.swing.Icon - -abstract class MvModuleUseSpeckMixin(node: ASTNode) : MvNamedElementImpl(node), - MvModuleUseSpeck { - override val nameElement: PsiElement? - get() = - useAlias?.identifier ?: fqModuleRef?.identifier - - override fun getIcon(flags: Int): Icon = MoveIcons.MODULE - -// override fun getReference(): MvReference { -// return MvModuleReferenceImpl(moduleRef) -// } - -} diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvPath.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvPath.kt index 2ed9700d1..effaa9cdf 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvPath.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvPath.kt @@ -5,11 +5,38 @@ import org.move.cli.settings.debugErrorOrFallback import org.move.ide.annotator.BUILTIN_TYPE_IDENTIFIERS import org.move.ide.annotator.PRIMITIVE_TYPE_IDENTIFIERS import org.move.ide.annotator.SPEC_ONLY_PRIMITIVE_TYPES +import org.move.ide.inspections.imports.BasePathType +import org.move.ide.inspections.imports.basePathType import org.move.lang.core.psi.* -import org.move.lang.core.resolve.ref.MvPathReference -import org.move.lang.core.resolve.ref.MvPathReferenceImpl -import org.move.lang.core.resolve.ref.MvReferenceElement +import org.move.lang.core.resolve.ref.MvPath2Reference import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.resolve2.ref.Path2ReferenceImpl +import java.util.* + +/** For `Foo::bar::baz::quux` path returns `Foo` */ +tailrec fun T.basePath(): T { + @Suppress("UNCHECKED_CAST") + val qualifier = path as T? + return if (qualifier === null) this else qualifier.basePath() +} + +/** For `Foo::bar` in `Foo::bar::baz::quux` returns `Foo::bar::baz::quux` */ +tailrec fun MvPath.rootPath(): MvPath { + // Use `parent` instead of `context` because of better performance. + // Assume nobody set a context for a part of a path + val parent = parent + return if (parent is MvPath) parent.rootPath() else this +} + +val MvPath.length: Int get() { + var length = 1 + var currentPath = this + while (currentPath.path != null) { + currentPath = currentPath.path!! + length += 1 + } + return length +} fun MvPath.isPrimitiveType(): Boolean = this.parent is MvPathType @@ -41,53 +68,101 @@ val MvPath.isUpdateFieldArg2: Boolean val MvPath.identifierName: String? get() = identifier?.text -val MvPath.nullModuleRef: Boolean - get() = - identifier != null && this.moduleRef == null - -val MvPath.isQualPath: Boolean get() = !this.nullModuleRef +//val MvPath.nullModuleRef: Boolean +// get() = +// identifier != null && this.moduleRef == null -val MvPath.maybeStruct get() = reference?.resolveWithAliases() as? MvStruct +val MvPath.maybeStruct get() = reference?.resolveFollowingAliases() as? MvStruct -val MvPath.maybeSchema get() = reference?.resolveWithAliases() as? MvSchema +val MvPath.maybeSchema get() = reference?.resolveFollowingAliases() as? MvSchema -fun MvPath.namespaces(): Set { +fun MvPath.allowedNamespaces(): Set { val parent = this.parent +// return when (parent) { +// is MvPath, +// is MvPathType -> TYPES +// else -> VALUES +// } + return if (parent is MvPath) EnumSet.of(Namespace.MODULE) else rootNamespace(this) +// return when { +// parent is MvSchemaLit || parent is MvSchemaRef -> setOf(Namespace.SCHEMA) +// parent is MvPathType -> setOf(Namespace.TYPE) +// parent is MvCallExpr -> setOf(Namespace.FUNCTION) +// parent is MvRefExpr && parent.isAbortCodeConst() -> setOf(Namespace.CONST) +// parent is MvRefExpr -> setOf(Namespace.NAME) +//// parent is MvRefExpr && this.nullModuleRef -> setOf(Namespace.NAME) +//// parent is MvRefExpr && !this.nullModuleRef -> setOf(Namespace.NAME, Namespace.MODULE) +// // TODO: it's own namespace? +// parent is MvStructLitExpr || parent is MvStructPat -> setOf(Namespace.NAME) +// parent is MvAccessSpecifier -> setOf(Namespace.TYPE) +// parent is MvAddressSpecifierArg -> setOf(Namespace.FUNCTION) +// parent is MvAddressSpecifierCallParam -> setOf(Namespace.NAME) +// else -> debugErrorOrFallback( +// "Cannot build path namespaces: unhandled parent type ${parent.elementType}", +// setOf(Namespace.NAME) +// ) +// } +} + +private fun rootNamespace(rootPath: MvPath): Set { + val parent = rootPath.parent + check(parent !is MvPath) return when { - parent is MvSchemaLit || parent is MvSchemaRef -> setOf(Namespace.SCHEMA) - parent is MvPathType -> setOf(Namespace.TYPE) - parent is MvCallExpr -> setOf(Namespace.FUNCTION) - parent is MvRefExpr && parent.isAbortCodeConst() -> setOf(Namespace.CONST) - parent is MvRefExpr -> setOf(Namespace.NAME) -// parent is MvRefExpr && this.nullModuleRef -> setOf(Namespace.NAME) -// parent is MvRefExpr && !this.nullModuleRef -> setOf(Namespace.NAME, Namespace.MODULE) - // TODO: it's own namespace? - parent is MvStructLitExpr || parent is MvStructPat -> setOf(Namespace.NAME) - parent is MvAccessSpecifier -> setOf(Namespace.TYPE) - parent is MvAddressSpecifierArg -> setOf(Namespace.FUNCTION) - parent is MvAddressSpecifierCallParam -> setOf(Namespace.NAME) + parent is MvSchemaLit || parent is MvSchemaRef -> EnumSet.of(Namespace.SCHEMA) + parent is MvPathType -> EnumSet.of(Namespace.TYPE) + parent is MvCallExpr -> EnumSet.of(Namespace.FUNCTION) +// parent is MvRefExpr && parent.isAbortCodeConst() -> EnumSet.of(Namespace.CONST) + parent is MvRefExpr && rootPath.hasAncestor() -> EnumSet.of(Namespace.NAME, Namespace.MODULE) + parent is MvRefExpr -> EnumSet.of(Namespace.NAME) +// parent is MvRefExpr -> EnumSet.of(Namespace.NAME, Namespace.CONST) + parent is MvStructLitExpr || parent is MvStructPat -> EnumSet.of(Namespace.TYPE) +// parent is MvStructLitExpr || parent is MvStructPat -> EnumSet.of(Namespace.NAME) + parent is MvAccessSpecifier -> EnumSet.of(Namespace.TYPE) + parent is MvAddressSpecifierArg -> EnumSet.of(Namespace.FUNCTION) + parent is MvAddressSpecifierCallParam -> EnumSet.of(Namespace.NAME) + parent is MvFriendDecl -> EnumSet.of(Namespace.MODULE) + parent is MvModuleSpec -> EnumSet.of(Namespace.MODULE) + parent is MvUseSpeck -> Namespace.all() else -> debugErrorOrFallback( "Cannot build path namespaces: unhandled parent type ${parent.elementType}", - setOf(Namespace.NAME) + EnumSet.of(Namespace.NAME) ) } } -abstract class MvPathMixin(node: ASTNode) : MvElementImpl(node), MvPath { +val MvPath.qualifier: MvPath? + get() { + path?.let { return it } + var ctx = context + while (ctx is MvPath) { + ctx = ctx.context + } + // returns the base qualifier, if it's inside the MvUseGroup + return (ctx as? MvUseSpeck)?.qualifier + } + +abstract class MvPathMixin(node: ASTNode): MvElementImpl(node), MvPath { - override fun getReference(): MvPathReference? = MvPathReferenceImpl(this) + override fun getReference(): MvPath2Reference? = Path2ReferenceImpl(this) } -fun MvReferenceElement.importCandidateNamespaces(): Set { +fun MvPath.importCandidateNamespaces(): Set { val parent = this.parent return when (parent) { is MvPathType -> setOf(Namespace.TYPE) is MvSchemaLit, is MvSchemaRef -> setOf(Namespace.SCHEMA) - else -> - when (this) { - is MvModuleRef -> setOf(Namespace.MODULE) - is MvPath -> setOf(Namespace.NAME, Namespace.FUNCTION) - else -> Namespace.all() + else -> { + val baseBaseType = this.basePathType() + when (baseBaseType) { + is BasePathType.Module -> EnumSet.of(Namespace.MODULE) + else -> EnumSet.of(Namespace.NAME, Namespace.FUNCTION) } + } } } + +val MvPath.hasColonColon: Boolean get() = colonColon != null + +val MvPath.useSpeck: MvUseSpeck? get() = this.rootPath().parent as? MvUseSpeck + +val MvPath.isUseSpeck get() = useSpeck != null \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvRefExpr.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvRefExpr.kt index 06a7dba1f..e54cdf537 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvRefExpr.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvRefExpr.kt @@ -8,7 +8,7 @@ import org.move.lang.core.psi.MvRefExpr fun MvRefExpr.isAbortCodeConst(): Boolean { val abortCodeItem = (this.parent.parent as? MvAttrItem) - ?.takeIf { it.identifier.text == "abort_code" } + ?.takeIf { it.isAbortCode } ?: return false val attr = abortCodeItem.ancestorStrict() ?: return false return (attr.owner as? MvFunction)?.hasTestAttr ?: false diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLit.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLit.kt index 80cfbc99c..2c4637c19 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLit.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLit.kt @@ -4,8 +4,6 @@ import org.move.lang.core.psi.MvSchema import org.move.lang.core.psi.MvSchemaLit import org.move.lang.core.psi.MvSchemaLitField -val MvSchemaLit.schema: MvSchema? get() = this.path.reference?.resolveWithAliases() as? MvSchema +val MvSchemaLit.schema: MvSchema? get() = this.path.reference?.resolveFollowingAliases() as? MvSchema val MvSchemaLit.fields: List get() = schemaFieldsBlock?.schemaLitFieldList.orEmpty() - -val MvSchemaLit.fieldNames: List get() = fields.map { it.referenceName } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLitField.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLitField.kt index c543922a0..db6ef1169 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLitField.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvSchemaLitField.kt @@ -3,54 +3,72 @@ package org.move.lang.core.psi.ext import com.intellij.lang.ASTNode import org.move.lang.MvElementTypes import org.move.lang.core.psi.* +import org.move.lang.core.resolve.RsResolveProcessor +import org.move.lang.core.resolve.SimpleScopeEntry +import org.move.lang.core.resolve.collectResolveVariants import org.move.lang.core.resolve.ref.MvPolyVariantReference import org.move.lang.core.resolve.ref.MvPolyVariantReferenceCached import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.resolveLocalItem +import org.move.lang.core.resolve2.resolveBindingForFieldShorthand val MvSchemaLitField.isShorthand get() = !hasChild(MvElementTypes.COLON) val MvSchemaLitField.schemaLit: MvSchemaLit? get() = ancestorStrict(stopAt = MvSpecCodeBlock::class.java) -inline fun MvSchemaLitField.resolveToElement(): T? = +inline fun MvSchemaLitField.resolveToElement(): T? = reference.multiResolve().filterIsInstance().singleOrNull() fun MvSchemaLitField.resolveToDeclaration(): MvSchemaFieldStmt? = resolveToElement() fun MvSchemaLitField.resolveToBinding(): MvBindingPat? = resolveToElement() -class MvSchemaFieldReferenceImpl( - element: MvSchemaLitField -) : MvPolyVariantReferenceCached(element) { - override fun multiResolveInner(): List = resolveIntoSchemaField(element) +//class MvSchemaFieldReferenceImpl( +// element: MvSchemaLitField +//): MvPolyVariantReferenceCached(element) { +// override fun multiResolveInner(): List = collectSchemaLitFieldResolveVariants(element) +//} + +//class MvSchemaFieldShorthandReferenceImpl( +// element: MvSchemaLitField +//): MvPolyVariantReferenceCached(element) { +// override fun multiResolveInner(): List { +// return listOf( +// collectSchemaLitFieldResolveVariants(element), +// resolveLocalItem(element, setOf(Namespace.NAME)) +// ).flatten() +// } +//} + +abstract class MvSchemaLitFieldMixin(node: ASTNode): MvElementImpl(node), + MvSchemaLitField { + override fun getReference(): MvPolyVariantReference = + MvSchemaLitFieldReferenceImpl(this, shorthand = this.isShorthand) } -class MvSchemaFieldShorthandReferenceImpl( - element: MvSchemaLitField -) : MvPolyVariantReferenceCached(element) { +class MvSchemaLitFieldReferenceImpl( + element: MvSchemaLitField, + val shorthand: Boolean, +): MvPolyVariantReferenceCached(element) { override fun multiResolveInner(): List { - return listOf( - resolveIntoSchemaField(element), - resolveLocalItem(element, setOf(Namespace.NAME)) - ).flatten() + var variants = collectResolveVariants(element.referenceName) { + processSchemaLitFieldResolveVariants(element, it) + } + if (shorthand) { + variants += resolveBindingForFieldShorthand(element) +// variants += resolveLocalItem(element, setOf(Namespace.NAME)) + } + return variants } } -abstract class MvSchemaLitFieldMixin(node: ASTNode) : MvElementImpl(node), - MvSchemaLitField { - override fun getReference(): MvPolyVariantReference { - if (this.isShorthand) { - return MvSchemaFieldShorthandReferenceImpl(this) - } else { - return MvSchemaFieldReferenceImpl(this) +fun processSchemaLitFieldResolveVariants( + literalField: MvSchemaLitField, + processor: RsResolveProcessor +): Boolean { + val schemaLit = literalField.schemaLit ?: return false + val schema = schemaLit.path.maybeSchema ?: return false + return schema.fieldBindings + .any { field -> + processor.process(SimpleScopeEntry(field.name, field, setOf(Namespace.NAME))) } - } } -private fun resolveIntoSchemaField(element: MvSchemaLitField): List { - val schemaLit = element.schemaLit ?: return emptyList() - val schema = schemaLit.path.maybeSchema - val referenceName = element.referenceName - return schema - ?.fieldBindings.orEmpty() - .filter { it.name == referenceName } -} \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvStructLitField.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvStructLitField.kt index ee8acd9af..767076220 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvStructLitField.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvStructLitField.kt @@ -3,9 +3,14 @@ package org.move.lang.core.psi.ext import com.intellij.lang.ASTNode import org.move.lang.MvElementTypes import org.move.lang.core.psi.* +import org.move.lang.core.resolve.RsResolveProcessor +import org.move.lang.core.resolve.SimpleScopeEntry +import org.move.lang.core.resolve.collectResolveVariants +import org.move.lang.core.resolve.ref.MvMandatoryReferenceElement import org.move.lang.core.resolve.ref.MvPolyVariantReference -import org.move.lang.core.resolve.ref.MvStructLitShorthandFieldReferenceImpl -import org.move.lang.core.resolve.ref.MvStructRefFieldReferenceImpl +import org.move.lang.core.resolve.ref.MvPolyVariantReferenceCached +import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.resolve2.resolveBindingForFieldShorthand val MvStructLitField.structLitExpr: MvStructLitExpr get() = ancestorStrict()!! @@ -13,16 +18,74 @@ val MvStructLitField.structLitExpr: MvStructLitExpr val MvStructLitField.isShorthand: Boolean get() = !hasChild(MvElementTypes.COLON) -inline fun MvStructLitField.resolveToElement(): T? = +inline fun MvStructLitField.resolveToElement(): T? = reference.multiResolve().filterIsInstance().singleOrNull() fun MvStructLitField.resolveToDeclaration(): MvStructField? = resolveToElement() fun MvStructLitField.resolveToBinding(): MvBindingPat? = resolveToElement() -abstract class MvStructLitFieldMixin(node: ASTNode) : MvElementImpl(node), - MvStructLitField { - override fun getReference(): MvPolyVariantReference { - if (!this.isShorthand) return MvStructRefFieldReferenceImpl(this) - return MvStructLitShorthandFieldReferenceImpl(this) +interface MvStructRefField: MvMandatoryReferenceElement + +abstract class MvStructLitFieldMixin(node: ASTNode): MvElementImpl(node), + MvStructLitField { + override fun getReference(): MvPolyVariantReference = + MvStructRefFieldReferenceImpl(this, shorthand = this.isShorthand) +// if (this.isShorthand) { +// return MvStructLitShorthandFieldReferenceImpl(this) +// } else { +// return MvStructRefFieldReferenceImpl(this) +// } +} + +class MvStructRefFieldReferenceImpl( + element: MvStructRefField, + var shorthand: Boolean, +): MvPolyVariantReferenceCached(element) { + + override fun multiResolveInner(): List { + val referenceName = element.referenceName + var variants = collectResolveVariants(referenceName) { + processStructRefFieldResolveVariants(element, it) + } +// var variants = resolveIntoStructField(element) + if (shorthand) { + variants += resolveBindingForFieldShorthand(element) +// variants += resolveLocalItem(element, setOf(Namespace.NAME)) + } + return variants +// return listOf( +// resolveIntoStructField(element), +// resolveLocalItem(element, setOf(Namespace.NAME)) +// ).flatten() } } + +fun processStructRefFieldResolveVariants( + fieldRef: MvStructRefField, + processor: RsResolveProcessor +): Boolean { + val structItem = fieldRef.maybeStruct ?: return false + return structItem.fields + .any { field -> + processor.process(SimpleScopeEntry(field.name, field, setOf(Namespace.NAME))) + } +} + +fun resolveIntoStructField(element: MvStructRefField): List { + val structItem = element.maybeStruct ?: return emptyList() + val referenceName = element.referenceName + return structItem.fields + .filter { it.name == referenceName } +} + +private val MvStructRefField.maybeStruct: MvStruct? + get() { + return when (this) { + is MvStructPatField -> this.structPat.structItem + is MvStructLitField -> this.structLitExpr.path.maybeStruct + else -> null + } + } + + + diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPat.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPat.kt index bf42ba952..c97d7fed9 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPat.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPat.kt @@ -12,4 +12,4 @@ val MvStructPat.patFieldNames: List get() = patFields.map { it.referenceName } -val MvStructPat.structItem: MvStruct? get() = this.path.reference?.resolveWithAliases() as? MvStruct +val MvStructPat.structItem: MvStruct? get() = this.path.reference?.resolveFollowingAliases() as? MvStruct diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPatField.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPatField.kt index 5864b2984..325d1c0ed 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPatField.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvStructPatField.kt @@ -4,8 +4,6 @@ import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement import org.move.lang.core.psi.* import org.move.lang.core.resolve.ref.MvPolyVariantReference -import org.move.lang.core.resolve.ref.MvStructPatShorthandFieldReferenceImpl -import org.move.lang.core.resolve.ref.MvStructRefFieldReferenceImpl val MvStructPatField.structPat: MvStructPat get() = ancestorStrict()!! @@ -29,6 +27,7 @@ sealed class PatFieldKind { * ~~~~~~~~~ */ data class Full(val ident: PsiElement, val pat: MvPat): PatFieldKind() + /** * struct S { a: i32 } * let S { ref a } = ... @@ -44,8 +43,8 @@ val PatFieldKind.fieldName: String } -abstract class MvStructPatFieldMixin(node: ASTNode) : MvElementImpl(node), - MvStructPatField { +abstract class MvStructPatFieldMixin(node: ASTNode): MvElementImpl(node), + MvStructPatField { override val referenceNameElement: PsiElement get() { val bindingPat = this.bindingPat @@ -56,11 +55,11 @@ abstract class MvStructPatFieldMixin(node: ASTNode) : MvElementImpl(node), } } - override fun getReference(): MvPolyVariantReference { - return if (this.isShorthand) { - MvStructPatShorthandFieldReferenceImpl(this) - } else { - MvStructRefFieldReferenceImpl(this) - } - } + override fun getReference(): MvPolyVariantReference = + MvStructRefFieldReferenceImpl(this, shorthand = this.isShorthand) +// return if (this.isShorthand) { +// MvStructPatShorthandFieldReferenceImpl(this) +// } else { +// MvStructRefFieldReferenceImpl(this) +// } } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseAlias.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseAlias.kt index 095e92ae3..fe734dd2c 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseAlias.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseAlias.kt @@ -1,14 +1,11 @@ package org.move.lang.core.psi.ext import com.intellij.lang.ASTNode -import org.move.lang.core.psi.MvModuleUseSpeck import org.move.lang.core.psi.MvUseAlias -import org.move.lang.core.psi.MvUseItem +import org.move.lang.core.psi.MvUseSpeck import org.move.lang.core.psi.impl.MvNameIdentifierOwnerImpl -val MvUseAlias.useItem: MvUseItem? get() = this.parent as? MvUseItem +val MvUseAlias.parentUseSpeck: MvUseSpeck get() = this.parent as MvUseSpeck -val MvUseAlias.moduleUseSpeck: MvModuleUseSpeck? get() = this.parent as? MvModuleUseSpeck - -abstract class MvUseAliasMixin(node: ASTNode) : MvNameIdentifierOwnerImpl(node), - MvUseAlias +abstract class MvUseAliasMixin(node: ASTNode): MvNameIdentifierOwnerImpl(node), + MvUseAlias diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseGroup.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseGroup.kt new file mode 100644 index 000000000..7a2a88a79 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseGroup.kt @@ -0,0 +1,19 @@ +package org.move.lang.core.psi.ext + +import com.intellij.psi.PsiComment +import com.intellij.psi.SyntaxTraverser +import org.move.lang.core.psi.MvUseGroup +import org.move.lang.core.psi.MvUseSpeck + +val MvUseGroup.parentUseSpeck: MvUseSpeck get() = parent as MvUseSpeck + +val MvUseGroup.names get() = this.useSpeckList.mapNotNull { it.path.identifier?.text } + +val MvUseGroup.asTrivial: MvUseSpeck? + get() { + val speck = useSpeckList.singleOrNull() ?: return null + // Do not change use-groups with comments + if (SyntaxTraverser.psiTraverser(this).traverse().any { it is PsiComment }) return null + return speck + } + diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseItem.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseItem.kt deleted file mode 100644 index 66eaa052e..000000000 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseItem.kt +++ /dev/null @@ -1,105 +0,0 @@ -package org.move.lang.core.psi.ext - -import com.intellij.lang.ASTNode -import com.intellij.psi.PsiElement -import org.move.lang.core.psi.* -import org.move.lang.core.psi.impl.MvNamedElementImpl -import org.move.lang.core.resolve.ContextScopeInfo -import org.move.lang.core.resolve.LetStmtScope -import org.move.lang.core.resolve.ref.MvPolyVariantReferenceCached -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility -import org.move.lang.core.resolve.resolveModuleItem - -val MvUseItem.itemUseSpeck: MvItemUseSpeck - get() = ancestorStrict() ?: error("MvUseItem outside MvItemUseSpeck") - -val MvUseItem.annotationItem: MvElement - get() { - val parent = this.parent - if (parent is MvUseItemGroup && parent.useItemList.size != 1) return this - return useStmt - } - -val MvUseItem.useStmt: MvUseStmt - get() = - ancestorStrict() ?: error("always has MvUseStmt as ancestor") - -val MvUseItem.nameOrAlias: String? - get() { - val alias = this.useAlias - if (alias != null) { - return alias.identifier?.text - } - return this.identifier.text - } - -val MvUseItem.moduleName: String - get() { - val useStmt = this.ancestorStrict() - return useStmt?.itemUseSpeck?.fqModuleRef?.referenceName.orEmpty() - } - -val MvUseItem.isSelf: Boolean get() = this.identifier.textMatches("Self") - -class MvUseItemReferenceElement( - element: MvUseItem -): MvPolyVariantReferenceCached(element) { - - override fun multiResolveInner(): List { - val fqModuleRef = element.itemUseSpeck.fqModuleRef - val module = - fqModuleRef.reference?.resolve() as? MvModule ?: return emptyList() - if ((element.useAlias == null && element.text == "Self") - || (element.useAlias != null && element.text.startsWith("Self as")) - ) { - return listOf(module) - } - - val ns = setOf( - Namespace.TYPE, - Namespace.NAME, - Namespace.FUNCTION, - Namespace.SCHEMA, - Namespace.CONST - ) - val vs = Visibility.visibilityScopesForElement(fqModuleRef) - - // import has MAIN+VERIFY, and TEST if it or any of the parents has test - val useItemScopes = mutableSetOf(NamedItemScope.MAIN, NamedItemScope.VERIFY) - - // gather scopes for all parents up to MvUseStmt - var scopedElement: MvElement? = element - while (scopedElement != null) { - useItemScopes.addAll(scopedElement.itemScopes) - scopedElement = scopedElement.parent as? MvElement - } - - val contextScopeInfo = - ContextScopeInfo( - letStmtScope = LetStmtScope.EXPR_STMT, - refItemScopes = useItemScopes, - ) - return resolveModuleItem( - module, - element.referenceName, - ns, - vs, - contextScopeInfo - ) - } - -} - -abstract class MvUseItemMixin(node: ASTNode): MvNamedElementImpl(node), - MvUseItem { - override fun getName(): String? { - val name = super.getName() - if (name != "Self") return name - return ancestorStrict()?.fqModuleRef?.referenceName ?: name - } - - override val referenceNameElement: PsiElement get() = identifier - - override fun getReference() = MvUseItemReferenceElement(this) -} diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseItemGroup.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseItemGroup.kt deleted file mode 100644 index f6b2ccb1e..000000000 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseItemGroup.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.move.lang.core.psi.ext - -import com.intellij.psi.PsiComment -import com.intellij.psi.SyntaxTraverser -import org.move.lang.core.psi.MvItemUseSpeck -import org.move.lang.core.psi.MvUseItem -import org.move.lang.core.psi.MvUseItemGroup - -val MvUseItemGroup.names get() = this.useItemList.mapNotNull { it.identifier.text } - -val MvUseItemGroup.parentUseSpeck: MvItemUseSpeck get() = parent as MvItemUseSpeck - -val MvUseItemGroup.asTrivial: MvUseItem? - get() { - val speck = useItemList.singleOrNull() ?: return null - // Do not change use-groups with comments - if (SyntaxTraverser.psiTraverser(this).traverse().any { it is PsiComment }) return null - return speck - } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseSpeck.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseSpeck.kt new file mode 100644 index 000000000..c022248d4 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseSpeck.kt @@ -0,0 +1,11 @@ +package org.move.lang.core.psi.ext + +import org.move.lang.core.psi.MvPath +import org.move.lang.core.psi.MvUseGroup +import org.move.lang.core.psi.MvUseSpeck + +val MvUseSpeck.qualifier: MvPath? get() { + val parentUseSpeck = (context as? MvUseGroup)?.parentUseSpeck ?: return null + return parentUseSpeck.path +} +val MvUseSpeck.isSelf: Boolean get() = this.path.identifier?.textMatches("Self") ?: false diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseStmt.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvUseStmt.kt deleted file mode 100644 index f6d635318..000000000 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvUseStmt.kt +++ /dev/null @@ -1,131 +0,0 @@ -package org.move.lang.core.psi.ext - -import org.move.ide.inspections.imports.declaredItemScope -import org.move.lang.core.psi.* -import org.move.stdext.wrapWithList - -val MvUseStmt.addressRef: MvAddressRef? - get() { - val moduleUseSpeck = this.moduleUseSpeck - if (moduleUseSpeck != null) { - val fqModuleRef = moduleUseSpeck.fqModuleRef - if (fqModuleRef != null) { - return fqModuleRef.addressRef - } else { - return moduleUseSpeck.addressRef - } - } - val itemUseSpeck = this.itemUseSpeck - if (itemUseSpeck != null) { - return itemUseSpeck.fqModuleRef.addressRef - } - return null - } - -val MvUseStmt.useGroupLevel: Int - get() { - if (this.hasTestOnlyAttr) return 5 - return this.addressRef?.useGroupLevel ?: -1 - } - -val MvUseStmt.fqModuleText: String? - get() { - val fqModuleRef = this.fqModuleRef ?: return null - return fqModuleRef.text - } - -val MvUseStmt.fqModuleRef: MvFQModuleRef? - get() { - val moduleUseSpeck = this.moduleUseSpeck - if (moduleUseSpeck != null) { - return moduleUseSpeck.fqModuleRef - } - val itemUseSpeck = this.itemUseSpeck - if (itemUseSpeck != null) { - return itemUseSpeck.fqModuleRef - } - return null - } - -val MvUseStmt.childUseItems: List - get() { - return this.itemUseSpeck?.useItems.orEmpty() -// if (itemUseSpeck != null) { -// return itemUseSpeck.useItems -//// val group = itemUseSpeck.useItemGroup -//// if (group != null) { -//// return group.useItemList -//// } -//// return itemUseSpeck.useItem.wrapWithList() -// } -// return emptyList() - } - -val MvItemUseSpeck.useItems: List - get() { - val group = this.useItemGroup - if (group != null) { - return group.useItemList - } - return this.useItem.wrapWithList() - } - -sealed class UseSpeck(open val nameOrAlias: String, open val scope: NamedItemScope) { - data class Module( - override val nameOrAlias: String, - override val scope: NamedItemScope, - val moduleUseSpeck: MvModuleUseSpeck, - ): UseSpeck(nameOrAlias, scope) - - data class SelfModule( - override val nameOrAlias: String, - override val scope: NamedItemScope, - val useItem: MvUseItem, - ): UseSpeck(nameOrAlias, scope) - - data class Item( - override val nameOrAlias: String, - override val scope: NamedItemScope, - val useItem: MvUseItem, - ): UseSpeck(nameOrAlias, scope) -} - -val MvUseStmt.useSpecks: List - get() { - val stmtItemScope = this.declaredItemScope - val moduleUseSpeck = this.moduleUseSpeck - if (moduleUseSpeck != null) { - val nameOrAlias = moduleUseSpeck.nameElement?.text ?: return emptyList() - return listOf(UseSpeck.Module(nameOrAlias, stmtItemScope, moduleUseSpeck)) - } - return this.itemUseSpeck?.useItems.orEmpty() - .mapNotNull { - if (it.isSelf) { - val useAlias = it.useAlias - val nameOrAlias = - if (useAlias != null) { - val aliasName = useAlias.name ?: return@mapNotNull null - aliasName - } else { - it.moduleName - } - UseSpeck.SelfModule(nameOrAlias, stmtItemScope, it) - } else { - val nameOrAlias = it.nameOrAlias ?: return@mapNotNull null - UseSpeck.Item(nameOrAlias, stmtItemScope, it) - } - } - } - -val MvUseStmt.useSpeckText: String - get() { - val moduleUseSpeck = this.moduleUseSpeck - if (moduleUseSpeck != null) { - return moduleUseSpeck.text - } - val itemUseSpeck = this.itemUseSpeck - if (itemUseSpeck != null) { - return itemUseSpeck.text - } - return "" - } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvVisibilityOwner.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvVisibilityOwner.kt new file mode 100644 index 000000000..f8b9e426f --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvVisibilityOwner.kt @@ -0,0 +1,55 @@ +package org.move.lang.core.psi.ext + +import com.intellij.psi.util.PsiTreeUtil +import org.move.cli.containingMovePackage +import org.move.lang.core.psi.MvElement +import org.move.lang.core.psi.MvVisibilityModifier +import org.move.lang.core.psi.containingModule +import org.move.lang.core.psi.ext.VisKind.* +import org.move.lang.core.resolve.ref.Visibility2 + +interface MvVisibilityOwner: MvElement { + val visibilityModifier: MvVisibilityModifier? + get() = PsiTreeUtil.getStubChildOfType(this, MvVisibilityModifier::class.java) + + // restricted visibility considered as public + val isPublic: Boolean get() = visibilityModifier != null +} + +// todo: add VisibilityModifier to stubs, rename this one to VisStubKind +enum class VisKind { + // PRIVATE, + PUBLIC, + FRIEND, + PACKAGE, + SCRIPT; +} + +val MvVisibilityModifier.stubKind: VisKind + get() = when { + this.isPublicFriend -> FRIEND + this.isPublicPackage -> PACKAGE + this.isPublicScript -> SCRIPT + this.isPublic -> PUBLIC + else -> error("unreachable") + } + +val MvVisibilityOwner.visibility2: Visibility2 + get() { + val kind = this.visibilityModifier?.stubKind ?: return Visibility2.Private + + return when (kind) { + PACKAGE -> containingMovePackage?.let { Visibility2.Restricted.Package(it) } ?: Visibility2.Public + FRIEND -> { + val module = this.containingModule ?: return Visibility2.Private + // todo: make lazy + Visibility2.Restricted.Friend(lazy { module.friendModules }) + } + SCRIPT -> Visibility2.Restricted.Script + PUBLIC -> Visibility2.Public + } + } + + + + diff --git a/src/main/kotlin/org/move/lang/core/resolve/MatchingProcessor.kt b/src/main/kotlin/org/move/lang/core/resolve/MatchingProcessor.kt index a2fa79305..5e0d69ee1 100644 --- a/src/main/kotlin/org/move/lang/core/resolve/MatchingProcessor.kt +++ b/src/main/kotlin/org/move/lang/core/resolve/MatchingProcessor.kt @@ -1,41 +1,8 @@ package org.move.lang.core.resolve import org.move.lang.core.psi.MvNamedElement -import org.move.lang.core.psi.ext.isMslOnlyItem -import org.move.lang.core.psi.isVisibleInContext data class ScopeItem( val name: String, val element: T ) - -fun interface MatchingProcessor { - fun match(entry: ScopeItem): Boolean - - fun match(itemElement: T): Boolean { - val name = itemElement.name ?: return false - val entry = ScopeItem(name, itemElement) - return match(entry) - } - - fun match(contextScopeInfo: ContextScopeInfo, itemElement: T): Boolean { - if ( - !contextScopeInfo.isMslScope && itemElement.isMslOnlyItem - ) return false - if (!itemElement.isVisibleInContext(contextScopeInfo.refItemScopes)) return false - - val name = itemElement.name ?: return false - val entry = ScopeItem(name, itemElement) - return match(entry) - } - - fun matchAll(contextScopeInfo: ContextScopeInfo, vararg collections: Iterable): Boolean = - sequenceOf(*collections) - .flatten() - .any { match(contextScopeInfo, it) } - - fun matchAll(vararg collections: Iterable): Boolean = - sequenceOf(*collections) - .flatten() - .any { match(it) } -} diff --git a/src/main/kotlin/org/move/lang/core/resolve/ModuleItemResolution.kt b/src/main/kotlin/org/move/lang/core/resolve/ModuleItemResolution.kt deleted file mode 100644 index 6b79a1eba..000000000 --- a/src/main/kotlin/org/move/lang/core/resolve/ModuleItemResolution.kt +++ /dev/null @@ -1,106 +0,0 @@ -package org.move.lang.core.resolve - -import org.move.lang.core.psi.MvModule -import org.move.lang.core.psi.MvNamedElement -import org.move.lang.core.psi.ext.* -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility - -fun processModuleInnerItems( - module: MvModule, - namespaces: Set, - visibilities: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, -): Boolean { - for (namespace in namespaces) { - val found = when (namespace) { - Namespace.NAME -> { - processor.matchAll( - contextScopeInfo, - if (contextScopeInfo.isMslScope) module.consts() else emptyList(), - if (contextScopeInfo.isMslScope) module.structs() else emptyList(), - if (contextScopeInfo.isMslScope) - module.allModuleSpecs() - .map { - it.moduleItemSpecs() - .flatMap { spec -> spec.itemSpecBlock?.globalVariables().orEmpty() } - } - .flatten() - else emptyList() - ) - } - Namespace.FUNCTION -> { - val functions = visibilities.flatMap { module.functionsVisibleInScope(it) } - val specFunctions = - if (contextScopeInfo.isMslScope) module.specFunctions() else emptyList() - val specInlineFunctions = - if (contextScopeInfo.isMslScope) module.specInlineFunctions() else emptyList() - processor.matchAll( - contextScopeInfo, - functions, specFunctions, specInlineFunctions - ) - } - Namespace.TYPE -> processor.matchAll(contextScopeInfo, module.structs()) - Namespace.SCHEMA -> processor.matchAll(contextScopeInfo, module.schemas()) - Namespace.CONST -> processor.matchAll(contextScopeInfo, module.consts()) - else -> continue - } - if (found) return true - } - return false -} - -fun processModuleSpecItems( - module: MvModule, - namespaces: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, -): Boolean { - for (namespace in namespaces) { - for (moduleSpec in module.allModuleSpecs()) { - val matched = when (namespace) { - Namespace.FUNCTION -> - processor.matchAll( - contextScopeInfo, - moduleSpec.specFunctions(), - moduleSpec.specInlineFunctions() - ) - Namespace.SCHEMA -> processor.matchAll(contextScopeInfo, moduleSpec.schemas()) - else -> false - } - if (matched) return true - } - } - return false -} - -fun processModuleItems( - module: MvModule, - namespaces: Set, - visibilities: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, -): Boolean { - return processModuleInnerItems(module, namespaces, visibilities, contextScopeInfo, processor) - || - contextScopeInfo.isMslScope && processModuleSpecItems(module, namespaces, contextScopeInfo, processor) -} - -fun resolveModuleItem( - module: MvModule, - name: String, - namespaces: Set, - visibilities: Set, - contextScopeInfo: ContextScopeInfo, -): List { - val resolved = mutableListOf() - processModuleItems(module, namespaces, visibilities, contextScopeInfo) { - if (it.name == name) { - resolved.add(it.element) - return@processModuleItems true - } - return@processModuleItems false - } - return resolved -} diff --git a/src/main/kotlin/org/move/lang/core/resolve/NameResolution.kt b/src/main/kotlin/org/move/lang/core/resolve/NameResolution.kt deleted file mode 100644 index 3f3cbe4ec..000000000 --- a/src/main/kotlin/org/move/lang/core/resolve/NameResolution.kt +++ /dev/null @@ -1,297 +0,0 @@ -package org.move.lang.core.resolve - -import com.intellij.psi.search.GlobalSearchScope -import org.move.cli.containingMovePackage -import org.move.cli.settings.moveSettings -import org.move.lang.MoveFile -import org.move.lang.core.psi.* -import org.move.lang.core.psi.NamedItemScope.MAIN -import org.move.lang.core.psi.NamedItemScope.VERIFY -import org.move.lang.core.psi.ext.* -import org.move.lang.core.resolve.LetStmtScope.EXPR_STMT -import org.move.lang.core.resolve.LetStmtScope.NONE -import org.move.lang.core.resolve.ref.MvReferenceElement -import org.move.lang.core.resolve.ref.Namespace -import org.move.lang.core.resolve.ref.Visibility -import org.move.lang.core.types.address -import org.move.lang.index.MvNamedElementIndex -import org.move.lang.moveProject -import org.move.lang.toNioPathOrNull -import org.move.stdext.wrapWithList - -data class ContextScopeInfo( - val refItemScopes: Set, - val letStmtScope: LetStmtScope, -) { - val isMslScope get() = letStmtScope != LetStmtScope.NONE - - fun matches(itemElement: MvNamedElement): Boolean { - if ( - !this.isMslScope && itemElement.isMslOnlyItem - ) return false - if (!itemElement.isVisibleInContext(this.refItemScopes)) return false - return true - } - - companion object { - /// really does not affect anything, created just to allow creating CompletionContext everywhere - fun default(): ContextScopeInfo = ContextScopeInfo(setOf(MAIN), NONE) - fun msl(): ContextScopeInfo = ContextScopeInfo(setOf(VERIFY), EXPR_STMT) - } -} - -fun processItems( - element: MvElement, - namespaces: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, -): Boolean { - return walkUpThroughScopes( - element, - stopAfter = { it is MvModule } - ) { cameFrom, scope -> - processItemsInScope( - scope, cameFrom, namespaces, contextScopeInfo, processor - ) - } -} - -fun resolveLocalItem( - element: MvReferenceElement, - namespaces: Set -): List { - val contextScopeInfo = - ContextScopeInfo( - letStmtScope = element.letStmtScope, - refItemScopes = element.refItemScopes, - ) - val referenceName = element.referenceName - var resolved: MvNamedElement? = null - processItems(element, namespaces, contextScopeInfo) { - if (it.name == referenceName) { - resolved = it.element - return@processItems true - } - return@processItems false - } - return resolved.wrapWithList() -} - -// go from local MODULE reference to corresponding FqModuleRef (from import) -fun resolveIntoFQModuleRefInUseSpeck(moduleRef: MvModuleRef): MvFQModuleRef? { - check(moduleRef !is MvFQModuleRef) { "Should be handled on the upper level" } - - // module refers to ModuleImport - var resolved = resolveLocalItem(moduleRef, setOf(Namespace.MODULE)).firstOrNull() - if (resolved is MvUseAlias) { - resolved = resolved.moduleUseSpeck ?: resolved.useItem - } - if (resolved is MvUseItem && resolved.isSelf) { - return resolved.itemUseSpeck.fqModuleRef - } -// if (resolved !is MvModuleUseSpeck) return null - return (resolved as? MvModuleUseSpeck)?.fqModuleRef -// return resolved.fqModuleRef -} - -fun processQualItem( - item: MvNamedElement, - namespaces: Set, - visibilities: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, -): Boolean { - val matched = when { - item is MvModule && Namespace.MODULE in namespaces - || item is MvStruct && Namespace.TYPE in namespaces - || item is MvSchema && Namespace.SCHEMA in namespaces -> - processor.match(contextScopeInfo, item) - - item is MvFunction && Namespace.FUNCTION in namespaces -> { - if (item.hasTestAttr) return false - for (vis in visibilities) { - when { - vis is Visibility.Public - && item.visibility == FunctionVisibility.PUBLIC -> processor.match( - contextScopeInfo, - item - ) - - vis is Visibility.PublicScript - && item.visibility == FunctionVisibility.PUBLIC_SCRIPT -> - processor.match(contextScopeInfo, item) - - vis is Visibility.PublicFriend && item.visibility == FunctionVisibility.PUBLIC_FRIEND -> { - val itemModule = item.module ?: return false - val currentModule = vis.currentModule.element ?: return false - if (currentModule.fqModule() in itemModule.declaredFriendModules) { - processor.match(contextScopeInfo, item) - } - } - - vis is Visibility.PublicPackage && item.visibility == FunctionVisibility.PUBLIC_PACKAGE -> { - if (!item.project.moveSettings.enablePublicPackage) { - return false - } - val itemPackage = item.containingMovePackage ?: return false - if (vis.originPackage == itemPackage) { - processor.match(contextScopeInfo, item) - } - } - - vis is Visibility.Internal -> processor.match(contextScopeInfo, item) - } - } - false - } - else -> false - } - return matched -} - -fun processModulesInFile(file: MoveFile, moduleProcessor: MatchingProcessor): Boolean { - for (module in file.modules()) { - if (moduleProcessor.match(module)) return true - } - return false -} - -fun processFQModuleRef( - fqModuleRef: MvFQModuleRef, - processor: MatchingProcessor, -) { - val moveProj = fqModuleRef.moveProject ?: return - val refAddressText = fqModuleRef.addressRef.address(moveProj)?.canonicalValue(moveProj) - - val contextScopeInfo = ContextScopeInfo( - letStmtScope = fqModuleRef.letStmtScope, - refItemScopes = fqModuleRef.refItemScopes, - ) - val moduleProcessor = MatchingProcessor { - if (!contextScopeInfo.matches(it.element)) { - return@MatchingProcessor false - } - val entry = ScopeItem(it.name, it.element as MvModule) - val modAddressText = entry.element.address(moveProj)?.canonicalValue(moveProj) - if (modAddressText != refAddressText) - return@MatchingProcessor false - processor.match(entry) - } - - // first search modules in the current file - val currentFile = fqModuleRef.containingMoveFile ?: return - var stopped = processModulesInFile(currentFile, moduleProcessor) -// var stopped = -// processFileItems(currentFile, setOf(Namespace.MODULE), Visibility.local(), itemVis, moduleProcessor) - if (stopped) return - - moveProj.processMoveFiles { moveFile -> - // skip current file as it's processed already - if (moveFile.toNioPathOrNull() == currentFile.toNioPathOrNull()) - return@processMoveFiles true - stopped = processModulesInFile(moveFile, moduleProcessor) - // if not resolved, returns true to indicate that next file should be tried - !stopped - } -} - -fun processFQModuleRef( - moduleRef: MvFQModuleRef, - target: String, - processor: MatchingProcessor, -) { - val project = moduleRef.project - val moveProj = moduleRef.moveProject ?: return - val refAddress = moduleRef.addressRef.address(moveProj)?.canonicalValue(moveProj) - - val contextScopeInfo = ContextScopeInfo( - letStmtScope = moduleRef.letStmtScope, - refItemScopes = moduleRef.refItemScopes, - ) - val matchModule = MatchingProcessor { - if (!contextScopeInfo.matches(it.element)) return@MatchingProcessor false - val entry = ScopeItem(it.name, it.element as MvModule) - // TODO: check belongs to the current project - val modAddress = entry.element.address(moveProj)?.canonicalValue(moveProj) - - if (modAddress != refAddress) return@MatchingProcessor false - processor.match(entry) - } - - - // search modules in the current file first - val currentFile = moduleRef.containingMoveFile ?: return - val stopped = processModulesInFile(currentFile, matchModule) -// processFileItems(currentFile, setOf(Namespace.MODULE), Visibility.local(), itemVis, matchModule) - if (stopped) return - - val currentFileScope = GlobalSearchScope.fileScope(currentFile) - val searchScope = - moveProj.searchScope().intersectWith(GlobalSearchScope.notScope(currentFileScope)) - - MvNamedElementIndex - .processElementsByName(project, target, searchScope) { - val matched = - processQualItem(it, setOf(Namespace.MODULE), Visibility.local(), contextScopeInfo, matchModule) - if (matched) return@processElementsByName false - - true - } -} - -fun walkUpThroughScopes( - start: MvElement, - stopAfter: (MvElement) -> Boolean, - handleScope: (cameFrom: MvElement, scope: MvElement) -> Boolean, -): Boolean { - var cameFrom = start - var scope = start.context as MvElement? - while (scope != null) { - if (handleScope(cameFrom, scope)) return true - - // walk all items in original module block - if (scope is MvModuleBlock) { - // handle spec module {} - if (handleModuleItemSpecsInBlock(cameFrom, scope, handleScope)) return true - // walk over all spec modules - for (moduleSpec in scope.module.allModuleSpecs()) { - val moduleSpecBlock = moduleSpec.moduleSpecBlock ?: continue - if (handleScope(cameFrom, moduleSpecBlock)) return true - if (handleModuleItemSpecsInBlock(cameFrom, moduleSpecBlock, handleScope)) return true - } - } - - if (scope is MvModuleSpecBlock) { - val moduleBlock = scope.moduleSpec.moduleItem?.moduleBlock - if (moduleBlock != null) { - cameFrom = scope - scope = moduleBlock - continue - } - } - - if (stopAfter(scope)) break - - cameFrom = scope - scope = scope.context as? MvElement - } - - return false -} - -private fun handleModuleItemSpecsInBlock( - cameFrom: MvElement, - block: MvElement, - handleScope: (cameFrom: MvElement, scope: MvElement) -> Boolean -): Boolean { - val moduleItemSpecs = when (block) { - is MvModuleBlock -> block.moduleItemSpecList - is MvModuleSpecBlock -> block.moduleItemSpecList - else -> emptyList() - } - for (moduleItemSpec in moduleItemSpecs.filter { it != cameFrom }) { - val itemSpecBlock = moduleItemSpec.itemSpecBlock ?: continue - if (handleScope(cameFrom, itemSpecBlock)) return true - } - return false -} diff --git a/src/main/kotlin/org/move/lang/core/resolve/Processors.kt b/src/main/kotlin/org/move/lang/core/resolve/Processors.kt new file mode 100644 index 000000000..92917afcc --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve/Processors.kt @@ -0,0 +1,578 @@ +package org.move.lang.core.resolve + +import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.util.SmartList +import org.move.lang.core.completion.CompletionContext +import org.move.lang.core.completion.createLookupElement +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.MvItemElement +import org.move.lang.core.psi.ext.MvMethodOrPath +import org.move.lang.core.resolve.VisibilityStatus.Visible +import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.resolve2.createFilter +import org.move.lang.core.resolve2.ref.ResolutionContext +import org.move.lang.core.resolve2.ref.RsPathResolveResult +import org.move.lang.core.resolve2.visInfo +import org.move.stdext.intersects + +/** + * ScopeEntry is some PsiElement visible in some code scope. + * + * [SimpleScopeEntry] handles the two cases: + * * aliases (that's why we need a [name] property) + * * lazy resolving of actual elements (that's why [element] can return `null`) - unused for now + */ +interface ScopeEntry { + val name: String + val element: MvNamedElement + val namespaces: Set + fun doCopyWithNs(namespaces: Set): ScopeEntry +} + +@Suppress("UNCHECKED_CAST") +private fun T.copyWithNs(namespaces: Set): T = doCopyWithNs(namespaces) as T + +typealias RsProcessor = (T) -> Boolean + +interface RsResolveProcessorBase { + /** + * Return `true` to stop further processing, + * return `false` to continue search + */ + fun process(entry: T): Boolean + + /** + * Indicates that processor is interested only in [ScopeEntry]s with specified [names]. + * Improves performance for Resolve2. + * `null` in completion + */ + val names: Set? + + fun acceptsName(name: String): Boolean { + val names = names + return names == null || name in names + } +} + +//interface RsResolveProcessor { +// /** +// * Return `true` to stop further processing, +// * return `false` to continue search +// */ +// fun process(entry: SimpleScopeEntry): Boolean +// +// /** +// * Indicates that processor is interested only in [SimpleScopeEntry]s with specified [names]. +// * Improves performance for Resolve2. +// * `null` in completion +// */ +// val names: Set? +// +// fun acceptsName(name: String): Boolean { +// val names = names +// return names == null || name in names +// } +//} + +typealias RsResolveProcessor = RsResolveProcessorBase + +fun createStoppableProcessor(processor: (ScopeEntry) -> Boolean): RsResolveProcessor { + return object: RsResolveProcessorBase { + override fun process(entry: ScopeEntry): Boolean = processor(entry) + override val names: Set? get() = null + } +} + +fun createProcessor(processor: (ScopeEntry) -> Unit): RsResolveProcessor { + return object: RsResolveProcessorBase { + override fun process(entry: ScopeEntry): Boolean { + processor(entry) + return false + } + + override val names: Set? get() = null + } +} + +fun RsResolveProcessorBase.wrapWithMapper( + mapper: (U) -> T +): RsResolveProcessorBase { + return MappingProcessor(this, mapper) +} + +private class MappingProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val mapper: (U) -> T, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: U): Boolean { + val mapped = mapper(entry) + return originalProcessor.process(mapped) + } + + override fun toString(): String = "MappingProcessor($originalProcessor, mapper = $mapper)" +} + +fun RsResolveProcessorBase.wrapWithNonNullMapper( + mapper: (U) -> T? +): RsResolveProcessorBase { + return NonNullMappingProcessor(this, mapper) +} + +private class NonNullMappingProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val mapper: (U) -> T?, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: U): Boolean { + val mapped = mapper(entry) + return if (mapped == null) { + false + } else { + originalProcessor.process(mapped) + } + } + + override fun toString(): String = "MappingProcessor($originalProcessor, mapper = $mapper)" +} + +fun RsResolveProcessorBase.wrapWithFilter( + filter: (T) -> Boolean +): RsResolveProcessorBase { + return FilteringProcessor(this, filter) +} + +private class FilteringProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val filter: (T) -> Boolean, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: T): Boolean { + return if (filter(entry)) { + originalProcessor.process(entry) + } else { + false + } + } + + override fun toString(): String = "FilteringProcessor($originalProcessor, filter = $filter)" +} + +fun RsResolveProcessorBase.wrapWithBeforeProcessingHandler( + handler: (T) -> Unit +): RsResolveProcessorBase { + return BeforeProcessingProcessor(this, handler) +} + +private class BeforeProcessingProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val handler: (T) -> Unit, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: T): Boolean { + handler(entry) + return originalProcessor.process(entry) + } + + override fun toString(): String = "BeforeProcessingProcessor($originalProcessor, handler = $handler)" +} + + +fun RsResolveProcessorBase.wrapWithShadowingProcessor( + prevScope: Map>, + ns: Set, +): RsResolveProcessorBase { + return ShadowingProcessor(this, prevScope, ns) +} + +private class ShadowingProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val prevScope: Map>, + private val ns: Set, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: T): Boolean { + val prevNs = prevScope[entry.name] + if (entry.name == "_" || prevNs == null) return originalProcessor.process(entry) + val restNs = entry.namespaces.minus(prevNs) + return ns.intersects(restNs) && originalProcessor.process(entry.copyWithNs(restNs)) + } + + override fun toString(): String = "ShadowingProcessor($originalProcessor, ns = $ns)" +} + +fun RsResolveProcessorBase.wrapWithShadowingProcessorAndUpdateScope( + prevScope: Map>, + currScope: MutableMap>, + ns: Set, +): RsResolveProcessorBase { + return ShadowingAndUpdateScopeProcessor(this, prevScope, currScope, ns) +} + +private class ShadowingAndUpdateScopeProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val prevScope: Map>, + private val currScope: MutableMap>, + private val ns: Set, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: T): Boolean { + if (!originalProcessor.acceptsName(entry.name) || entry.name == "_") { + return originalProcessor.process(entry) + } + val prevNs = prevScope[entry.name] + val newNs = entry.namespaces + val entryWithIntersectedNs = if (prevNs != null) { + val restNs = newNs.minus(prevNs) + if (ns.intersects(restNs)) { + entry.copyWithNs(restNs) + } else { + return false + } + } else { + entry + } + currScope[entry.name] = prevNs?.let { it + newNs } ?: newNs + return originalProcessor.process(entryWithIntersectedNs) + } + + override fun toString(): String = "ShadowingAndUpdateScopeProcessor($originalProcessor, ns = $ns)" +} + +fun RsResolveProcessorBase.wrapWithShadowingProcessorAndImmediatelyUpdateScope( + prevScope: MutableMap>, + ns: Set, +): RsResolveProcessorBase { + return ShadowingAndImmediatelyUpdateScopeProcessor(this, prevScope, ns) +} + +private class ShadowingAndImmediatelyUpdateScopeProcessor( + private val originalProcessor: RsResolveProcessorBase, + private val prevScope: MutableMap>, + private val ns: Set, +): RsResolveProcessorBase { + override val names: Set? = originalProcessor.names + override fun process(entry: T): Boolean { + if (entry.name in prevScope) return false + val result = originalProcessor.process(entry) + if (originalProcessor.acceptsName(entry.name)) { + prevScope[entry.name] = ns + } + return result + } + + override fun toString(): String = + "ShadowingAndImmediatelyUpdateScopeProcessor($originalProcessor, ns = $ns)" +} + +fun collectResolveVariants(referenceName: String?, f: (RsResolveProcessor) -> Unit): List { + if (referenceName == null) return emptyList() + val processor = ResolveVariantsCollector(referenceName) + f(processor) + return processor.result +} + +private class ResolveVariantsCollector( + private val referenceName: String, + val result: MutableList = SmartList(), +): RsResolveProcessorBase { + override val names: Set = setOf(referenceName) + + override fun process(entry: ScopeEntry): Boolean { + if (entry.name == referenceName) { + val element = entry.element +// if (element !is RsDocAndAttributeOwner || element.existsAfterExpansionSelf) { + result += element +// } + } + return false + } +} + +fun collectResolveVariantsAsScopeEntries( + referenceName: String?, + f: (RsResolveProcessorBase) -> Unit +): List { + if (referenceName == null) return emptyList() + val processor = ResolveVariantsAsScopeEntriesCollector(referenceName) + f(processor) + return processor.result +} + +private class ResolveVariantsAsScopeEntriesCollector( + private val referenceName: String, + val result: MutableList = mutableListOf(), +): RsResolveProcessorBase { + override val names: Set = setOf(referenceName) + + override fun process(entry: T): Boolean { + if (entry.name == referenceName) { +// val element = entry.element +// if (element !is RsDocAndAttributeOwner || element.existsAfterExpansionSelf) { + result += entry +// } + } + return false + } +} + +/// checks for visibility of items +fun collectMethodOrPathResolveVariants( + methodOrPath: MvMethodOrPath, + ctx: ResolutionContext, + f: (RsResolveProcessor) -> Unit +): List> { + val referenceName = methodOrPath.referenceName ?: return emptyList() + val processor = SinglePathResolveVariantsCollector(ctx, referenceName) + f(processor) + return processor.result +} + +private class SinglePathResolveVariantsCollector( + private val ctx: ResolutionContext, + private val referenceName: String, + val result: MutableList> = SmartList(), +): RsResolveProcessorBase { + override val names: Set = setOf(referenceName) + + override fun process(entry: ScopeEntry): Boolean { + if (entry.name == referenceName) { + collectMethodOrPathScopeEntry(ctx, result, entry) + } + return false + } +} + +private fun collectMethodOrPathScopeEntry( + ctx: ResolutionContext, + result: MutableList>, + e: ScopeEntry +) { + val element = e.element + val visibilityStatus = ctx.methodOrPath?.let { e.getVisibilityStatusFrom(it) } ?: Visible + val isVisible = visibilityStatus == Visible + result += RsPathResolveResult(element, isVisible) +} + +//fun pickFirstResolveVariant(referenceName: String?, f: (RsResolveProcessor) -> Unit): MvElement? = +// pickFirstResolveEntry(referenceName, f)?.element + +fun pickFirstResolveEntry(referenceName: String?, f: (RsResolveProcessor) -> Unit): ScopeEntry? { + if (referenceName == null) return null + val processor = PickFirstScopeEntryCollector(referenceName) + f(processor) + return processor.result +} + +private class PickFirstScopeEntryCollector( + private val referenceName: String, + var result: ScopeEntry? = null, +): RsResolveProcessorBase { + override val names: Set = setOf(referenceName) + + override fun process(entry: ScopeEntry): Boolean { + if (entry.name == referenceName) { +// val element = entry.element +// if (element !is RsDocAndAttributeOwner || element.existsAfterExpansionSelf) { + result = entry + return true +// } + } + return false + } +} + + +fun collectCompletionVariants( + result: CompletionResultSet, + context: CompletionContext, + f: (RsResolveProcessor) -> Unit +) { + val processor = CompletionVariantsCollector(result, context) + f(processor) +} + +private class CompletionVariantsCollector( + private val result: CompletionResultSet, + private val context: CompletionContext, +): RsResolveProcessorBase { + override val names: Set? get() = null + + override fun process(entry: ScopeEntry): Boolean { +// addEnumVariantsIfNeeded(entry) + + result.addElement(createLookupElement( + scopeEntry = entry, + completionContext = context, + priority = entry.element.completionPriority + )) + return false + } + +// private fun addEnumVariantsIfNeeded(entry: ScopeEntry) { +// val element = entry.element as? RsEnumItem ?: return +// +// val expectedType = (context.expectedTy?.ty?.stripReferences() as? TyAdt)?.item +// val actualType = (element.declaredType as? TyAdt)?.item +// +// val parent = context.context +// val contextPat = if (parent is RsPath) parent.context else parent +// val contextIsPat = contextPat is RsPatBinding || contextPat is RsPatStruct || contextPat is RsPatTupleStruct +// +// if (expectedType == actualType || contextIsPat) { +// val variants = collectVariantsForEnumCompletion(element, context, entry.subst) +// val filtered = when (contextPat) { +// is RsPatStruct -> variants.filter { (it.psiElement as? RsEnumVariant)?.blockFields != null } +// is RsPatTupleStruct -> variants.filter { (it.psiElement as? RsEnumVariant)?.tupleFields != null } +// else -> variants +// } +// result.addAllElements(filtered) +// } +// } +} + +//fun collectNames(f: (RsResolveProcessor) -> Unit): Set { +// val processor = NamesCollector() +// f(processor) +// return processor.result +//} + +//private class NamesCollector( +// val result: MutableSet = mutableSetOf(), +//): RsResolveProcessor { +// override val names: Set? get() = null +// +// override fun process(entry: SimpleScopeEntry): Boolean { +// if (entry.name != "_") { +// result += entry.name +// } +// return false +// } +//} + +data class SimpleScopeEntry( + override val name: String, + override val element: MvNamedElement, + override val namespaces: Set, +// override val subst: Substitution = emptySubstitution +): ScopeEntry { + override fun doCopyWithNs(namespaces: Set): ScopeEntry = copy(namespaces = namespaces) +} + +data class ModInfo( +// val movePackage: MovePackage?, + val module: MvModule?, +// val isScript: Boolean, +) { +} + +fun Map.entriesWithNames(names: Set?): Map { + return if (names.isNullOrEmpty()) { + this + } else if (names.size == 1) { + val single = names.single() + val value = this[single] ?: return emptyMap() + mapOf(single to value) + } else { + names.mapNotNull { name -> this[name]?.let { name to it } }.toMap() + } +} + +fun interface VisibilityFilter { + fun filter(methodOrPath: MvMethodOrPath, ns: Set): VisibilityStatus +} + +//typealias VisibilityFilter = (MvPath, Set) -> VisibilityStatus +//typealias VisibilityFilter = (MvElement, Lazy?) -> VisibilityStatus + +fun ScopeEntry.getVisibilityStatusFrom(methodOrPath: MvMethodOrPath): VisibilityStatus = + if (this is ScopeEntryWithVisibility) { + visibilityFilter.filter(methodOrPath, this.namespaces) + } else { + Visible + } + + +fun ScopeEntry.isVisibleFrom(context: MvPath): Boolean = getVisibilityStatusFrom(context) == Visible + +enum class VisibilityStatus { + Visible, + Invisible, +} + +data class ScopeEntryWithVisibility( + override val name: String, + override val element: MvNamedElement, + override val namespaces: Set, + /** Given a [MvElement] (usually [MvPath]) checks if this item is visible in `containingMod` of that element */ + val visibilityFilter: VisibilityFilter, +// override val subst: Substitution = emptySubstitution, +): ScopeEntry { + override fun doCopyWithNs(namespaces: Set): ScopeEntry = copy(namespaces = namespaces) +} + +fun RsResolveProcessor.process( + name: String, + e: MvNamedElement, + namespaces: Set, + visibilityFilter: VisibilityFilter +): Boolean = process(ScopeEntryWithVisibility(name, e, namespaces, visibilityFilter)) + +fun RsResolveProcessor.processAllItems( + namespaces: Set, + vararg collections: Iterable, +): Boolean { + return sequenceOf(*collections).flatten().any { e -> + val name = e.name ?: return false + val visibilityFilter = e.visInfo().createFilter() + process(ScopeEntryWithVisibility(name, e, namespaces, visibilityFilter)) + } +} + +fun RsResolveProcessor.process( + name: String, +// namespaces: Set, + e: MvNamedElement +): Boolean = + process(SimpleScopeEntry(name, e, Namespace.none())) +// process(ScopeEntry(name, e, namespaces)) + +inline fun RsResolveProcessor.lazy( + name: String, +// namespaces: Set, + e: () -> MvNamedElement? +): Boolean { + if (!acceptsName(name)) return false + val element = e() ?: return false + return process(name, element) +} + +fun RsResolveProcessor.process( + e: MvNamedElement, +// namespaces: Set +): Boolean { + val name = e.name ?: return false + return process(name, e) +} + +fun RsResolveProcessor.processAll( + elements: List, +// namespaces: Set +): Boolean { + return elements.any { process(it) } +} + +fun RsResolveProcessor.processAll( + vararg collections: Iterable, +// namespaces: Set +): Boolean { + return sequenceOf(*collections).flatten().any { process(it) } +} + +fun processAllScopeEntries(elements: List, processor: RsResolveProcessor): Boolean { + return elements.any { processor.process(it) } +} + + + + diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveFQModuleReferenceImpl.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveFQModuleReferenceImpl.kt deleted file mode 100644 index d77894cd5..000000000 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveFQModuleReferenceImpl.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.move.lang.core.resolve.ref - -import org.move.lang.core.psi.MvFQModuleRef -import org.move.lang.core.psi.MvModule -import org.move.lang.core.psi.MvNamedElement -import org.move.lang.core.resolve.processFQModuleRef -import org.move.stdext.wrapWithList - -interface MvFQModuleReference : MvPolyVariantReference - -class MvFQModuleReferenceImpl( - element: MvFQModuleRef, -) : MvPolyVariantReferenceCached(element), MvFQModuleReference { - - override val cacheDependency: ResolveCacheDependency get() = ResolveCacheDependency.LOCAL_AND_RUST_STRUCTURE - - override fun multiResolveInner(): List { - val referenceName = element.referenceName ?: return emptyList() - var resolved: MvModule? = null - processFQModuleRef(element, referenceName) { - if (it.name == referenceName) { - resolved = it.element - true - } else { - false - } - } - return resolved.wrapWithList() - } -} diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveModuleReferenceImpl.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveModuleReferenceImpl.kt deleted file mode 100644 index 81eacd948..000000000 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveModuleReferenceImpl.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.move.lang.core.resolve.ref - -import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.isSelfModuleRef -import org.move.lang.core.psi.ext.itemUseSpeck -import org.move.lang.core.resolve.resolveLocalItem -import org.move.stdext.wrapWithList - -class MvModuleReferenceImpl( - element: MvModuleRef, -): MvPolyVariantReferenceCached(element) { - - override fun multiResolveInner(): List { - if (element.isSelfModuleRef) return element.containingModule.wrapWithList() - - check(element !is MvFQModuleRef) { - "That element has different reference item" - } - - val resolved = resolveLocalItem(element, setOf(Namespace.MODULE)).firstOrNull() - if (resolved is MvUseAlias) { - return resolved.wrapWithList() - } - val moduleRef = when { - resolved is MvUseItem && resolved.text == "Self" -> resolved.itemUseSpeck.fqModuleRef - resolved is MvModuleUseSpeck -> resolved.fqModuleRef - else -> return emptyList() - } - return moduleRef?.reference?.resolve().wrapWithList() - } -} diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/MovePathReferenceImpl.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/MovePathReferenceImpl.kt deleted file mode 100644 index 367110ef7..000000000 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/MovePathReferenceImpl.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.move.lang.core.resolve.ref - -import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.isSelfModuleRef -import org.move.lang.core.psi.ext.isUpdateFieldArg2 -import org.move.lang.core.psi.ext.itemUseSpeck -import org.move.lang.core.psi.ext.namespaces -import org.move.lang.core.resolve.* - -class MvPathReferenceImpl( - element: MvPath, -): MvPolyVariantReferenceCached(element), MvPathReference { - - override val cacheDependency: ResolveCacheDependency get() = ResolveCacheDependency.LOCAL_AND_RUST_STRUCTURE - - override fun multiResolveInner(): List { - val pathNamespaces = element.namespaces() - val vs = Visibility.visibilityScopesForElement(element) - val contextScopeInfo = - ContextScopeInfo( - refItemScopes = element.refItemScopes, - letStmtScope = element.letStmtScope, - ) - - val refName = element.referenceName ?: return emptyList() - val moduleRef = element.moduleRef - // first, see whether it's a fully qualified path (ADDRESS::MODULE::NAME) and try to resolve those - if (moduleRef is MvFQModuleRef) { - // TODO: can be replaced with index call - val module = moduleRef.reference?.resolve() as? MvModule ?: return emptyList() - return resolveModuleItem(module, refName, pathNamespaces, vs, contextScopeInfo) - } - // second, - // if it's MODULE::NAME -> resolve MODULE into corresponding FQModuleRef using imports - if (moduleRef != null) { - if (moduleRef.isSelfModuleRef) { - val containingModule = moduleRef.containingModule ?: return emptyList() - return resolveModuleItem( - containingModule, - refName, - pathNamespaces, - setOf(Visibility.Internal), - contextScopeInfo - ) - } - val useSpeckFQModuleRef = resolveIntoFQModuleRefInUseSpeck(moduleRef) ?: return emptyList() - val useSpeckModule = - useSpeckFQModuleRef.reference?.resolve() as? MvModule ?: return emptyList() - return resolveModuleItem(useSpeckModule, refName, pathNamespaces, vs, contextScopeInfo) - } else { - // if it's NAME - // special case second argument of update_field function in specs - if (element.isUpdateFieldArg2) return emptyList() - - // try local names - val item = resolveLocalItem(element, pathNamespaces).firstOrNull() ?: return emptyList() - // local name -> return - return when (item) { - // item import - is MvUseItem -> { - // find corresponding FQModuleRef from imports and resolve -// // TODO: index call - val useSpeckModule = - item.itemUseSpeck.fqModuleRef.reference?.resolve() as? MvModule - ?: return emptyList() - return resolveModuleItem(useSpeckModule, refName, pathNamespaces, vs, contextScopeInfo) - } - // module import - is MvModuleUseSpeck -> { - val module = item.fqModuleRef?.reference?.resolve() as? MvModule - return listOfNotNull(module) - } - // local item - else -> listOf(item) - } - } - } -} diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReference.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReference.kt index dfa94ef3d..75bda53ff 100644 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReference.kt +++ b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReference.kt @@ -4,8 +4,7 @@ import com.intellij.psi.PsiPolyVariantReference import org.move.lang.core.psi.MvElement import org.move.lang.core.psi.MvNamedElement import org.move.lang.core.psi.MvUseAlias -import org.move.lang.core.psi.ext.moduleUseSpeck -import org.move.lang.core.psi.ext.useItem +import org.move.lang.core.psi.ext.parentUseSpeck interface MvPolyVariantReference : PsiPolyVariantReference { @@ -13,14 +12,11 @@ interface MvPolyVariantReference : PsiPolyVariantReference { override fun resolve(): MvNamedElement? - fun resolveWithAliases(): MvNamedElement? { + fun resolveFollowingAliases(): MvNamedElement? { val resolved = this.resolve() if (resolved is MvUseAlias) { - val useItem = resolved.useItem - if (useItem != null) { - return useItem.reference.resolve() - } - return resolved.moduleUseSpeck?.fqModuleRef?.reference?.resolve() + val aliasedPath = resolved.parentUseSpeck.path + return aliasedPath.reference?.resolve() } return resolved } @@ -29,6 +25,17 @@ interface MvPolyVariantReference : PsiPolyVariantReference { } interface MvPathReference : MvPolyVariantReference { -// fun advancedResolve(): BoundElement? = -// resolveWithAliases()?.let { BoundElement(it) } + +// fun multiResolveIfVisible(): List = multiResolve() +// +// fun rawMultiResolve(): List> = +// multiResolve().map { RsPathResolveResult(it, isVisible = true) } +} + +interface MvPath2Reference: MvPolyVariantReference { +// fun multiResolveIfVisible(): List = multiResolve() + +// fun rawMultiResolve(): List> +// multiResolve().map { RsPathResolveResult(it, isVisible = true) } + } diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceBase.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceBase.kt index 1500fc1f8..6e9b46c1c 100644 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceBase.kt +++ b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceBase.kt @@ -39,7 +39,7 @@ abstract class MvPolyVariantReferenceBase(element: T): return element } - final override fun resolve(): MvNamedElement? = super.resolve() as? MvNamedElement + override fun resolve(): MvNamedElement? = super.resolve() as? MvNamedElement override fun multiResolve(incompleteCode: Boolean): Array = multiResolve().map { PsiElementResolveResult(it) }.toTypedArray() diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceElement.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceElement.kt index d23a1b321..4e2d0f9d9 100644 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceElement.kt +++ b/src/main/kotlin/org/move/lang/core/resolve/ref/MoveReferenceElement.kt @@ -57,10 +57,14 @@ interface MvPathReferenceElement : MvReferenceElement { override fun getReference(): MvPathReference? } -interface MvFQModuleReferenceElement : MvReferenceElement { - override fun getReference(): MvFQModuleReference? +interface MvNameAccessChainReferenceElement : MvReferenceElement { + override fun getReference(): MvPath2Reference? } +//interface MvFQModuleReferenceElement : MvReferenceElement { +// override fun getReference(): MvFQModuleReference? +//} + interface MvStructPatFieldReferenceElement : MvMandatoryReferenceElement interface MvStructFieldLitReferenceElement : MvMandatoryReferenceElement diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/Namespace.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/Namespace.kt index d6de0eb66..a92502c39 100644 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/Namespace.kt +++ b/src/main/kotlin/org/move/lang/core/resolve/ref/Namespace.kt @@ -1,46 +1,18 @@ package org.move.lang.core.resolve.ref -import com.intellij.psi.SmartPsiElementPointer import org.move.cli.MovePackage -import org.move.cli.containingMovePackage -import org.move.lang.core.psi.MvElement import org.move.lang.core.psi.MvModule -import org.move.lang.core.psi.containingFunction -import org.move.lang.core.psi.containingModule -import org.move.lang.core.psi.ext.FunctionVisibility -import org.move.lang.core.psi.ext.asSmartPointer -import org.move.lang.core.psi.ext.visibility +import java.util.* -sealed class Visibility { - object Public : Visibility() - object PublicScript : Visibility() - class PublicFriend(val currentModule: SmartPsiElementPointer) : Visibility() - data class PublicPackage(val originPackage: MovePackage) : Visibility() - object Internal : Visibility() - - companion object { - fun local(): Set = setOf(Public, Internal) - fun none(): Set = setOf() - - fun visibilityScopesForElement(element: MvElement): Set { - val vs = mutableSetOf(Public) - val containingModule = element.containingModule - if (containingModule != null) { - vs.add(PublicFriend(containingModule.asSmartPointer())) - } - val containingFun = element.containingFunction - if (containingModule == null - || (containingFun?.visibility == FunctionVisibility.PUBLIC_SCRIPT) - ) { - vs.add(PublicScript) - } - val containingMovePackage = element.containingMovePackage - if (containingMovePackage != null) { - vs.add(PublicPackage(containingMovePackage)) - } - return vs - } +sealed class Visibility2 { + data object Public: Visibility2() + data object Private: Visibility2() + sealed class Restricted: Visibility2() { + class Friend(val friendModules: Lazy>): Restricted() + class Package(val originPackage: MovePackage): Restricted() + data object Script: Restricted() } + } enum class Namespace { @@ -53,9 +25,11 @@ enum class Namespace { companion object { fun all(): Set { - return setOf(NAME, FUNCTION, TYPE, SCHEMA, MODULE, CONST) + return EnumSet.of(NAME, FUNCTION, TYPE, SCHEMA, MODULE, CONST) } + fun items(): Set = EnumSet.of(NAME, FUNCTION, TYPE, SCHEMA, CONST) + fun none(): Set = setOf() } } diff --git a/src/main/kotlin/org/move/lang/core/resolve/ref/field_ref.kt b/src/main/kotlin/org/move/lang/core/resolve/ref/field_ref.kt deleted file mode 100644 index 22f17531a..000000000 --- a/src/main/kotlin/org/move/lang/core/resolve/ref/field_ref.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.move.lang.core.resolve.ref - -import org.move.lang.core.psi.MvNamedElement -import org.move.lang.core.psi.MvStruct -import org.move.lang.core.psi.MvStructLitField -import org.move.lang.core.psi.MvStructPatField -import org.move.lang.core.psi.ext.* -import org.move.lang.core.resolve.resolveLocalItem - -interface MvStructRefField: MvReferenceElement - -class MvStructRefFieldReferenceImpl( - element: MvStructRefField -) : MvPolyVariantReferenceCached(element) { - - override fun multiResolveInner() = resolveIntoStructField(element) -} - -class MvStructLitShorthandFieldReferenceImpl( - element: MvStructLitField, -) : MvPolyVariantReferenceCached(element) { - - override fun multiResolveInner(): List { - return listOf( - resolveIntoStructField(element), - resolveLocalItem(element, setOf(Namespace.NAME)) - ).flatten() - } -} - -class MvStructPatShorthandFieldReferenceImpl( - element: MvStructPatField -) : MvPolyVariantReferenceCached(element) { - - override fun multiResolveInner(): List = resolveIntoStructField(element) -} - -private val MvStructRefField.maybeStruct: MvStruct? get() { - return when (this) { - is MvStructPatField -> this.structPat.structItem - is MvStructLitField -> this.structLitExpr.path.maybeStruct - else -> null - } -} - -private fun resolveIntoStructField(element: MvStructRefField): List { - val structItem = element.maybeStruct ?: return emptyList() - val referenceName = element.referenceName - return structItem.fields - .filter { it.name == referenceName } -} diff --git a/src/main/kotlin/org/move/lang/core/resolve2/ItemResolution.kt b/src/main/kotlin/org/move/lang/core/resolve2/ItemResolution.kt new file mode 100644 index 000000000..5a6c1e804 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/ItemResolution.kt @@ -0,0 +1,106 @@ +package org.move.lang.core.resolve2 + +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.* +import org.move.lang.core.resolve.* +import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.types.infer.foldTyTypeParameterWith +import org.move.lang.core.types.ty.Ty +import org.move.lang.core.types.ty.TyInfer +import org.move.lang.core.types.ty.TyReference +import org.move.lang.moveProject +import java.util.* + +val MvNamedElement.namespaces: Set + get() = when (this) { + is MvFunction -> EnumSet.of(Namespace.FUNCTION) + is MvStruct -> EnumSet.of(Namespace.TYPE) + is MvConst -> EnumSet.of(Namespace.NAME) +// is MvConst -> EnumSet.of(Namespace.CONST) + is MvSchema -> EnumSet.of(Namespace.SCHEMA) + is MvModule -> EnumSet.of(Namespace.MODULE) + else -> EnumSet.of(Namespace.NAME) + } + +val MvNamedElement.namespace + get() = when (this) { + is MvFunctionLike -> Namespace.FUNCTION + is MvStruct -> Namespace.TYPE + is MvConst -> Namespace.NAME + is MvSchema -> Namespace.SCHEMA + is MvModule -> Namespace.MODULE + is MvGlobalVariableStmt -> Namespace.NAME + else -> error("when should be exhaustive, $this is not covered") + } + +fun processMethodResolveVariants( + methodOrField: MvMethodOrField, + receiverTy: Ty, + msl: Boolean, + processor: RsResolveProcessor +): Boolean { + val moveProject = methodOrField.moveProject ?: return false + val itemModule = receiverTy.itemModule(moveProject) ?: return false + return processor + .wrapWithFilter { e -> + val function = e.element as? MvFunction ?: return@wrapWithFilter false + val selfTy = function.selfParamTy(msl) ?: return@wrapWithFilter false + // need to use TyVar here, loweredType() erases them + val selfTyWithTyVars = + selfTy.foldTyTypeParameterWith { tp -> TyInfer.TyVar(tp) } + TyReference.isCompatibleWithAutoborrow(receiverTy, selfTyWithTyVars, msl) + } + .processAllItems(setOf(Namespace.FUNCTION), itemModule.allNonTestFunctions()) +} + +fun processItemDeclarations( + itemsOwner: MvItemsOwner, + ns: Set, + processor: RsResolveProcessor +): Boolean { + + // 1. loop over all items in module (item is anything accessible with MODULE:: ) + // 2. for every item, use it's .visibility to create VisibilityFilter, even it's just a { false } + val items = itemsOwner.itemElements + + (itemsOwner as? MvModuleBlock)?.module?.innerSpecItems.orEmpty() + + (itemsOwner as? MvModuleBlock)?.module?.let { getItemsFromModuleSpecs(it, ns) }.orEmpty() + for (item in items) { + val name = item.name ?: continue + + val namespace = item.namespace + if (namespace !in ns) continue + + val visibilityFilter = item.visInfo().createFilter() + if (processor.process(name, item, EnumSet.of(namespace), visibilityFilter)) return true + } + + return false +} + +fun getItemsFromModuleSpecs(module: MvModule, ns: Set): List { + val c = mutableListOf() + processItemsFromModuleSpecs(module, ns, createProcessor { c.add(it.element as MvItemElement) }) + return c +} + +fun processItemsFromModuleSpecs( + module: MvModule, + namespaces: Set, + processor: RsResolveProcessor, +): Boolean { + for (namespace in namespaces) { + for (moduleSpec in module.allModuleSpecs()) { + val matched = when (namespace) { + Namespace.FUNCTION -> + processor.processAll( + moduleSpec.specFunctions(), + moduleSpec.specInlineFunctions(), + ) + Namespace.SCHEMA -> processor.processAll(moduleSpec.schemas()) + else -> false + } + if (matched) return true + } + } + return false +} diff --git a/src/main/kotlin/org/move/lang/core/resolve/LexicalDeclarations.kt b/src/main/kotlin/org/move/lang/core/resolve2/LexicalDeclarations2.kt similarity index 52% rename from src/main/kotlin/org/move/lang/core/resolve/LexicalDeclarations.kt rename to src/main/kotlin/org/move/lang/core/resolve2/LexicalDeclarations2.kt index d1be1e506..716a5fa03 100644 --- a/src/main/kotlin/org/move/lang/core/resolve/LexicalDeclarations.kt +++ b/src/main/kotlin/org/move/lang/core/resolve2/LexicalDeclarations2.kt @@ -1,57 +1,42 @@ -package org.move.lang.core.resolve +package org.move.lang.core.resolve2 import com.intellij.psi.util.PsiTreeUtil +import org.move.ide.inspections.imports.usageScope import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* +import org.move.lang.core.resolve.* +import org.move.lang.core.resolve.LetStmtScope.* import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.resolve2.ref.ResolutionContext +import org.move.lang.core.resolve2.util.forEachLeafSpeck fun processItemsInScope( scope: MvElement, cameFrom: MvElement, - namespaces: Set, - contextScopeInfo: ContextScopeInfo, - processor: MatchingProcessor, + ns: Set, + ctx: ResolutionContext, + processor: RsResolveProcessor, ): Boolean { - for (namespace in namespaces) { + for (namespace in ns) { val stop = when (namespace) { - - Namespace.CONST -> { - val found = when (scope) { - is MvModuleBlock -> { - val module = scope.parent as MvModule - processor.matchAll( - contextScopeInfo, - module.consts(), - ) - } - else -> false - } - if (!found) { - if (scope is MvImportsOwner) { - if (processor.matchAll(contextScopeInfo, scope.allUseItems())) return true - } - } - found - } - Namespace.NAME -> { val found = when (scope) { is MvModuleBlock -> { val module = scope.parent as MvModule - processor.matchAll( - contextScopeInfo, + processor.processAllItems( + ns, module.structs(), module.consts(), ) } - is MvModuleSpecBlock -> processor.matchAll(contextScopeInfo, scope.schemaList) - is MvScript -> processor.matchAll(contextScopeInfo, scope.consts()) - is MvFunctionLike -> processor.matchAll(contextScopeInfo, scope.allParamsAsBindings) - is MvLambdaExpr -> processor.matchAll(contextScopeInfo, scope.bindingPatList) + is MvModuleSpecBlock -> processor.processAllItems(ns, scope.schemaList) + is MvScript -> processor.processAllItems(ns, scope.consts()) + is MvFunctionLike -> processor.processAll(scope.allParamsAsBindings) + is MvLambdaExpr -> processor.processAll(scope.bindingPatList) is MvForExpr -> { val iterConditionBindingPat = scope.forIterCondition?.bindingPat if (iterConditionBindingPat != null) { - processor.match(iterConditionBindingPat) + processor.process(iterConditionBindingPat) } else { false } @@ -60,18 +45,18 @@ fun processItemsInScope( val item = scope.item when (item) { is MvFunction -> { - processor.matchAll( - contextScopeInfo, + processor.processAll( +// contextScopeInfo, item.valueParamsAsBindings, item.specResultParameters.map { it.bindingPat }, ) } - is MvStruct -> processor.matchAll(contextScopeInfo, item.fields) + is MvStruct -> processor.processAll(item.fields) else -> false } } - is MvSchema -> processor.matchAll(contextScopeInfo, scope.fieldBindings) - is MvQuantBindingsOwner -> processor.matchAll(contextScopeInfo, scope.bindings) + is MvSchema -> processor.processAll(scope.fieldBindings) + is MvQuantBindingsOwner -> processor.processAll(scope.bindings) is MvCodeBlock, is MvSpecCodeBlock -> { val visibleLetStmts = when (scope) { @@ -86,14 +71,16 @@ fun processItemsInScope( } } is MvSpecCodeBlock -> { - when (contextScopeInfo.letStmtScope) { - LetStmtScope.EXPR_STMT -> scope.allLetStmts - LetStmtScope.LET_STMT, LetStmtScope.LET_POST_STMT -> { - val letDecls = if (contextScopeInfo.letStmtScope == LetStmtScope.LET_POST_STMT) { - scope.allLetStmts - } else { - scope.letStmts(false) - } + val letStmtScope = ctx.element.letStmtScope + when (letStmtScope) { + EXPR_STMT -> scope.allLetStmts + LET_STMT, LET_POST_STMT -> { + val letDecls = + if (letStmtScope == LET_POST_STMT) { + scope.allLetStmts + } else { + scope.letStmts(false) + } letDecls // drops all let-statements after the current position .filter { it.cameBefore(cameFrom) } @@ -103,7 +90,7 @@ fun processItemsInScope( && !PsiTreeUtil.isAncestor(cameFrom, it, true) } } - LetStmtScope.NONE -> emptyList() + NONE -> emptyList() } } else -> error("unreachable") @@ -115,15 +102,18 @@ fun processItemsInScope( // skip shadowed (already visited) elements val visited = mutableSetOf() - val processorWithShadowing = MatchingProcessor { entry -> - ((entry.name !in visited) - && processor.match(entry).also { visited += entry.name }) + val shadowingProcessor = processor.wrapWithFilter { + val isVisited = it.name in visited + if (!isVisited) { + visited += it.name + } + !isVisited } - var found = processorWithShadowing.matchAll(contextScopeInfo, namedElements) + var found = shadowingProcessor.processAll(namedElements) if (!found && scope is MvSpecCodeBlock) { - // if inside SpecCodeBlock, match also with builtin spec consts and global variables - found = processorWithShadowing.matchAll( - contextScopeInfo, + // if inside SpecCodeBlock, process also with builtin spec consts and global variables + found = shadowingProcessor.processAllItems( + ns, scope.builtinSpecConsts(), scope.globalVariables() ) @@ -132,31 +122,19 @@ fun processItemsInScope( } else -> false } - if (!found) { - if (scope is MvImportsOwner) { - if (processor.matchAll(contextScopeInfo, scope.allUseItems())) return true - } - } found } Namespace.FUNCTION -> { val found = when (scope) { is MvModuleBlock -> { val module = scope.parent as MvModule - val specFunctions = if (contextScopeInfo.isMslScope) { + val specFunctions = listOf(module.specFunctions(), module.builtinSpecFunctions()).flatten() - } else { - emptyList() - } - val specInlineFunctions = if (contextScopeInfo.isMslScope) { - module.moduleItemSpecs().flatMap { it.specInlineFunctions() } - } else { - emptyList() - } - processor.matchAll( - contextScopeInfo, - module.allNonTestFunctions(), + val specInlineFunctions = module.moduleItemSpecs().flatMap { it.specInlineFunctions() } + processor.processAllItems( + ns, module.builtinFunctions(), + module.allNonTestFunctions(), specFunctions, specInlineFunctions ) @@ -164,95 +142,113 @@ fun processItemsInScope( is MvModuleSpecBlock -> { val specFunctions = scope.specFunctionList val specInlineFunctions = scope.moduleItemSpecList.flatMap { it.specInlineFunctions() } - processor.matchAll( - contextScopeInfo, + processor.processAllItems( + ns, specFunctions, specInlineFunctions ) } - is MvFunctionLike -> processor.matchAll(contextScopeInfo, scope.lambdaParamsAsBindings) - is MvLambdaExpr -> processor.matchAll(contextScopeInfo, scope.bindingPatList) + is MvFunctionLike -> processor.processAll(scope.lambdaParamsAsBindings) + is MvLambdaExpr -> processor.processAll(scope.bindingPatList) is MvItemSpec -> { val item = scope.item when (item) { - is MvFunction -> processor.matchAll(contextScopeInfo, item.lambdaParamsAsBindings) + is MvFunction -> processor.processAll(item.lambdaParamsAsBindings) else -> false } } is MvSpecCodeBlock -> { val inlineFunctions = scope.specInlineFunctions().asReversed() - return processor.matchAll(contextScopeInfo, inlineFunctions) + processor.processAllItems(ns, inlineFunctions) } else -> false } - if (!found) { - if (scope is MvImportsOwner) { - if (processor.matchAll(contextScopeInfo, scope.allUseItems())) return true - } - } found } Namespace.TYPE -> { if (scope is MvTypeParametersOwner) { - if (processor.matchAll(contextScopeInfo, scope.typeParameters)) return true + if (processor.processAll(scope.typeParameters)) return true } val found = when (scope) { is MvItemSpec -> { val funcItem = scope.funcItem if (funcItem != null) { - processor.matchAll(contextScopeInfo, funcItem.typeParameters) + processor.processAll(funcItem.typeParameters) } else { false } } is MvModuleBlock -> { val module = scope.parent as MvModule - processor.matchAll( - contextScopeInfo, - scope.allUseItems(), - module.structs() - ) + processor.processAllItems(ns, module.structs()) } is MvApplySchemaStmt -> { val toPatterns = scope.applyTo?.functionPatternList.orEmpty() val patternTypeParams = toPatterns.flatMap { it.typeParameterList?.typeParameterList.orEmpty() } - processor.matchAll(contextScopeInfo, patternTypeParams) + processor.processAll(patternTypeParams) } else -> false } - if (!found) { - if (scope is MvImportsOwner) { - if (processor.matchAll(contextScopeInfo, scope.allUseItems())) return true - } - } found } Namespace.SCHEMA -> when (scope) { - is MvModuleBlock -> processor.matchAll( - contextScopeInfo, - scope.allUseItems(), - scope.schemaList - ) - is MvModuleSpecBlock -> processor.matchAll( - contextScopeInfo, - scope.allUseItems(), - scope.schemaList, - scope.specFunctionList - ) + is MvModuleBlock -> processor.processAllItems(ns, scope.schemaList) + is MvModuleSpecBlock -> processor.processAllItems(ns, scope.schemaList, scope.specFunctionList) else -> false } + else -> false + } + if (stop) return true + } - Namespace.MODULE -> when (scope) { - is MvImportsOwner -> - processor.matchAll(contextScopeInfo, scope.moduleUseItems()) - else -> false + if (scope is MvItemsOwner) { + if (scope.processUseSpeckElements(ns, processor)) return true + } + + return false +} + +private fun MvItemsOwner.processUseSpeckElements(ns: Set, processor: RsResolveProcessor): Boolean { + var stop = false + for (useStmt in this.useStmtList) { + useStmt.forEachLeafSpeck { path, alias -> + val name = if (alias != null) { + alias.name ?: return@forEachLeafSpeck false + } else { + var n = path.referenceName ?: return@forEachLeafSpeck false + // 0x1::m::Self -> 0x1::m + if (n == "Self") { + n = path.qualifier?.referenceName ?: return@forEachLeafSpeck false + } + n } + val namedElement = path.reference?.resolve() + if (namedElement == null) { + if (alias != null) { + // aliased element cannot be resolved, but alias itself is valid, resolve to it + if (processor.process(name, alias)) return@forEachLeafSpeck true + } + // todo: should it be resolved to import anyway? + return@forEachLeafSpeck false + } + + val element = alias ?: namedElement + val namespace = namedElement.namespace + val useSpeckUsageScope = path.usageScope + val visibilityFilter = + namedElement.visInfo(adjustScope = useSpeckUsageScope).createFilter() + + if (namespace in ns && processor.process(name, element, ns, visibilityFilter)) { + stop = true + return@forEachLeafSpeck true + } + false } if (stop) return true } - return false + return stop } diff --git a/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt b/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt new file mode 100644 index 000000000..1e71eaf9e --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt @@ -0,0 +1,183 @@ +package org.move.lang.core.resolve2 + +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.* +import org.move.lang.core.resolve.* +import org.move.lang.core.resolve.ref.MvMandatoryReferenceElement +import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.resolve.ref.Namespace.MODULE +import org.move.lang.core.resolve2.ref.ResolutionContext +import org.move.lang.core.types.Address +import org.move.lang.core.types.Address.Named +import org.move.lang.core.types.address +import org.move.lang.index.MvModuleIndex + +fun resolveBindingForFieldShorthand( + element: MvMandatoryReferenceElement, +): List { + return collectResolveVariants(element.referenceName) { + processNestedScopesUpwards( + element, + setOf(Namespace.NAME), + ResolutionContext(element, isCompletion = false), + it + ) + } +} + +fun processNestedScopesUpwards( + scopeStart: MvElement, + ns: Set, + ctx: ResolutionContext, + processor: RsResolveProcessor +): Boolean { + val prevScope = hashMapOf>() + return walkUpThroughScopes( + scopeStart, + stopAfter = { it is MvModule } + ) { cameFrom, scope -> + processWithShadowingAndUpdateScope(prevScope, ns, processor) { shadowingProcessor -> + processItemsInScope( + scope, cameFrom, ns, ctx, shadowingProcessor + ) + } + } +} + +fun processModulePathResolveVariants( + ctx: ResolutionContext, + address: Address, + processor: RsResolveProcessor, +): Boolean { + // if no project, cannot use the index + val moveProject = ctx.moveProject + if (moveProject == null) return false + + val project = ctx.element.project + val searchScope = moveProject.searchScope() + + val addrProcessor = processor.wrapWithFilter { e -> + val candidate = e.element as? MvModule ?: return@wrapWithFilter false + val candidateAddress = candidate.address(moveProject) + val sameValues = Address.equals(address, candidateAddress) + + if (ctx.isCompletion && sameValues) { + // compare named addresses by name in case of the same values for the completion + if (address is Named && candidateAddress is Named && address.name != candidateAddress.name) + return@wrapWithFilter false + } + + sameValues + } + + val targetNames = addrProcessor.names + if (targetNames == null) { + // completion + val moduleNames = MvModuleIndex.getAllModuleNames(project) + moduleNames.forEach { moduleName -> + val modules = MvModuleIndex.getModulesByName(project, moduleName, searchScope) + for (module in modules) { + if (addrProcessor.process(moduleName, module)) return true + } + } + return false + } + + var stop = false + for (targetModuleName in targetNames) { + MvModuleIndex + .processModulesByName(project, targetModuleName, searchScope) { + val module = it + val visFilter = module.visInfo().createFilter() + stop = addrProcessor.process(targetModuleName, module, setOf(MODULE), visFilter) + // true to continue processing, if .process does not find anything, it returns false + !stop + } + if (stop) return true + } + + return false +} + +inline fun processWithShadowingAndUpdateScope( + prevScope: MutableMap>, + ns: Set, + processor: RsResolveProcessor, + f: (RsResolveProcessor) -> Boolean +): Boolean { + val currScope = mutableMapOf>() + val shadowingProcessor = processor.wrapWithShadowingProcessorAndUpdateScope(prevScope, currScope, ns) + return try { + f(shadowingProcessor) + } finally { + prevScope.putAll(currScope) + } +} + +inline fun processWithShadowing( + prevScope: Map>, + ns: Set, + processor: RsResolveProcessor, + f: (RsResolveProcessor) -> Boolean +): Boolean { + val shadowingProcessor = processor.wrapWithShadowingProcessor(prevScope, ns) + return f(shadowingProcessor) +} + + +fun walkUpThroughScopes( + start: MvElement, + stopAfter: (MvElement) -> Boolean, + handleScope: (cameFrom: MvElement, scope: MvElement) -> Boolean, +): Boolean { + var cameFrom = start + var scope = start.context as MvElement? + while (scope != null) { + if (handleScope(cameFrom, scope)) return true + + // walk all items in original module block + if (scope is MvModuleBlock) { + // handle spec module {} + if (handleModuleItemSpecsInBlock(cameFrom, scope, handleScope)) return true + // walk over all spec modules + for (moduleSpec in scope.module.allModuleSpecs()) { + val moduleSpecBlock = moduleSpec.moduleSpecBlock ?: continue + if (handleScope(cameFrom, moduleSpecBlock)) return true + if (handleModuleItemSpecsInBlock(cameFrom, moduleSpecBlock, handleScope)) return true + } + } + + if (scope is MvModuleSpecBlock) { + val moduleBlock = scope.moduleSpec.moduleItem?.moduleBlock + if (moduleBlock != null) { + cameFrom = scope + scope = moduleBlock + continue + } + } + + if (stopAfter(scope)) break + + cameFrom = scope + scope = scope.context as? MvElement + } + + return false +} + +private fun handleModuleItemSpecsInBlock( + cameFrom: MvElement, + block: MvElement, + handleScope: (cameFrom: MvElement, scope: MvElement) -> Boolean +): Boolean { + val moduleItemSpecs = when (block) { + is MvModuleBlock -> block.moduleItemSpecList + is MvModuleSpecBlock -> block.moduleItemSpecList + else -> emptyList() + } + for (moduleItemSpec in moduleItemSpecs.filter { it != cameFrom }) { + val itemSpecBlock = moduleItemSpec.itemSpecBlock ?: continue + if (handleScope(cameFrom, itemSpecBlock)) return true + } + return false +} diff --git a/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt b/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt new file mode 100644 index 000000000..4366e9468 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt @@ -0,0 +1,136 @@ +package org.move.lang.core.resolve2 + +import org.move.lang.core.psi.MvPath +import org.move.lang.core.psi.MvUseGroup +import org.move.lang.core.psi.MvUseSpeck +import org.move.lang.core.psi.MvUseStmt +import org.move.lang.core.psi.ext.allowedNamespaces +import org.move.lang.core.psi.ext.ancestorStrict +import org.move.lang.core.psi.ext.isUseSpeck +import org.move.lang.core.psi.ext.useSpeck +import org.move.lang.core.resolve.ref.Namespace +import org.move.lang.core.types.Address +import org.move.lang.moveProject + +sealed class PathKind { + // aptos_std:: where aptos_std is a existing named address in a project + data class NamedAddress(val address: Address.Named): PathKind() + + // 0x1:: + data class ValueAddress(val address: Address.Value): PathKind() + + // foo + data class UnqualifiedPath(val ns: Set): PathKind() + + // any multi element path + sealed class QualifiedPath(val path: MvPath, val qualifier: MvPath, val ns: Set): PathKind() { + // `0x1:foo` or `aptos_framework::foo` (where aptos_framework is known named address) + class Module(path: MvPath, qualifier: MvPath, ns: Set, val address: Address): + QualifiedPath(path, qualifier, ns) + + // bar in foo::bar, where foo is not a named address + class ModuleItem(path: MvPath, qualifier: MvPath, ns: Set): + QualifiedPath(path, qualifier, ns) + + // bar in `0x1::foo::bar` or `aptos_std::foo::bar` (where aptos_std is known named address) + class FQModuleItem(path: MvPath, qualifier: MvPath, ns: Set): + QualifiedPath(path, qualifier, ns) + + // use 0x1::m::{item1}; + // ^ + class UseGroupItem(path: MvPath, qualifier: MvPath, ns: Set): + QualifiedPath(path, qualifier, ns) + } +} + +fun MvPath.pathKind(overwriteNs: Set? = null): PathKind { + val ns = overwriteNs ?: this.allowedNamespaces() + // [0x1::foo]::bar + // ^ qualifier + val qualifier = this.path + val moveProject = this.moveProject + + val useGroup = this.ancestorStrict() + if (useGroup != null) { + // use 0x1::m::{item} + // ^ + val useSpeckQualifier = (useGroup.parent as MvUseSpeck).path + return PathKind.QualifiedPath.UseGroupItem(this, useSpeckQualifier, ns) + } + + if (qualifier == null) { + // one-element path + + // if pathAddress exists, it means it has to be a value address + val pathAddress = this.pathAddress + if (pathAddress != null) { + return PathKind.ValueAddress(Address.Value(pathAddress.text)) + } + val referenceName = this.referenceName ?: error("if pathAddress is null, reference has to be non-null") + + // check whether it's a first element in use stmt, i.e. use [std]::module; + // ^ + val useSpeck = this.useSpeck + if (useSpeck != null && useSpeck.parent is MvUseStmt) { + // if so, local path expr is a named address + val namedAddress = moveProject?.getNamedAddressTestAware(referenceName) + if (namedAddress != null) { + return PathKind.NamedAddress(namedAddress) + } + // and it can be with null value if absent, still a named address + return PathKind.NamedAddress(Address.Named(referenceName, null, moveProject)) + } + + // check whether it's inside use group, then it cannot be named address +// if (this.useSpeck?.useGroup != null) { +// return PathKind.UnqualifiedPath(ns) +// } + + // outside use stmt context + if (moveProject != null) { + // try whether it's a named address + val namedAddress = moveProject.getNamedAddressTestAware(referenceName) + if (namedAddress != null) { + return PathKind.NamedAddress(namedAddress) + } + } + + // if it's not, then it just an unqualified path + return PathKind.UnqualifiedPath(ns) + } + + val qualifierOfQualifier = qualifier.path + + // two-element paths + if (qualifierOfQualifier == null) { + val qualifierPathAddress = qualifier.pathAddress + val qualifierItemName = qualifier.referenceName + when { + // 0x1::bar + // ^ + qualifierPathAddress != null -> { + val address = Address.Value(qualifierPathAddress.text) + return PathKind.QualifiedPath.Module(this, qualifier, ns, address) + } + // aptos_framework::bar + // ^ + moveProject != null && qualifierItemName != null -> { + val namedAddress = moveProject.getNamedAddressTestAware(qualifierItemName) + if (namedAddress != null) { + // known named address, can be module path + return PathKind.QualifiedPath.Module(this, qualifier, ns, namedAddress) + } + if (this.isUseSpeck) { + // use std::main where std is the unknown named address + val address = Address.Named(qualifierItemName, null, moveProject) + return PathKind.QualifiedPath.Module(this, qualifier, ns, address) + } + } + } + // module::name + return PathKind.QualifiedPath.ModuleItem(this, qualifier, ns) + } + + // three-element path + return PathKind.QualifiedPath.FQModuleItem(this, qualifier, ns) +} diff --git a/src/main/kotlin/org/move/lang/core/resolve2/Visibility2.kt b/src/main/kotlin/org/move/lang/core/resolve2/Visibility2.kt new file mode 100644 index 000000000..dbe37c32e --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/Visibility2.kt @@ -0,0 +1,115 @@ +package org.move.lang.core.resolve2 + +import org.move.cli.containingMovePackage +import org.move.cli.settings.moveSettings +import org.move.ide.inspections.imports.usageScope +import org.move.lang.core.psi.* +import org.move.lang.core.psi.NamedItemScope.MAIN +import org.move.lang.core.psi.ext.* +import org.move.lang.core.resolve.ModInfo +import org.move.lang.core.resolve.VisibilityFilter +import org.move.lang.core.resolve.VisibilityStatus.Invisible +import org.move.lang.core.resolve.VisibilityStatus.Visible +import org.move.lang.core.resolve.ref.Namespace.NAME +import org.move.lang.core.resolve.ref.Namespace.TYPE +import org.move.lang.core.resolve.ref.Visibility2 +import org.move.lang.core.resolve.ref.Visibility2.* + +data class ItemVisibilityInfo( + val item: MvNamedElement, + val usageScope: NamedItemScope, + val vis: Visibility2, +) + +fun MvNamedElement.visInfo(adjustScope: NamedItemScope = MAIN): ItemVisibilityInfo { + // todo: can be lazy + val itemUsageScope = this.itemScope.shrinkScope(adjustScope) + val visibility = (this as? MvVisibilityOwner)?.visibility2 ?: Public + return ItemVisibilityInfo(this, usageScope = itemUsageScope, vis = visibility) +} + +/** Creates filter which determines whether item with [this] visibility is visible from specific [ModInfo] */ +fun ItemVisibilityInfo.createFilter(): VisibilityFilter { + val (item, itemUsageScope, visibility) = this + return VisibilityFilter { element, namespaces -> + + // inside msl everything is visible + if (element.isMsl()) return@VisibilityFilter Visible + + // types are always visible, their correct usage is checked in a separate inspection + if (namespaces.contains(TYPE)) return@VisibilityFilter Visible + + // if inside MvAttrItem like abort_code= + val attrItem = element.ancestorStrict() + if (attrItem != null) return@VisibilityFilter Visible + + val pathUsageScope = element.usageScope + + val path = element as? MvPath + if (path != null) { + val useSpeck = path.useSpeck + if (useSpeck != null) { + // inside import, all visibilities except for private work + if (visibility !is Private) return@VisibilityFilter Visible + + // msl-only items are available from imports + if (item.isMslOnlyItem) return@VisibilityFilter Visible + + // consts are importable in tests + if (pathUsageScope.isTest && namespaces.contains(NAME)) return@VisibilityFilter Visible + } + } + + // #[test] functions cannot be used from non-imports + if (item is MvFunction && item.hasTestAttr) return@VisibilityFilter Invisible + + val itemModule = item.containingModule + // 0x0::builtins module items are always visible + if (itemModule != null && itemModule.isBuiltins) return@VisibilityFilter Visible + + // #[test_only] items in non-test-only scope + if (itemUsageScope != MAIN) { + // cannot be used everywhere, need to check for scope compatibility + if (itemUsageScope != pathUsageScope) return@VisibilityFilter Invisible + } + + // we're in non-msl scope at this point, msl only items aren't available + if (item is MslOnlyElement) return@VisibilityFilter Invisible + + val pathModule = element.containingModule + // local methods, Self::method - everything is visible + if (itemModule == pathModule) return@VisibilityFilter Visible + + when (visibility) { + is Restricted -> { + when (visibility) { + is Restricted.Friend -> { + if (pathModule != null) { + val friendModules = visibility.friendModules.value + if (friendModules.any { isModulesEqual(it, pathModule) }) return@VisibilityFilter Visible + } + Invisible + } + is Restricted.Script -> { + val containingFunction = element.containingFunction + if (containingFunction != null) { + if (containingFunction.isEntry || containingFunction.isPublicScript + ) return@VisibilityFilter Visible + } + if (element.containingScript != null) return@VisibilityFilter Visible + Invisible + } + is Restricted.Package -> { + if (!item.project.moveSettings.enablePublicPackage) { + return@VisibilityFilter Invisible + } + val pathPackage = element.containingMovePackage ?: return@VisibilityFilter Invisible + if (visibility.originPackage == pathPackage) Visible else Invisible + } + } + } + is Public -> Visible + is Private -> Invisible + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/resolve2/ref/Path2ReferenceImpl.kt b/src/main/kotlin/org/move/lang/core/resolve2/ref/Path2ReferenceImpl.kt new file mode 100644 index 000000000..9774c8bd2 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/ref/Path2ReferenceImpl.kt @@ -0,0 +1,157 @@ +package org.move.lang.core.resolve2.ref + +import com.intellij.psi.ResolveResult +import org.move.cli.MoveProject +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.MvMethodOrPath +import org.move.lang.core.psi.ext.useSpeck +import org.move.lang.core.resolve.* +import org.move.lang.core.resolve.ref.* +import org.move.lang.core.resolve2.* +import org.move.lang.core.resolve2.PathKind.NamedAddress +import org.move.lang.core.resolve2.PathKind.ValueAddress +import org.move.lang.moveProject +import kotlin.LazyThreadSafetyMode.NONE + +class Path2ReferenceImpl(element: MvPath): + MvPolyVariantReferenceBase(element), MvPath2Reference { + + override fun resolve(): MvNamedElement? = + rawMultiResolveIfVisible().singleOrNull()?.element as? MvNamedElement + + override fun multiResolve(): List = + rawMultiResolveIfVisible().mapNotNull { it.element as? MvNamedElement } + + override fun multiResolve(incompleteCode: Boolean): Array = + rawMultiResolve().toTypedArray() + +// fun multiResolveIfVisible(): List = +// rawMultiResolve().mapNotNull { +// if (!it.isVisible) return@mapNotNull null +// it.element +// } + + fun rawMultiResolveIfVisible(): List> = + rawMultiResolve().filter { it.isVisible } + + // fun rawMultiResolve(): List> = Resolver.invoke(this.element) + fun rawMultiResolve(): List> = rawCachedMultiResolve() + + private fun rawCachedMultiResolve(): List> { + val rawResult = MvResolveCache.getInstance(element.project) + .resolveWithCaching(element, ResolveCacheDependency.LOCAL_AND_RUST_STRUCTURE, Resolver) + return rawResult.orEmpty() + } + + private object Resolver: (MvElement) -> List> { + override fun invoke(path: MvElement): List> { + // should not really happen + if (path !is MvPath) return emptyList() + val resolutionCtx = ResolutionContext(path, isCompletion = false) + return resolvePath(resolutionCtx, path) + } + } +} + +fun processPathResolveVariants( + ctx: ResolutionContext, + pathKind: PathKind, + processor: RsResolveProcessor +): Boolean { + return when (pathKind) { + is NamedAddress, is ValueAddress -> false + is PathKind.UnqualifiedPath -> { + // Self:: + if (processor.lazy("Self") { ctx.containingModule }) return true + // local + processNestedScopesUpwards(ctx.element, pathKind.ns, ctx, processor) + } + is PathKind.QualifiedPath.Module -> { + processModulePathResolveVariants(ctx, pathKind.address, processor) + } + is PathKind.QualifiedPath -> { + processQualifiedPathResolveVariants(ctx, pathKind.ns, pathKind.qualifier, processor) + } + } +} + +/** + * foo::bar + * | | + * | [path] + * [qualifier] + */ +fun processQualifiedPathResolveVariants( + ctx: ResolutionContext, + ns: Set, + qualifier: MvPath, + processor: RsResolveProcessor +): Boolean { + val resolvedQualifier = qualifier.reference?.resolveFollowingAliases() + if (resolvedQualifier == null) { + if (Namespace.MODULE in ns) { + // can be module, try for named address as a qualifier + val addressName = qualifier.referenceName ?: return false + val address = ctx.moveProject?.getNamedAddressTestAware(addressName) ?: return false + if (processModulePathResolveVariants(ctx, address, processor)) return true + } + return false + } + if (resolvedQualifier is MvModule) { + if (processor.process("Self", resolvedQualifier)) return true + + val moduleBlock = resolvedQualifier.moduleBlock + if (moduleBlock != null) { + if (processItemDeclarations(moduleBlock, ns, processor)) return true + } + } + return false +} + +class ResolutionContext(val element: MvElement, val isCompletion: Boolean) { + + private var lazyContainingMoveProject: Lazy = lazy(NONE) { + element.moveProject + } + val moveProject: MoveProject? get() = lazyContainingMoveProject.value + + private var lazyContainingModule: Lazy = lazy(NONE) { + element.containingModule + } + val containingModule: MvModule? get() = lazyContainingModule.value + + private var lazyUseSpeck: Lazy = lazy(NONE) { path?.useSpeck } + val useSpeck: MvUseSpeck? get() = lazyUseSpeck.value + val isUseSpeck: Boolean get() = useSpeck != null + + val methodOrPath: MvMethodOrPath? get() = element as? MvMethodOrPath + val path: MvPath? get() = element as? MvPath +} + +//// todo: use in inference later +//fun resolvePathRaw(path: MvPath): List { +// return collectResolveVariantsAsScopeEntries(path.referenceName) { +// processPathResolveVariants(path, it) +// } +//} + +private fun resolvePath( + ctx: ResolutionContext, + path: MvPath, +// kind: RsPathResolveKind +): List> { + val pathKind = path.pathKind() + val result = + // matches resolve variants against referenceName from path + collectMethodOrPathResolveVariants(path, ctx) { + // actually emits resolve variants + processPathResolveVariants(ctx, pathKind, it) + } + return result +// return when (result.size) { +// 0 -> emptyList() +// 1 -> listOf(result.single()) +// else -> result +// } +} + diff --git a/src/main/kotlin/org/move/lang/core/resolve2/ref/RsPathResolveResult.kt b/src/main/kotlin/org/move/lang/core/resolve2/ref/RsPathResolveResult.kt new file mode 100644 index 000000000..1021cf5d3 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/ref/RsPathResolveResult.kt @@ -0,0 +1,19 @@ +package org.move.lang.core.resolve2.ref + +import com.intellij.psi.PsiElement +import com.intellij.psi.ResolveResult +import org.move.lang.core.psi.MvElement + +/** + * Used as a resolve result in [org.rust.lang.core.resolve.ref.RsPathReferenceImpl] + */ +data class RsPathResolveResult( + val element: T, +// val resolvedSubst: Substitution = emptySubstitution, + val isVisible: Boolean, +// val namespaces: Set = emptySet(), +): ResolveResult { + override fun getElement(): PsiElement = element + + override fun isValidResult(): Boolean = true +} diff --git a/src/main/kotlin/org/move/lang/core/resolve2/util/PathUtil.kt b/src/main/kotlin/org/move/lang/core/resolve2/util/PathUtil.kt new file mode 100644 index 000000000..d5a970a80 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/resolve2/util/PathUtil.kt @@ -0,0 +1,27 @@ +package org.move.lang.core.resolve2.util + +import org.move.lang.core.psi.MvPath +import org.move.lang.core.psi.MvUseAlias +import org.move.lang.core.psi.MvUseSpeck +import org.move.lang.core.psi.MvUseStmt +import org.move.lang.core.psi.ext.childOfType +import org.move.lang.core.psi.ext.childrenOfType + +fun interface LeafUseSpeckConsumer { + fun consume(path: MvPath, useAlias: MvUseAlias?): Boolean +} + +fun MvUseStmt.forEachLeafSpeck(consumer: LeafUseSpeckConsumer) { + val rootUseSpeck = this.childOfType() ?: return + val useGroup = rootUseSpeck.useGroup + if (useGroup == null) { + // basePath is null, path is full path of useSpeck + val alias = rootUseSpeck.useAlias + if (!consumer.consume(rootUseSpeck.path, alias)) return + } else { + for (childSpeck in useGroup.childrenOfType()) { + val alias = childSpeck.useAlias + if (!consumer.consume(childSpeck.path, alias)) continue + } + } +} diff --git a/src/main/kotlin/org/move/lang/core/stubs/StubIndexing.kt b/src/main/kotlin/org/move/lang/core/stubs/StubIndexing.kt index 32d899f9f..cc1d05afc 100644 --- a/src/main/kotlin/org/move/lang/core/stubs/StubIndexing.kt +++ b/src/main/kotlin/org/move/lang/core/stubs/StubIndexing.kt @@ -6,6 +6,9 @@ import org.move.lang.index.* import org.move.lang.moveProject fun IndexSink.indexModuleStub(stub: MvModuleStub) { + stub.name?.let { + occurrence(MvModuleIndex.KEY, it) + } indexNamedStub(stub) } diff --git a/src/main/kotlin/org/move/lang/core/stubs/Stubs.kt b/src/main/kotlin/org/move/lang/core/stubs/Stubs.kt index 780a69729..6b57bd873 100644 --- a/src/main/kotlin/org/move/lang/core/stubs/Stubs.kt +++ b/src/main/kotlin/org/move/lang/core/stubs/Stubs.kt @@ -363,7 +363,7 @@ class MvModuleSpecStub( MvModuleSpecImpl(stub, this) override fun createStub(psi: MvModuleSpec, parentStub: StubElement<*>?): MvModuleSpecStub { - return MvModuleSpecStub(parentStub, this, psi.fqModuleRef?.referenceName) + return MvModuleSpecStub(parentStub, this, psi.path?.referenceName) } override fun indexStub(stub: MvModuleSpecStub, sink: IndexSink) = sink.indexModuleSpecStub(stub) diff --git a/src/main/kotlin/org/move/lang/core/stubs/impl/MvFileStub.kt b/src/main/kotlin/org/move/lang/core/stubs/impl/MvFileStub.kt index dc61c4bb6..9cf7ac2c4 100644 --- a/src/main/kotlin/org/move/lang/core/stubs/impl/MvFileStub.kt +++ b/src/main/kotlin/org/move/lang/core/stubs/impl/MvFileStub.kt @@ -17,7 +17,7 @@ class MvFileStub(file: MoveFile?) : PsiFileStubImpl(file) { override fun getType() = Type object Type : IStubFileElementType(MoveLanguage) { - private const val STUB_VERSION = 24 + private const val STUB_VERSION = 25 // Bump this number if Stub structure changes override fun getStubVersion(): Int = MoveParserDefinition.PARSER_VERSION + STUB_VERSION diff --git a/src/main/kotlin/org/move/lang/core/types/Address.kt b/src/main/kotlin/org/move/lang/core/types/Address.kt index caa0ea60a..f5c8e3a19 100644 --- a/src/main/kotlin/org/move/lang/core/types/Address.kt +++ b/src/main/kotlin/org/move/lang/core/types/Address.kt @@ -7,6 +7,8 @@ import org.move.lang.core.psi.MvAddressRef import org.move.lang.core.psi.MvModule import org.move.lang.core.psi.ext.addressRef import org.move.lang.core.psi.ext.greenStub +import org.move.lang.core.types.Address.Named +import org.move.lang.core.types.Address.Value import org.move.lang.core.types.AddressLit.Companion.normalizeValue const val MAX_LENGTH = 32 @@ -40,31 +42,35 @@ class AddressLit(val original: String) { sealed class Address { - abstract fun canonicalValue(moveProject: MoveProject): String? abstract fun text(): String + abstract fun canonicalValue(moveProject: MoveProject): String? + + val is0x0 get() = this is Value && this.addressLit().original == "0x0" - class Value(private val value: String) : Address() { + class Value(private val value: String): Address() { fun addressLit(): AddressLit = AddressLit(value) override fun canonicalValue(moveProject: MoveProject): String = this.addressLit().canonical() override fun text(): String = this.addressLit().original - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Value) return false - if (this.hashCode() != other.hashCode()) return false - return eq(this, other) - } +// override fun equals(other: Any?): Boolean { +// if (this === other) return true +// if (other !is Value) return false +// if (this.hashCode() != other.hashCode()) return false +// return eq(this, other) +// } + +// override fun hashCode(): Int = normalizeValue(value).hashCode() - override fun hashCode(): Int = normalizeValue(value).hashCode() + override fun toString(): String = "Address.Value($value)" } class Named( val name: String, - private val value: String?, + val value: String?, private val declMoveProject: MoveProject? - ) : Address() { + ): Address() { fun value(moveProject: MoveProject? = null): String { return value ?: this.declMoveProject?.getNamedAddressValue(name) @@ -75,43 +81,57 @@ sealed class Address { fun addressLit(moveProject: MoveProject): AddressLit? = moveProject.getNamedAddressValue(this.name)?.let { AddressLit(it) } - override fun canonicalValue(moveProject: MoveProject): String? = this.addressLit(moveProject)?.canonical() + override fun canonicalValue(moveProject: MoveProject): String? = + this.addressLit(moveProject)?.canonical() override fun text(): String = "$name = ${value()}" - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Named) return false - if (this.hashCode() != other.hashCode()) return false - return eq(this, other) - } +// override fun equals(other: Any?): Boolean { +// if (this === other) return true +// if (other !is Named) return false +//// if (this.hashCode() != other.hashCode()) return false +// return eq(this, other) +// } - override fun hashCode(): Int = name.hashCode() +// override fun hashCode(): Int = name.hashCode() } companion object { const val UNKNOWN: String = "0x0" - fun eq(left: Address?, right: Address?): Boolean { + fun equals(left: Address?, right: Address?): Boolean { if (left === right) return true if (left == null && right == null) return true return when { - left is Value && right is Value -> left.addressLit().canonical() == right.addressLit().canonical() - left is Named && right is Named -> - Pair(left.name, normalizeValue(left.value())) == Pair( - right.name, - normalizeValue(right.value()) - ) + left is Value && right is Value -> + left.addressLit().canonical() == right.addressLit().canonical() + left is Named && right is Named -> { + val leftValue = left.value?.let { normalizeValue(it) } + val rightValue = right.value?.let { normalizeValue(it) } + if (leftValue == null && rightValue == null) { + // null items cannot be equal + return false + } + return leftValue == rightValue + } + left is Value && right is Named -> checkValueNamedEquals(left, right) + left is Named && right is Value -> checkValueNamedEquals(right, left) else -> false } } + + private fun checkValueNamedEquals(value: Value, named: Named): Boolean { + val normalizedValue = value.addressLit().canonical() + val normalizedNamed = named.value?.let { normalizeValue(it) } + return normalizedValue == normalizedNamed + } } } sealed class StubAddress { - object Unknown : StubAddress() - data class Value(val value: String) : StubAddress() - data class Named(val name: String) : StubAddress() + object Unknown: StubAddress() + data class Value(val value: String): StubAddress() + data class Named(val name: String): StubAddress() fun asInt(): Int { return when (this) { @@ -125,9 +145,13 @@ sealed class StubAddress { return when (this) { is Named -> { if (moveProject == null) { - Address.Named(this.name, null, null) + Named(this.name, null, null) } else { - moveProject.getNamedAddress(this.name) + moveProject.getNamedAddressTestAware(this.name) ?: Named( + this.name, + null, + moveProject + ) } } is Value -> Address.Value(this.value) diff --git a/src/main/kotlin/org/move/lang/core/types/infer/ExpectedType.kt b/src/main/kotlin/org/move/lang/core/types/infer/ExpectedType.kt index d675bf4b6..fbc26f792 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/ExpectedType.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/ExpectedType.kt @@ -27,7 +27,7 @@ fun inferExpectedTypeArgumentTy(typeArgument: MvTypeArgument): Ty? { if (paramIndex == -1) return null val path = typeArgumentList.parent as MvPath - val genericItem = path.reference?.resolveWithAliases() as? MvTypeParametersOwner ?: return null + val genericItem = path.reference?.resolveFollowingAliases() as? MvTypeParametersOwner ?: return null genericItem.typeParameters .getOrNull(paramIndex) ?.let { TyInfer.TyVar(TyTypeParameter(it)) } diff --git a/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt b/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt index 22dfb50b6..ef287c69f 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt @@ -72,6 +72,7 @@ data class InferenceResult( private val exprTypes: Map, private val exprExpectedTypes: Map, private val methodOrPathTypes: Map, +// private val resolvedPaths: Map>, private val resolvedFields: Map, private val resolvedMethodCalls: Map, val callableTypes: Map, @@ -90,6 +91,9 @@ data class InferenceResult( fun getCallableType(callable: MvCallable): Ty? = callableTypes[callable] fun getMethodOrPathType(methodOrPath: MvMethodOrPath): Ty? = methodOrPathTypes[methodOrPath] +// fun getResolvedPath(path: MvPath): List = +// resolvedPaths[path] ?: emptyList() + fun getResolvedField(field: MvStructDotField): MvNamedElement? = resolvedFields[field] fun getResolvedMethod(methodCall: MvMethodCall): MvNamedElement? = resolvedMethodCalls[methodCall] } @@ -136,6 +140,19 @@ fun MvElement.inference(msl: Boolean): InferenceResult? { return contextOwner.inference(msl) } +//data class ResolvedPath(val element: MvElement, val isVisible: Boolean) { +// companion object { +// fun from(entry: ScopeEntry, context: MvElement): ResolvedPath { +//// return if (entry is AssocItemScopeEntry) { +//// AssocItem(entry.element, entry.source) +//// } else { +//// val isVisible = entry.isVisibleFrom(context.containingModule) +// return ResolvedPath(entry.element, true) +//// } +// } +// } +//} + class InferenceContext( var msl: Boolean, private val skipUnification: Boolean = false @@ -150,6 +167,7 @@ class InferenceContext( // private val pathTypes = mutableMapOf() private val methodOrPathTypes = mutableMapOf() +// val resolvedPaths = mutableMapOf>() val resolvedFields = mutableMapOf() val resolvedMethodCalls = mutableMapOf() @@ -209,6 +227,7 @@ class InferenceContext( exprTypes, exprExpectedTypes, methodOrPathTypes, +// resolvedPaths, resolvedFields, resolvedMethodCalls, callableTypes, @@ -256,6 +275,10 @@ class InferenceContext( this.callableTypes[callable] = ty } +// fun writePath(path: MvPath, resolved: List) { +// resolvedPaths[path] = resolved +// } + @Suppress("UNCHECKED_CAST") fun instantiateMethodOrPath( methodOrPath: MvMethodOrPath, diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TyLowering.kt b/src/main/kotlin/org/move/lang/core/types/infer/TyLowering.kt index 4ff23777c..5f1e16336 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TyLowering.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TyLowering.kt @@ -13,7 +13,7 @@ class TyLowering { fun lowerTy(moveType: MvType, msl: Boolean): Ty { return when (moveType) { is MvPathType -> { - val genericItem = moveType.path.reference?.resolveWithAliases() + val genericItem = moveType.path.reference?.resolveFollowingAliases() lowerPath(moveType.path, genericItem, msl) } is MvRefType -> { diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index c1a345bba..4abd2066f 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -9,8 +9,10 @@ import org.move.cli.settings.moveSettings import org.move.ide.formatter.impl.location import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* +import org.move.lang.core.resolve.collectMethodOrPathResolveVariants +import org.move.lang.core.resolve2.processMethodResolveVariants +import org.move.lang.core.resolve2.ref.ResolutionContext import org.move.lang.core.types.ty.* -import org.move.lang.core.types.ty.TyInfer.TyVar import org.move.lang.core.types.ty.TyReference.Companion.autoborrow import org.move.stdext.RsResult import org.move.stdext.chain @@ -291,7 +293,7 @@ class TypeInferenceWalker( return funcItem.rawReturnType(true) } } - val item = refExpr.path.reference?.resolveWithAliases() ?: return TyUnknown + val item = refExpr.path.reference?.resolveFollowingAliases() ?: return TyUnknown val ty = when (item) { is MvBindingPat -> ctx.getPatType(item) is MvConst -> item.type?.loweredType(msl) ?: TyUnknown @@ -353,7 +355,7 @@ class TypeInferenceWalker( private fun inferCallExprTy(callExpr: MvCallExpr, expected: Expectation): Ty { val path = callExpr.path - val item = path.reference?.resolveWithAliases() + val item = path.reference?.resolveFollowingAliases() val baseTy = when (item) { is MvFunctionLike -> { @@ -413,10 +415,14 @@ class TypeInferenceWalker( } fun inferMethodCallTy(receiverTy: Ty, methodCall: MvMethodCall, expected: Expectation): Ty { - val refName = methodCall.referenceName - val methodVariants = getMethodVariants(methodCall, receiverTy, ctx.msl) - val genericItem = methodVariants.filterByName(refName).firstOrNull() + val resolutionCtx = ResolutionContext(methodCall, isCompletion = false) + val resolvedMethods = + collectMethodOrPathResolveVariants(methodCall, resolutionCtx) { + processMethodResolveVariants(methodCall, receiverTy, msl, it) + } + val genericItem = + resolvedMethods.filter { it.isVisible }.mapNotNull { it.element as? MvNamedElement }.firstOrNull() ctx.resolvedMethodCalls[methodCall] = genericItem val baseTy = diff --git a/src/main/kotlin/org/move/lang/index/MvModuleIndex.kt b/src/main/kotlin/org/move/lang/index/MvModuleIndex.kt new file mode 100644 index 000000000..6d77c673f --- /dev/null +++ b/src/main/kotlin/org/move/lang/index/MvModuleIndex.kt @@ -0,0 +1,53 @@ +package org.move.lang.index + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndex +import com.intellij.psi.stubs.StubIndexKey +import org.move.lang.core.psi.MvModule +import org.move.lang.core.stubs.impl.MvFileStub +import org.move.openapiext.checkCommitIsNotInProgress + +class MvModuleIndex: StringStubIndexExtension() { + override fun getVersion(): Int = MvFileStub.Type.stubVersion + override fun getKey(): StubIndexKey = KEY + + companion object { + val KEY: StubIndexKey = + StubIndexKey.createIndexKey("org.move.index.ModuleIndex") + + fun getAllModuleNames(project: Project): Collection { + checkCommitIsNotInProgress(project) + return StubIndex.getInstance().getAllKeys(KEY, project) + } + + fun processModulesByName( + project: Project, + target: String, + scope: GlobalSearchScope = GlobalSearchScope.allScope(project), + processor: (MvModule) -> Boolean, + ) { + checkCommitIsNotInProgress(project) + StubIndex.getInstance() + .processElements(KEY, target, project, scope, MvModule::class.java, processor) + } + + fun getModulesByName( + project: Project, + name: String, + scope: GlobalSearchScope, + ): Collection { + checkCommitIsNotInProgress(project) + return StubIndex.getElements(KEY, name, project, scope, MvModule::class.java) + } + + fun getModuleByName( + project: Project, + name: String, + scope: GlobalSearchScope, + ): MvModule? { + return getModulesByName(project, name, scope).singleOrNull() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/index/MvModuleSpecIndex.kt b/src/main/kotlin/org/move/lang/index/MvModuleSpecIndex.kt index ec547ebe6..b34b9476f 100644 --- a/src/main/kotlin/org/move/lang/index/MvModuleSpecIndex.kt +++ b/src/main/kotlin/org/move/lang/index/MvModuleSpecIndex.kt @@ -9,7 +9,7 @@ import org.move.lang.core.stubs.impl.MvFileStub import org.move.openapiext.checkCommitIsNotInProgress import org.move.openapiext.getElements -class MvModuleSpecIndex : StringStubIndexExtension() { +class MvModuleSpecIndex: StringStubIndexExtension() { override fun getVersion(): Int = MvFileStub.Type.stubVersion override fun getKey(): StubIndexKey = KEY diff --git a/src/main/kotlin/org/move/lang/index/MvNamedElementIndex.kt b/src/main/kotlin/org/move/lang/index/MvNamedElementIndex.kt index 54e7500a4..ce91c7001 100644 --- a/src/main/kotlin/org/move/lang/index/MvNamedElementIndex.kt +++ b/src/main/kotlin/org/move/lang/index/MvNamedElementIndex.kt @@ -9,7 +9,7 @@ import org.move.lang.core.psi.MvNamedElement import org.move.lang.core.stubs.impl.MvFileStub import org.move.openapiext.checkCommitIsNotInProgress -class MvNamedElementIndex : StringStubIndexExtension() { +class MvNamedElementIndex: StringStubIndexExtension() { override fun getVersion(): Int = MvFileStub.Type.stubVersion override fun getKey(): StubIndexKey = KEY diff --git a/src/main/kotlin/org/move/lang/utils/Diagnostic.kt b/src/main/kotlin/org/move/lang/utils/Diagnostic.kt index 81451bd59..2e5a62933 100644 --- a/src/main/kotlin/org/move/lang/utils/Diagnostic.kt +++ b/src/main/kotlin/org/move/lang/utils/Diagnostic.kt @@ -16,7 +16,6 @@ import org.move.ide.annotator.pluralise import org.move.ide.inspections.fixes.CompilerV2Feat.* import org.move.ide.inspections.fixes.EnableCompilerV2FeatureFix import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.dotExpr import org.move.lang.core.psi.ext.endOffset import org.move.lang.core.psi.ext.itemSpecBlock import org.move.lang.core.psi.ext.startOffset diff --git a/src/main/kotlin/org/move/openapiext/utils.kt b/src/main/kotlin/org/move/openapiext/utils.kt index 5783a65e6..fcd12d210 100644 --- a/src/main/kotlin/org/move/openapiext/utils.kt +++ b/src/main/kotlin/org/move/openapiext/utils.kt @@ -102,13 +102,6 @@ fun saveAllDocumentsAsTheyAre() { // } } -inline fun testAssert(action: () -> Boolean, lazyMessage: () -> Any) { - if (isUnitTestMode && !action()) { - val message = lazyMessage() - throw AssertionError(message) - } -} - val DataContext.psiFile: PsiFile? get() = getData(CommonDataKeys.PSI_FILE) @@ -242,4 +235,15 @@ fun joinPathArray(segments: Array) = segments.joinTo(StringBuilder(), Platform.current().fileSeparator.toString()).toString() fun joinPath(vararg segments: String) = - segments.joinTo(StringBuilder(), Platform.current().fileSeparator.toString()).toString() \ No newline at end of file + segments.joinTo(StringBuilder(), Platform.current().fileSeparator.toString()).toString() + +inline fun testAssert(action: () -> Boolean) { + testAssert(action) { "Assertion failed" } +} + +inline fun testAssert(action: () -> Boolean, lazyMessage: () -> Any) { + if (isUnitTestMode && !action()) { + val message = lazyMessage() + throw AssertionError(message) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 1348c02cf..e7bc6e6ed 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,6 +1,6 @@ +> org.move.lang Move on Aptos @@ -45,6 +45,11 @@ + + + + - + @@ -199,7 +205,7 @@ + order="first" /> @@ -235,10 +241,6 @@ displayName="Local variable naming convention" enabledByDefault="true" level="WARNING" implementationClass="org.move.ide.inspections.MvLocalBindingNamingInspection" /> - - + displayName="External linter" /> @@ -312,9 +310,9 @@ - - - + + + - + + description="Enable debug mode for Move" /> + description="Show notification warning if the external linter is running longer than the set value (ms)" /> + description="Force-enable support for pre-compiled bundled Aptos CLI (imitate non-MacOS)" /> + description="Force-disable support for pre-compiled bundled Aptos CLI (imitate MacOS)" /> + topic="org.move.cli.MoveProjectsService$MoveProjectsListener" /> @@ -460,14 +458,14 @@ - - - - - - + + + + + + - + diff --git a/src/main/resources/icons/move_logo.svg b/src/main/resources/icons/move_logo.svg index 5ea4d1abd..baed098a5 100644 --- a/src/main/resources/icons/move_logo.svg +++ b/src/main/resources/icons/move_logo.svg @@ -1,7 +1,7 @@ - +