Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix names of anonymous classes defined in lambdas #5207

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Comment on lines -122 to +127
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a typo I saw while working on this. I could move this to a separate PR, but I thought I'd include it here since the PR is relatively small anyways


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
Loading