From 630c743c551b16ad10e795876951c5071319bf53 Mon Sep 17 00:00:00 2001 From: Johannes Coetzee Date: Mon, 6 Jan 2025 15:38:13 +0100 Subject: [PATCH] Fix names of anonymous classes defined in lambdas --- .../expressions/AstForLambdasCreator.scala | 17 +++++----- .../querying/AnonymousClassTests.scala | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForLambdasCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForLambdasCreator.scala index 467e938efa4b..ea336025a68f 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForLambdasCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForLambdasCreator.scala @@ -54,6 +54,7 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator => variablesInScope: Seq[ScopeVariable], expectedLambdaType: ExpectedType ): (NewMethod, LambdaBody) = { + val implementedMethod = implementedInfo.implementedMethod val implementedInterface = implementedInfo.implementedInterface @@ -61,8 +62,12 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator => // 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) @@ -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)) @@ -109,6 +112,7 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator => ast.withRefEdge(identifier, lambdaParameterNamesToNodes(identifier.name)) ) + scope.popMethodScope() scope.addLambdaMethod(lambdaMethodAst) lambdaMethodNode -> lambdaBody @@ -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) } @@ -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() @@ -237,7 +239,6 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator => val lambdaTypeDeclNode = createAndPushLambdaTypeDecl(lambdaMethodNode, implementedInfo) BindingTable.createBindingNodes(diffGraph, lambdaTypeDeclNode, bindingTable) - scope.popMethodScope() Ast(methodRef) } diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/AnonymousClassTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/AnonymousClassTests.scala index 0121371706a6..edc4fe60bd9d 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/AnonymousClassTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/AnonymousClassTests.scala @@ -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.0.Action$0", + "foo.Foo.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;