Skip to content

Commit

Permalink
Fix names of anonymous classes defined in lambdas (#5207)
Browse files Browse the repository at this point in the history
  • Loading branch information
johannescoetzee authored Jan 6, 2025
1 parent 76dd74a commit 578b422
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,20 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
variablesInScope: Seq[ScopeVariable],
expectedLambdaType: ExpectedType
): (NewMethod, LambdaBody) = {

val implementedMethod = implementedInfo.implementedMethod
val implementedInterface = implementedInfo.implementedInterface

// We need to get this information from the expected type as the JavaParser
// symbol solver returns the erased types when resolving the lambda itself.
val expectedTypeParamTypes = genericParamTypeMapForLambda(expectedLambdaType)
val parametersWithoutThis = buildParamListForLambda(expr, implementedMethod, expectedTypeParamTypes)
val returnType = getLambdaReturnType(implementedInterface, implementedMethod, expectedTypeParamTypes)

val returnType = getLambdaReturnType(implementedInterface, implementedMethod, expectedTypeParamTypes)
val lambdaMethodNode = createLambdaMethodNode(expr, lambdaMethodName, parametersWithoutThis, returnType)

// TODO: lambda method scope can be static if no non-static captures are used
scope.pushMethodScope(lambdaMethodNode, expectedLambdaType, isStatic = false)

val lambdaBody = astForLambdaBody(lambdaMethodName, expr.getBody, variablesInScope, returnType)

Expand All @@ -88,8 +93,6 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
.collect { case identifier: NewIdentifier => identifier }
.filter { identifier => lambdaParameterNamesToNodes.contains(identifier.name) }

val lambdaMethodNode = createLambdaMethodNode(expr, lambdaMethodName, parametersWithoutThis, returnType)

val returnNode = newMethodReturnNode(returnType.getOrElse(defaultTypeFallback()), None, line(expr), column(expr))
val virtualModifier = Some(newModifierNode(ModifierTypes.VIRTUAL))
val staticModifier = Option.when(thisParam.isEmpty)(newModifierNode(ModifierTypes.STATIC))
Expand All @@ -109,6 +112,7 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
ast.withRefEdge(identifier, lambdaParameterNamesToNodes(identifier.name))
)

scope.popMethodScope()
scope.addLambdaMethod(lambdaMethodAst)

lambdaMethodNode -> lambdaBody
Expand All @@ -119,8 +123,8 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
val containsEmptyType = maybeParameterTypes.exists(_.contains(ParameterDefaults.TypeFullName))

(returnType, maybeParameterTypes) match {
case (Some(returnTpe), Some(parameterTpes)) if !containsEmptyType =>
composeMethodLikeSignature(returnTpe, parameterTpes)
case (Some(returnType), Some(parameterTypes)) if !containsEmptyType =>
composeMethodLikeSignature(returnType, parameterTypes)

case _ => composeUnresolvedSignature(parameters.size)
}
Expand Down Expand Up @@ -207,8 +211,6 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
}

def astForLambdaExpr(expr: LambdaExpr, expectedType: ExpectedType): Ast = {
// TODO: lambda method scope can be static if no non-static captures are used
scope.pushMethodScope(NewMethod(), expectedType, isStatic = false)

val lambdaMethodName = nextClosureName()

Expand Down Expand Up @@ -237,7 +239,6 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
val lambdaTypeDeclNode = createAndPushLambdaTypeDecl(lambdaMethodNode, implementedInfo)
BindingTable.createBindingNodes(diffGraph, lambdaTypeDeclNode, bindingTable)

scope.popMethodScope()
Ast(methodRef)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@ import io.shiftleft.codepropertygraph.generated.Operators

class AnonymousClassTests extends JavaSrcCode2CpgFixture {

"mixed static/non-static anonymous classes with the same name as children of lambdas" should {
val cpg = code("""
|package foo;
|
|public class Foo {
|
| private static FirstProvider method1() {
| return firstTask -> {
| firstTask.doFirst(new Action() { });
| };
| }
|
| private SecondProvider method2() {
| return secondTask -> {
| secondTask.doSecond(new Action() { });
| };
| }
|}
|
|""".stripMargin)

"have the correct names" in {
cpg.typeDecl.name(".*Action.*").fullName.sorted.l shouldBe List(
"foo.Foo.<lambda>0.Action$0",
"foo.Foo.<lambda>1.Action$0"
)
}

"not result in any orphan locals" in {
!cpg.local.exists(_._astIn.isEmpty) shouldBe true
}

}

"simple anonymous classes extending interfaces in method bodies" should {
val cpg = code("""
|package foo;
Expand Down

0 comments on commit 578b422

Please sign in to comment.