diff --git a/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/AstCreator.scala b/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/AstCreator.scala index 3b6929c50bb1..be62836b169f 100644 --- a/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/AstCreator.scala +++ b/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/AstCreator.scala @@ -7,14 +7,7 @@ import io.joern.rust2cpg.parser.RustNodeSyntax import io.joern.rust2cpg.parser.RustNodeSyntax.RustNode import io.joern.x2cpg.datastructures.Stack.* import io.joern.x2cpg.{Ast, AstCreatorBase, ValidationMode} -import io.shiftleft.codepropertygraph.generated.nodes.{ - NewCall, - NewControlStructure, - NewMethod, - NewNamespaceBlock, - NewNode, - NewTypeDecl -} +import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewMethod, NewNamespaceBlock, NewNode, NewTypeDecl} import io.shiftleft.codepropertygraph.generated.{NodeTypes, Operators, PropertyDefaults, PropertyNames} import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal import org.slf4j.LoggerFactory @@ -29,14 +22,20 @@ class AstCreator(val config: Config, val parseResult: ParseResult)(implicit with private val logger = LoggerFactory.getLogger(getClass) protected val methodAstParentStack = new Stack[NewNode] + private var detachedAsts = List.empty[Ast] override def createAst(): DiffGraphBuilder = { val sourceFile = parseResult.ast.asInstanceOf[RustNodeSyntax.SourceFile] val ast = visitSourceFile(sourceFile) Ast.storeInDiffGraph(ast, diffGraph) + detachedAsts.foreach(Ast.storeInDiffGraph(_, diffGraph)) diffGraph } + protected def addDetachedAst(ast: Ast): Unit = { + detachedAsts = ast :: detachedAsts + } + // NB: rust_ast_gen uses 0-based line/column override protected def line(node: RustNode): Option[Int] = if (node.isMacroExpanded) None else node.startLine.map(_ + 1) diff --git a/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/RustVisitor.scala b/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/RustVisitor.scala index 5003b0ec1d2e..1a639b4cf6a4 100644 --- a/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/RustVisitor.scala +++ b/joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/RustVisitor.scala @@ -355,25 +355,35 @@ trait RustVisitor(implicit withSchemaValidation: ValidationMode) { this: AstCrea // AssocItemList private def visitImpl(impl: Impl): Seq[Ast] = { if (impl.forKwToken.isDefined) { - impl.typ match { - case implTrait :: implType :: Nil => - val typeDecl = typeDeclForTraitImpl(impl, implTrait, implType) - methodAstParentStack.push(typeDecl) - val methodAsts = impl.assocItemList.assocItem.collect { case fn: Fn => visitFn(fn) } - methodAstParentStack.pop() - Ast(typeDecl).withChildren(methodAsts) :: Nil - case _ => notHandledYet(impl) :: Nil - } + lowerImplFor(impl) :: Nil } else { - methodAstParentStack.push(typeDeclForImpl(impl)) - val methodAsts = impl.assocItemList.assocItem.collect { case fn: Fn => visitFn(fn) } - methodAstParentStack.pop() - // TODO: remove this side-effect once ref-linker/scope is in. - methodAsts.foreach(Ast.storeInDiffGraph(_, diffGraph)) + lowerInherentImplAsDetachedAst(impl) Nil } } + // There can be at most one `impl X for Y` (for some X, Y) in the same file/project. + private def lowerImplFor(impl: Impl): Ast = { + impl.typ match { + case implTrait :: implType :: Nil => + val typeDecl = typeDeclForTraitImpl(impl, implTrait, implType) + methodAstParentStack.push(typeDecl) + val methodAsts = impl.assocItemList.assocItem.collect { case fn: Fn => visitFn(fn) } + methodAstParentStack.pop() + Ast(typeDecl).withChildren(methodAsts) + case _ => notHandledYet(impl) + } + } + + // There can be multiple `impl X` in the same file/project, so we lower their + // methods as detached Asts. The AstLinker pass shall later create the appropriate Ast edges. + private def lowerInherentImplAsDetachedAst(impl: Impl): Unit = { + methodAstParentStack.push(typeDeclForImpl(impl)) + val methodAsts = impl.assocItemList.assocItem.collect { case fn: Fn => visitFn(fn) } + methodAstParentStack.pop() + methodAsts.foreach(addDetachedAst) + } + // Trait = // Attr* Visibility? // 'unsafe'? 'auto'?