Rename unqualified method calls (#11556)

close #11281

Changelog:
- update: `refactoring/renameSymbol` can rename unqualified method calls

# Important Notes
https://github.com/user-attachments/assets/e03c4ec7-7620-4ce4-8eab-86b95f308be2
This commit is contained in:
Dmitry Bushev 2024-11-15 16:10:57 +03:00 committed by GitHub
parent 58512e701e
commit 6b810ee1e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 394 additions and 123 deletions

View File

@ -3191,14 +3191,6 @@ Current limitations of the method renaming are:
```rust
Main.function1 x = x
```
- Method calls where the self type is not specified will not be renamed, i.e.
```rust
function1 x = x
main =
operator1 = function1 42
```
#### Parameters

View File

@ -77,7 +77,8 @@ public final class SectionsToBinOp implements MiniPassFactory {
var loc = sectionLeft.location().isDefined() ? sectionLeft.location().get() : null;
var passData = sectionLeft.passData();
var rightArgName = freshNameSupply.newName(false, Option.empty());
var rightCallArg = new CallArgument.Specified(Option.empty(), rightArgName, null, meta());
var rightCallArg =
new CallArgument.Specified(Option.empty(), rightArgName, true, null, meta());
var rightDefArg =
new DefinitionArgument.Specified(
rightArgName.duplicate(true, true, true, false),
@ -89,7 +90,8 @@ public final class SectionsToBinOp implements MiniPassFactory {
if (arg.value() instanceof Name.Blank) {
var leftArgName = freshNameSupply.newName(false, Option.empty());
var leftCallArg = new CallArgument.Specified(Option.empty(), leftArgName, null, meta());
var leftCallArg =
new CallArgument.Specified(Option.empty(), leftArgName, true, null, meta());
var leftDefArg =
new DefinitionArgument.Specified(
leftArgName.duplicate(true, true, true, false),
@ -122,7 +124,8 @@ public final class SectionsToBinOp implements MiniPassFactory {
var loc = sectionSides.location().isDefined() ? sectionSides.location().get() : null;
var passData = sectionSides.passData();
var leftArgName = freshNameSupply.newName(false, Option.empty());
var leftCallArg = new CallArgument.Specified(Option.empty(), leftArgName, null, meta());
var leftCallArg =
new CallArgument.Specified(Option.empty(), leftArgName, true, null, meta());
var leftDefArg =
new DefinitionArgument.Specified(
leftArgName.duplicate(true, true, true, false),
@ -133,7 +136,8 @@ public final class SectionsToBinOp implements MiniPassFactory {
meta());
var rightArgName = freshNameSupply.newName(false, Option.empty());
var rightCallArg = new CallArgument.Specified(Option.empty(), rightArgName, null, meta());
var rightCallArg =
new CallArgument.Specified(Option.empty(), rightArgName, true, null, meta());
var rightDefArg =
new DefinitionArgument.Specified(
rightArgName.duplicate(true, true, true, false),
@ -183,7 +187,8 @@ public final class SectionsToBinOp implements MiniPassFactory {
var loc = sectionRight.location().isDefined() ? sectionRight.location().get() : null;
var passData = sectionRight.passData();
var leftArgName = freshNameSupply.newName(false, Option.empty());
var leftCallArg = new CallArgument.Specified(Option.empty(), leftArgName, null, meta());
var leftCallArg =
new CallArgument.Specified(Option.empty(), leftArgName, true, null, meta());
var leftDefArg =
new DefinitionArgument.Specified(
leftArgName.duplicate(true, true, true, false),
@ -197,7 +202,7 @@ public final class SectionsToBinOp implements MiniPassFactory {
// Note [Blanks in Sections]
var rightArgName = freshNameSupply.newName(false, Option.empty());
var rightCallArg =
new CallArgument.Specified(Option.empty(), rightArgName, null, meta());
new CallArgument.Specified(Option.empty(), rightArgName, true, null, meta());
var rightDefArg =
new DefinitionArgument.Specified(
rightArgName.duplicate(true, true, true, false),

View File

@ -653,7 +653,7 @@ case object AliasAnalysis extends IRPass {
args: List[CallArgument],
builder: GraphBuilder
): List[CallArgument] = {
args.map { case arg @ CallArgument.Specified(_, expr, _, _) =>
args.map { case arg @ CallArgument.Specified(_, expr, _, _, _) =>
val currentScope = expr match {
case _: Literal => builder
case _ => builder.addChild()

View File

@ -323,8 +323,8 @@ object AutomaticParallelism extends IRPass {
Name.Special(Name.Special.WriteRef, null),
List(
CallArgument
.Specified(None, refVars(bind.name).duplicate(), null),
CallArgument.Specified(None, bind.name.duplicate(), null)
.Specified(None, refVars(bind.name).duplicate(), true, null),
CallArgument.Specified(None, bind.name.duplicate(), true, null)
),
false,
null
@ -338,6 +338,7 @@ object AutomaticParallelism extends IRPass {
CallArgument.Specified(
None,
Expression.Block(blockBody.init, blockBody.last, null),
true,
null
)
),
@ -354,7 +355,7 @@ object AutomaticParallelism extends IRPass {
val threadJoins = threadSpawns.map { bind =>
Application.Prefix(
Name.Special(Name.Special.JoinThread, null),
List(CallArgument.Specified(None, bind.name.duplicate(), null)),
List(CallArgument.Specified(None, bind.name.duplicate(), true, null)),
false,
null
)
@ -366,7 +367,7 @@ object AutomaticParallelism extends IRPass {
name.duplicate(),
Application.Prefix(
Name.Special(Name.Special.ReadRef, null),
List(CallArgument.Specified(None, ref.duplicate(), null)),
List(CallArgument.Specified(None, ref.duplicate(), true, null)),
false,
null
),

View File

@ -745,7 +745,7 @@ case object DataflowAnalysis extends IRPass {
info: DependencyInfo
): CallArgument = {
argument match {
case spec @ CallArgument.Specified(name, value, _, _) =>
case spec @ CallArgument.Specified(name, value, _, _, _) =>
val specDep = asStatic(spec)
val valueDep = asStatic(value)
info.dependents.updateAt(valueDep, Set(specDep))

View File

@ -265,10 +265,10 @@ case object DemandAnalysis extends IRPass {
*/
def analyseCallArgument(arg: CallArgument): CallArgument = {
arg match {
case spec @ CallArgument.Specified(_, expr, _, _) =>
spec.copy(
case arg: CallArgument.Specified =>
arg.copy(
value = analyseExpression(
expr,
arg.value,
isInsideCallArgument = true
)
)

View File

@ -242,12 +242,13 @@ case object FramePointerAnalysis extends IRPass {
arguments: List[CallArgument],
graph: Graph
): Unit = {
arguments.foreach { case arg @ CallArgument.Specified(name, value, _, _) =>
maybeAttachFramePointer(arg, graph)
name.foreach(maybeAttachFramePointer(_, graph))
processExpression(value, graph, false)
maybAttachFrameVariableNames(value)
maybAttachFrameVariableNames(arg)
arguments.foreach {
case arg @ CallArgument.Specified(name, value, _, _, _) =>
maybeAttachFramePointer(arg, graph)
name.foreach(maybeAttachFramePointer(_, graph))
processExpression(value, graph, false)
maybAttachFrameVariableNames(value)
maybAttachFrameVariableNames(arg)
}
}

View File

@ -34,8 +34,8 @@ class LambdaShorthandToLambdaMini(
parent match {
case Application.Prefix(fn, args, _, _, _) =>
val hasBlankArg = args.exists {
case CallArgument.Specified(_, _: Name.Blank, _, _) => true
case _ => false
case CallArgument.Specified(_, _: Name.Blank, _, _, _) => true
case _ => false
}
val hasBlankFn = fn.isInstanceOf[Name.Blank]
hasBlankArg || hasBlankFn
@ -222,8 +222,8 @@ class LambdaShorthandToLambdaMini(
private def determineLambdaShorthand(
args: List[CallArgument]
): List[Boolean] = {
args.map { case CallArgument.Specified(_, value, _, _) =>
value match {
args.map { arg =>
arg.value match {
case _: Name.Blank => true
case _ => false
}
@ -245,14 +245,14 @@ class LambdaShorthandToLambdaMini(
val isShorthand = argAndIsShorthand._2
arg match {
case s @ CallArgument.Specified(_, value, _, _) =>
case s: CallArgument.Specified =>
if (isShorthand) {
val newName = freshNameSupply
.newName()
.copy(
location = value.location,
passData = value.passData,
diagnostics = value.diagnostics
location = s.value.location,
passData = s.value.passData,
diagnostics = s.value.diagnostics
)
s.copy(value = newName)
@ -274,11 +274,11 @@ class LambdaShorthandToLambdaMini(
): Option[DefinitionArgument] = {
if (isShorthand) {
arg match {
case specified @ CallArgument.Specified(_, value, _, passData) =>
case specified: CallArgument.Specified =>
// Note [Safe Casting to Name.Literal]
val defArgName =
Name.Literal(
value.asInstanceOf[Name.Literal].name,
specified.value.asInstanceOf[Name.Literal].name,
isMethod = false,
null
)
@ -290,7 +290,7 @@ class LambdaShorthandToLambdaMini(
None,
suspended = false,
null,
passData.duplicate,
specified.passData.duplicate,
specified.diagnosticsCopy
)
)

View File

@ -221,8 +221,12 @@ case object GlobalNames extends IRPass {
val app = Application.Prefix(
fun,
List(
CallArgument
.Specified(None, self, identifiedLocation = null)
CallArgument.Specified(
None,
self,
true,
identifiedLocation = null
)
),
hasDefaultsSuspended = false,
lit.identifiedLocation
@ -345,7 +349,7 @@ case object GlobalNames extends IRPass {
)
)
val selfArg =
CallArgument.Specified(None, self, identifiedLocation = null)
CallArgument.Specified(None, self, true, identifiedLocation = null)
processedFun.passData.remove(this) // Necessary for IrToTruffle
app.copy(function = processedFun, arguments = selfArg :: processedArgs)
case _ =>

View File

@ -219,9 +219,9 @@ case object TypeFunctions extends IRPass {
*/
private def resolveCallArgument(arg: CallArgument): CallArgument = {
arg match {
case spec @ CallArgument.Specified(_, value, _, _) =>
case spec: CallArgument.Specified =>
spec.copy(
value = resolveExpression(value)
value = resolveExpression(spec.value)
)
}
}
@ -241,8 +241,8 @@ case object TypeFunctions extends IRPass {
*/
private def isValidCallArg(arg: CallArgument): Boolean = {
arg match {
case CallArgument.Specified(name, _, _, _) =>
name.isEmpty
case specified: CallArgument.Specified =>
specified.name.isEmpty
}
}
}

View File

@ -3,6 +3,7 @@ package org.enso.compiler.refactoring
import org.enso.compiler.core.Implicits.AsMetadata
import org.enso.compiler.core.{ExternalID, IR, Identifier}
import org.enso.compiler.core.ir.Name
import org.enso.compiler.core.ir.expression.Application
import org.enso.compiler.data.BindingsMap
import org.enso.compiler.pass.analyse.DataflowAnalysis
import org.enso.compiler.pass.resolve.MethodCalls
@ -64,23 +65,27 @@ trait IRUtils {
for {
usages <- findDynamicUsages(ir, node)
} yield {
usages
.collect {
case usage: Name.Literal
if usage.isMethod && usage.name == node.name =>
usage
}
.flatMap { symbol =>
symbol.getMetadata(MethodCalls).flatMap { resolution =>
resolution.target match {
case BindingsMap.ResolvedModuleMethod(module, _)
if module.getName == moduleName =>
Some(symbol)
case _ =>
None
}
usages.collect {
case Application.Prefix(function: Name.Literal, args, _, _, _)
if function.name == node.name =>
function.getMetadata(MethodCalls) match {
case Some(resolution) =>
resolution.target match {
case BindingsMap.ResolvedModuleMethod(module, _)
if module.getName == moduleName =>
Some(function)
case _ =>
None
}
case None =>
args.headOption match {
case Some(arg) if arg.isSynthetic =>
Some(function)
case _ =>
None
}
}
}
}.flatten
}
/** Find usages of a static dependency in the [[DataflowAnalysis]] metadata.

View File

@ -26,7 +26,7 @@ class GatherDiagnosticsTest extends CompilerTest {
val plusApp = Application.Prefix(
plusOp,
List(
CallArgument.Specified(None, error1, identifiedLocation = null)
CallArgument.Specified(None, error1, false, identifiedLocation = null)
),
hasDefaultsSuspended = false,
identifiedLocation = null
@ -121,11 +121,11 @@ class GatherDiagnosticsTest extends CompilerTest {
)
val result = GatherDiagnostics.runModule(module, buildModuleContext())
val gatheredErrros = result
val gatheredErros = result
.unsafeGetMetadata(GatherDiagnostics, "Impossible")
.diagnostics
gatheredErrros.toSet shouldEqual Set(error1, error2, error3)
gatheredErros.toSet shouldEqual Set(error1, error2, error3)
}
"work with annotations" in {

View File

@ -300,11 +300,11 @@ case object TailCallMegaPass extends IRPass {
*/
private def analyseCallArg(argument: CallArgument): CallArgument = {
argument match {
case arg @ CallArgument.Specified(_, expr, _, _) =>
case arg: CallArgument.Specified =>
arg
.copy(
// Note [Call Argument Tail Position]
value = analyseExpression(expr, isInTailPosition = true)
value = analyseExpression(arg.value, isInTailPosition = true)
)
.updateMetadata(TAIL_META)
}

View File

@ -189,8 +189,8 @@ case object LambdaShorthandToLambdaMegaPass extends IRPass {
args
.zip(argIsUnderscore)
.map(updateShorthandArg(_, freshNameSupply))
.map { case s @ CallArgument.Specified(_, value, _, _) =>
s.copy(value = desugarExpression(value, freshNameSupply))
.map { case s: CallArgument.Specified =>
s.copy(value = desugarExpression(s.value, freshNameSupply))
}
// Generate a definition arg instance for each shorthand arg
@ -306,8 +306,8 @@ case object LambdaShorthandToLambdaMegaPass extends IRPass {
private def determineLambdaShorthand(
args: List[CallArgument]
): List[Boolean] = {
args.map { case CallArgument.Specified(_, value, _, _) =>
value match {
args.map { arg =>
arg.value match {
case _: Name.Blank => true
case _ => false
}
@ -330,14 +330,14 @@ case object LambdaShorthandToLambdaMegaPass extends IRPass {
val isShorthand = argAndIsShorthand._2
arg match {
case s @ CallArgument.Specified(_, value, _, _) =>
case s: CallArgument.Specified =>
if (isShorthand) {
val newName = freshNameSupply
.newName()
.copy(
location = value.location,
passData = value.passData,
diagnostics = value.diagnostics
location = s.value.location,
passData = s.value.passData,
diagnostics = s.value.diagnostics
)
s.copy(value = newName)
@ -359,11 +359,11 @@ case object LambdaShorthandToLambdaMegaPass extends IRPass {
): Option[DefinitionArgument] = {
if (isShorthand) {
arg match {
case specified @ CallArgument.Specified(_, value, _, passData) =>
case specified: CallArgument.Specified =>
// Note [Safe Casting to Name.Literal]
val defArgName =
Name.Literal(
value.asInstanceOf[Name.Literal].name,
specified.value.asInstanceOf[Name.Literal].name,
isMethod = false,
null
)
@ -375,7 +375,7 @@ case object LambdaShorthandToLambdaMegaPass extends IRPass {
None,
suspended = false,
null,
passData.duplicate,
specified.passData.duplicate,
specified.diagnosticsCopy
)
)

View File

@ -64,9 +64,10 @@ class OperatorToFunctionTest extends MiniPassTest {
): (Operator.Binary, Application.Prefix) = {
val loc = new IdentifiedLocation(new Location(1, 33))
val leftArg = CallArgument.Specified(None, left, left.identifiedLocation())
val leftArg =
CallArgument.Specified(None, left, false, left.identifiedLocation())
val rightArg =
CallArgument.Specified(None, right, right.identifiedLocation())
CallArgument.Specified(None, right, false, right.identifiedLocation())
val binOp =
Operator.Binary(leftArg, name, rightArg, loc)
@ -85,12 +86,12 @@ class OperatorToFunctionTest extends MiniPassTest {
Name.Literal("=:=", isMethod = true, null)
val left = Empty(null)
val right = Empty(null)
val rightArg = CallArgument.Specified(None, Empty(null), null)
val rightArg = CallArgument.Specified(None, Empty(null), false, null)
val (operator, operatorFn) = genOprAndFn(opName, left, right)
val oprArg = CallArgument.Specified(None, operator, null)
val oprFnArg = CallArgument.Specified(None, operatorFn, null)
val oprArg = CallArgument.Specified(None, operator, false, null)
val oprFnArg = CallArgument.Specified(None, operatorFn, false, null)
"Operators" should {
val opName =
@ -100,15 +101,16 @@ class OperatorToFunctionTest extends MiniPassTest {
val rightArg = CallArgument.Specified(
None,
Empty(identifiedLocation = null),
false,
identifiedLocation = null
)
val (operator, operatorFn) = genOprAndFn(opName, left, right)
val oprArg =
CallArgument.Specified(None, operator, identifiedLocation = null)
CallArgument.Specified(None, operator, false, identifiedLocation = null)
val oprFnArg =
CallArgument.Specified(None, operatorFn, identifiedLocation = null)
CallArgument.Specified(None, operatorFn, false, identifiedLocation = null)
"be translated to functions" in {
OperatorToFunctionTestPass.runExpression(

View File

@ -105,7 +105,12 @@ case object SectionsToBinOpMegaPass extends IRPass {
case sectionLeft @ Section.Left(arg, op, loc, passData) =>
val rightArgName = freshNameSupply.newName()
val rightCallArg =
CallArgument.Specified(None, rightArgName, identifiedLocation = null)
CallArgument.Specified(
None,
rightArgName,
true,
identifiedLocation = null
)
val rightDefArg = DefinitionArgument.Specified(
rightArgName.duplicate(),
None,
@ -117,7 +122,12 @@ case object SectionsToBinOpMegaPass extends IRPass {
if (arg.value.isInstanceOf[Name.Blank]) {
val leftArgName = freshNameSupply.newName()
val leftCallArg =
CallArgument.Specified(None, leftArgName, identifiedLocation = null)
CallArgument.Specified(
None,
leftArgName,
true,
identifiedLocation = null
)
val leftDefArg = DefinitionArgument.Specified(
leftArgName.duplicate(),
None,
@ -161,7 +171,12 @@ case object SectionsToBinOpMegaPass extends IRPass {
case sectionSides @ Section.Sides(op, loc, passData) =>
val leftArgName = freshNameSupply.newName()
val leftCallArg =
CallArgument.Specified(None, leftArgName, identifiedLocation = null)
CallArgument.Specified(
None,
leftArgName,
true,
identifiedLocation = null
)
val leftDefArg = DefinitionArgument.Specified(
leftArgName.duplicate(),
None,
@ -172,7 +187,12 @@ case object SectionsToBinOpMegaPass extends IRPass {
val rightArgName = freshNameSupply.newName()
val rightCallArg =
CallArgument.Specified(None, rightArgName, identifiedLocation = null)
CallArgument.Specified(
None,
rightArgName,
true,
identifiedLocation = null
)
val rightDefArg = DefinitionArgument.Specified(
rightArgName.duplicate(),
None,
@ -224,7 +244,12 @@ case object SectionsToBinOpMegaPass extends IRPass {
case sectionRight @ Section.Right(op, arg, loc, passData) =>
val leftArgName = freshNameSupply.newName()
val leftCallArg =
CallArgument.Specified(None, leftArgName, identifiedLocation = null)
CallArgument.Specified(
None,
leftArgName,
true,
identifiedLocation = null
)
val leftDefArg =
DefinitionArgument.Specified(
leftArgName.duplicate(),
@ -241,6 +266,7 @@ case object SectionsToBinOpMegaPass extends IRPass {
CallArgument.Specified(
None,
rightArgName,
true,
identifiedLocation = null
)
val rightDefArg = DefinitionArgument.Specified(

View File

@ -170,7 +170,34 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
}
}
"find usages of a static call in lambda" in {
"find usages of unqualified method call in main body" in {
val uuid1 = new UUID(0, 1)
val code =
s"""function1 = ""
|
|main =
| operator1 = Test.function1
| operator2 = function1
| operator3 = operator2.function1
|
|
|#### METADATA ####
|[[{"index": {"value": 0}, "size": {"value": 9}}, "$uuid1"]]
|[]
|""".stripMargin
val module = code.preprocessModule()
val function1 = IRUtils.findByExternalId(module, uuid1).get
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
usages.value.size shouldEqual 2
usages.value.foreach {
case _: Name.Literal => succeed
case ir => fail(s"Not a literal: $ir")
}
}
"find usages of a qualified static call in lambda" in {
val uuid1 = new UUID(0, 1)
val code =
s"""function1 x = x
@ -197,6 +224,32 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
}
}
"find usages of unqualified static call in lambda" in {
val uuid1 = new UUID(0, 1)
val code =
s"""function1 x = x
|
|main =
| operator2 = function1 (x -> function1 x)
| operator2
|
|
|#### METADATA ####
|[[{"index": {"value": 0}, "size": {"value": 9}}, "$uuid1"]]
|[]
|""".stripMargin
val module = code.preprocessModule()
val function1 = IRUtils.findByExternalId(module, uuid1).get
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
usages.value.size shouldEqual 2
usages.value.foreach {
case _: Name.Literal => succeed
case ir => fail(s"Not a literal: $ir")
}
}
"find usages of a static method call in presence of an instance method" in {
val uuid1 = new UUID(0, 1)
val code =
@ -235,7 +288,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|main =
| operator1 = 41
| operator2 = Test.function1 operator1
| operator3 = A.function1
| operator3 = function1 operator1
| operator4 = A.function1
|
|
|#### METADATA ####
@ -247,7 +301,7 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
val operator1 = IRUtils.findByExternalId(module, uuid1).get
val usages = findUsagesOfModuleMethod(moduleName, module, operator1)
usages.value.size shouldEqual 1
usages.value.size shouldEqual 2
usages.value.foreach {
case _: Name.Literal => succeed
case ir => fail(s"Not a literal: $ir")

View File

@ -430,7 +430,7 @@ class RuntimeRefactoringTest
context.consumeOut shouldEqual List("43")
}
it should "rename module method in main body" in {
it should "rename qualified module method in main body" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
@ -508,7 +508,7 @@ class RuntimeRefactoringTest
context.send(
Api.Request(requestId, Api.RenameSymbol(moduleName, idFunction1, newName))
)
context.receiveNIgnoreStdLib(4, 5) should contain theSameElementsAs Seq(
context.receiveNIgnoreStdLib(4) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.SymbolRenamed(newName)),
Api.Response(None, expectedFileEdit),
TestMessages.pending(contextId, idFunction1),
@ -517,7 +517,7 @@ class RuntimeRefactoringTest
context.consumeOut shouldEqual List("42")
}
it should "rename module method in lambda expression" in {
it should "rename qualified module method in lambda expression" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
@ -595,7 +595,180 @@ class RuntimeRefactoringTest
context.send(
Api.Request(requestId, Api.RenameSymbol(moduleName, idFunction1, newName))
)
context.receiveNIgnoreStdLib(4, 5) should contain theSameElementsAs Seq(
context.receiveNIgnoreStdLib(4) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.SymbolRenamed(newName)),
Api.Response(None, expectedFileEdit),
TestMessages.pending(contextId, idFunction1),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("42")
}
it should "rename unqualified module method in main body" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
val metadata = new Metadata
val idFunction1 = metadata.addItem(31, 9)
val code =
"""from Standard.Base import all
|
|function1 x = x + 1
|
|main =
| operator1 = function1 41
| IO.println operator1
|""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code)
val mainFile = context.writeMain(contents)
// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
context.receive shouldEqual Some(
Api.Response(requestId, Api.CreateContextResponse(contextId))
)
// open file
context.send(
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
)
context.receive shouldEqual Some(
Api.Response(Some(requestId), Api.OpenFileResponse)
)
// push main
context.send(
Api.Request(
requestId,
Api.PushContextRequest(
contextId,
Api.StackItem.ExplicitCall(
Api.MethodPointer(moduleName, moduleName, "main"),
None,
Vector()
)
)
)
)
context.receiveNIgnoreStdLib(2) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PushContextResponse(contextId)),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("42")
// rename operator1
val newName = "function2"
val expectedEdits = Vector(
TextEdit(
model.Range(model.Position(2, 0), model.Position(2, 9)),
newName
),
TextEdit(
model.Range(model.Position(5, 16), model.Position(5, 25)),
newName
)
)
val expectedFileEdit = Api.FileEdit(
context.pkg.mainFile,
expectedEdits,
versionCalculator.evalVersion(contents).toHexString,
versionCalculator
.evalVersion(contents.replaceAll("function1", newName))
.toHexString
)
context.send(
Api.Request(requestId, Api.RenameSymbol(moduleName, idFunction1, newName))
)
context.receiveNIgnoreStdLib(4) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.SymbolRenamed(newName)),
Api.Response(None, expectedFileEdit),
TestMessages.pending(contextId, idFunction1),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("42")
}
it should "rename unqualified module method in lambda expression" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
val metadata = new Metadata
val idFunction1 = metadata.addItem(31, 9)
val code =
"""from Standard.Base import all
|
|function1 x = x + 1
|
|main =
| operator1 = 41
| operator2 = x -> function1 x
| IO.println (operator2 operator1)
|""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code)
val mainFile = context.writeMain(contents)
// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
context.receive shouldEqual Some(
Api.Response(requestId, Api.CreateContextResponse(contextId))
)
// open file
context.send(
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
)
context.receive shouldEqual Some(
Api.Response(Some(requestId), Api.OpenFileResponse)
)
// push main
context.send(
Api.Request(
requestId,
Api.PushContextRequest(
contextId,
Api.StackItem.ExplicitCall(
Api.MethodPointer(moduleName, moduleName, "main"),
None,
Vector()
)
)
)
)
context.receiveNIgnoreStdLib(2) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PushContextResponse(contextId)),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("42")
// rename operator1
val newName = "function2"
val expectedEdits = Vector(
TextEdit(
model.Range(model.Position(2, 0), model.Position(2, 9)),
newName
),
TextEdit(
model.Range(model.Position(6, 21), model.Position(6, 30)),
newName
)
)
val expectedFileEdit = Api.FileEdit(
context.pkg.mainFile,
expectedEdits,
versionCalculator.evalVersion(contents).toHexString,
versionCalculator
.evalVersion(contents.replaceAll("function1", newName))
.toHexString
)
context.send(
Api.Request(requestId, Api.RenameSymbol(moduleName, idFunction1, newName))
)
context.receiveNIgnoreStdLib(4) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.SymbolRenamed(newName)),
Api.Response(None, expectedFileEdit),
TestMessages.pending(contextId, idFunction1),

View File

@ -445,7 +445,7 @@ final class TreeToIr {
} else {
arg = new Name.Qualified(tail, loc, meta());
}
var ca = new CallArgument.Specified(Option.empty(), arg, loc, meta());
var ca = new CallArgument.Specified(Option.empty(), arg, false, loc, meta());
args = join(ca, args);
yield name;
}
@ -453,7 +453,7 @@ final class TreeToIr {
if (in == null) {
return new Application.Prefix(type, args, false, getIdentifiedLocation(app), meta());
} else {
var fn = new CallArgument.Specified(Option.empty(), type, getIdentifiedLocation(app), meta());
var fn = new CallArgument.Specified(Option.empty(), type, false, getIdentifiedLocation(app), meta());
return new Operator.Binary(fn, in, args.head(), getIdentifiedLocation(app), meta());
}
}
@ -652,14 +652,14 @@ final class TreeToIr {
case Tree.App app -> {
var expr = translateExpression(app.getArg(), false);
var loc = getIdentifiedLocation(app.getArg());
args.add(new CallArgument.Specified(Option.empty(), expr, loc, meta()));
args.add(new CallArgument.Specified(Option.empty(), expr, false, loc, meta()));
tree = app.getFunc();
}
case Tree.NamedApp app -> {
var expr = translateExpression(app.getArg(), false);
var loc = getIdentifiedLocation(app.getArg());
var id = buildName(app, app.getName());
args.add(new CallArgument.Specified(Option.apply(id), expr, loc, meta()));
args.add(new CallArgument.Specified(Option.apply(id), expr, false, loc, meta()));
tree = app.getFunc();
}
case Tree.OperatorBlockApplication app -> {
@ -675,16 +675,16 @@ final class TreeToIr {
}
var expr = switch (translateExpression(l.getExpression().getExpression(), true)) {
case Application.Prefix pref -> {
var arg = new CallArgument.Specified(Option.empty(), self, self.identifiedLocation(), meta());
var arg = new CallArgument.Specified(Option.empty(), self, false, self.identifiedLocation(), meta());
yield new Application.Prefix(pref.function(), join(arg, pref.arguments()), false, pref.identifiedLocation(), meta());
}
case Expression any -> {
var arg = new CallArgument.Specified(Option.empty(), self, self.identifiedLocation(), meta());
var arg = new CallArgument.Specified(Option.empty(), self, false, self.identifiedLocation(), meta());
yield new Application.Prefix(any, join(arg, nil()), false, any.identifiedLocation(), meta());
}
};
var loc = getIdentifiedLocation(l.getExpression().getExpression());
args.add(at, new CallArgument.Specified(Option.empty(), expr, loc, meta()));
args.add(at, new CallArgument.Specified(Option.empty(), expr, false, loc, meta()));
self = expr;
}
return self;
@ -701,7 +701,7 @@ final class TreeToIr {
if (oprApp.getLhs() != null) {
var self = translateExpression(oprApp.getLhs(), isMethod);
var loc = getIdentifiedLocation(oprApp.getLhs());
args.add(new CallArgument.Specified(Option.empty(), self, loc, meta()));
args.add(new CallArgument.Specified(Option.empty(), self, false, loc, meta()));
}
} else if (args.isEmpty()) {
return null;
@ -878,7 +878,7 @@ final class TreeToIr {
var loc = getIdentifiedLocation(app);
var both = applyOperator(op, lhs, rhs, loc);
expr = both;
lhs = new CallArgument.Specified(Option.empty(), expr, loc, meta());
lhs = new CallArgument.Specified(Option.empty(), expr, false, loc, meta());
}
yield expr;
}
@ -1033,7 +1033,7 @@ final class TreeToIr {
);
case Expression expr -> {
var negate = new Name.Literal("negate", true, null, Option.empty(), meta());
var arg = new CallArgument.Specified(Option.empty(), expr, expr.identifiedLocation(), meta());
var arg = new CallArgument.Specified(Option.empty(), expr, false, expr.identifiedLocation(), meta());
yield new Application.Prefix(negate, join(arg, nil()), false, getIdentifiedLocation(un), meta());
}
case null ->
@ -1080,6 +1080,7 @@ final class TreeToIr {
var methodReference = new CallArgument.Specified(
Option.empty(),
methodName,
false,
methodName.identifiedLocation(),
meta()
);
@ -1394,7 +1395,7 @@ final class TreeToIr {
args = (List<CallArgument>) args.tail();
}
List<CallArgument> allArgs = (List<CallArgument>) pref.arguments().appendedAll(args.reverse());
final CallArgument.Specified blockArg = new CallArgument.Specified(Option.empty(), block, block.identifiedLocation(), meta());
final CallArgument.Specified blockArg = new CallArgument.Specified(Option.empty(), block, false, block.identifiedLocation(), meta());
List<CallArgument> withBlockArgs = (List<CallArgument>) allArgs.appended(blockArg);
if (!checkArgs(withBlockArgs)) {
return translateSyntaxError(pref.location().get(), Syntax.UnexpectedExpression$.MODULE$);
@ -1534,12 +1535,12 @@ final class TreeToIr {
case Tree.NamedApp app -> {
var expr = translateExpression(app.getArg(), false);
var id = sanitizeName(buildName(app, app.getName()));
yield new CallArgument.Specified(Option.apply(id), expr, loc, meta());
yield new CallArgument.Specified(Option.apply(id), expr, false, loc, meta());
}
case null -> null;
default -> {
var expr = translateExpression(arg, false);
yield new CallArgument.Specified(Option.empty(), expr, loc, meta());
yield new CallArgument.Specified(Option.empty(), expr, false, loc, meta());
}
};
}
@ -1547,7 +1548,7 @@ final class TreeToIr {
CallArgument.Specified translateTypeCallArgument(Tree arg) {
var loc = getIdentifiedLocation(arg);
var expr = translateType(arg);
return new CallArgument.Specified(Option.empty(), expr, loc, meta());
return new CallArgument.Specified(Option.empty(), expr, false, loc, meta());
}
CallArgument.Specified unnamedCallArgument(Tree arg) {
@ -1556,7 +1557,7 @@ final class TreeToIr {
}
var loc = getIdentifiedLocation(arg);
var expr = translateExpression(arg);
return new CallArgument.Specified(Option.empty(), expr, loc, meta());
return new CallArgument.Specified(Option.empty(), expr, false, loc, meta());
}
/**

View File

@ -9,10 +9,13 @@ import java.util.UUID
sealed trait CallArgument extends IR {
/** The name of the argument, if present. */
val name: Option[Name]
def name: Option[Name]
/** The expression of the argument, if present. */
val value: Expression
def value: Expression
/** Flag indicating that the argument was generated by compiler. */
def isSynthetic: Boolean
/** @inheritdoc */
override def mapExpressions(
@ -37,12 +40,14 @@ object CallArgument {
*
* @param name the name of the argument being called, if present
* @param value the expression being passed as the argument's value
* @param isSynthetic the flag indicating that the argument was generated by compiler
* @param identifiedLocation the source location that the node corresponds to
* @param passData the pass metadata associated with this node
*/
sealed case class Specified(
override val name: Option[Name],
override val value: Expression,
override val isSynthetic: Boolean,
identifiedLocation: IdentifiedLocation,
passData: MetadataStorage = new MetadataStorage()
) extends CallArgument
@ -52,17 +57,19 @@ object CallArgument {
/** Creates a copy of `this`.
*
* @param name the name of the argument being called, if present
* @param value the expression being passed as the argument's value
* @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node
* @param id the identifier for the new node
* @param name the name of the argument being called, if present
* @param value the expression being passed as the argument's value
* @param isSynthetic the flag indicating that the argument was generated by compiler
* @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node
* @param id the identifier for the new node
* @return a copy of `this`, updated with the specified values
*/
def copy(
name: Option[Name] = name,
value: Expression = value,
isSynthetic: Boolean = isSynthetic,
location: Option[IdentifiedLocation] = location,
passData: MetadataStorage = passData,
diagnostics: DiagnosticStorage = diagnostics,
@ -76,7 +83,7 @@ object CallArgument {
|| diagnostics != this.diagnostics
|| id != this.id
) {
val res = Specified(name, value, location.orNull, passData)
val res = Specified(name, value, isSynthetic, location.orNull, passData)
res.diagnostics = diagnostics
res.id = id
res

View File

@ -2472,7 +2472,7 @@ class IrToTruffle(
subjectToInstrumentation: Boolean
): callable.argument.CallArgument =
arg match {
case CallArgument.Specified(name, value, _, _) =>
case CallArgument.Specified(name, value, _, _, _) =>
val scopeInfo = childScopeInfo("call argument", arg)
def valueHasSomeTypeCheck() =