Skip to content

Commit 8f7d5ba

Browse files
ursachecmpollmeier
andauthored
Add macro for generating modified Query instantiations (#1098)
* Add `macros` project * Add temporary usage of macro * Retrieve source positions * upgrade to scala 2.13.4 fixes the expression.end issue in macro * scalameta macros - not a good idea probably * more WIP * more wip * use wildcard types * cleanup * more cleanup * bring both together, make them both whitebox * move over to console project * Return Query in queryInit macro * Reintroduce query properties To be removed _after_ they're removed from the query database. * Reenable deprecation warnings * Run scalafmt Co-authored-by: Michael Pollmeier <michael@michaelpollmeier.com>
1 parent c71dc53 commit 8f7d5ba

7 files changed

Lines changed: 81 additions & 5 deletions

File tree

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ lazy val cpgvalidator = Projects.cpgvalidator
6767
lazy val console = Projects.console
6868
lazy val fuzzyc2cpg = Projects.fuzzyc2cpg
6969
lazy val fuzzyc2cpgtests = Projects.fuzzyc2cpgtests
70+
lazy val macros = Projects.macros
7071

7172
ThisBuild/scalacOptions ++= Seq(
7273
"-deprecation",

console/build.sbt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ name := "console"
33
enablePlugins(JavaAppPackaging)
44

55
dependsOn(Projects.codepropertygraph,
6-
Projects.semanticcpg)
6+
Projects.semanticcpg,
7+
Projects.macros)
78

89
scalacOptions ++= Seq(
910
"-deprecation", // Emit warning and location for usages of deprecated APIs.
@@ -37,7 +38,8 @@ scalacOptions ++= Seq(
3738
"-Ywarn-unused:locals", // Warn if a local definition is unused.
3839
"-Ywarn-unused:params", // Warn if a value parameter is unused.
3940
"-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused.
40-
"-Ywarn-unused:privates" // Warn if a private member is unused.
41+
"-Ywarn-unused:privates", // Warn if a private member is unused.
42+
"-Yrangepos"
4143
)
4244

4345
// would love to reenable, but somehow StorageBackend.scala triggers a strange `[warn] method with a single empty parameter list overrides method without any parameter list` that doesn't make sense to me...

console/src/main/scala/io/shiftleft/console/QueryDatabase.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ case class Query(name: String,
2727
docEndLine: Int = 0,
2828
// intended to be filled by com.lihaoyi.sourcecode.FileName
2929
docFileName: String = "",
30+
traversalAsString: String = "",
3031
tags: List[String] = List())
3132

3233
class QueryDatabase(defaultArgumentProvider: DefaultArgumentProvider = new DefaultArgumentProvider,

console/src/test/scala/io/shiftleft/console/QueryDatabaseTests.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.shiftleft.console
22

33
import io.shiftleft.semanticcpg.language._
4+
import io.shiftleft.codepropertygraph.Cpg
45
import org.scalatest.matchers.should
56
import org.scalatest.wordspec.AnyWordSpec
7+
import overflowdb.traversal.Traversal
8+
import io.shiftleft.macros.QueryMacros
69

710
object TestBundle extends QueryBundle {
811
@q def foo(n: Int = 4): Query = Query(
@@ -11,9 +14,7 @@ object TestBundle extends QueryBundle {
1114
title = "a-title",
1215
description = s"a-description $n",
1316
score = 2.0,
14-
traversal = { cpg =>
15-
cpg.method
16-
}
17+
traversal = { cpg => cpg.method }
1718
)
1819
}
1920

@@ -35,5 +36,16 @@ class QueryDatabaseTests extends AnyWordSpec with should.Matchers {
3536
val queries = qdb.queriesInBundle(testBundle)
3637
queries.count(_.title == "a-title") shouldBe 1
3738
}
39+
40+
"serialize traversal to string" in {
41+
val query = QueryMacros.queryInit(
42+
"a-name",
43+
"an-author",
44+
"a-title",
45+
"a-description",
46+
2.0, { cpg: Cpg => cpg.method }).asInstanceOf[Query]
47+
query.title shouldBe "a-title"
48+
query.traversalAsString shouldBe "cpg: Cpg => cpg.method"
49+
}
3850
}
3951
}

macros/build.sbt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name := "macros"
2+
3+
enablePlugins(JavaAppPackaging)
4+
5+
dependsOn(Projects.codepropertygraph)
6+
scalacOptions ++= Seq( "-Yrangepos" )
7+
8+
libraryDependencies ++= Seq(
9+
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
10+
)
11+
12+
publishArtifact in (Test, packageBin) := true
13+
14+
// execute tests in root project so that they work in sbt *and* intellij
15+
Test / baseDirectory := (ThisBuild / Test / run / baseDirectory).value
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.shiftleft.macros
2+
3+
import io.shiftleft.codepropertygraph.Cpg
4+
import io.shiftleft.codepropertygraph.generated.nodes
5+
import overflowdb.traversal.Traversal
6+
import scala.language.experimental.macros
7+
import scala.reflect.macros.whitebox
8+
9+
object QueryMacros {
10+
11+
def queryInit(name: String,
12+
author: String,
13+
title: String,
14+
description: String,
15+
score: Double,
16+
traversal: Cpg => Traversal[nodes.StoredNode]): Any = macro queryInitImpl
17+
18+
def queryInitImpl(c: whitebox.Context)(name: c.Tree,
19+
author: c.Tree,
20+
title: c.Tree,
21+
description: c.Tree,
22+
score: c.Tree,
23+
traversal: c.Tree): c.Expr[Any] = {
24+
import c.universe._
25+
val fileContent = new String(traversal.pos.source.content)
26+
val start = traversal.pos.start
27+
val end = traversal.pos.end
28+
val traversalAsString: String = fileContent.slice(start, end)
29+
30+
c.Expr[Any](
31+
q"""
32+
Query(
33+
name = $name,
34+
author = $author,
35+
title = $title,
36+
description = $description,
37+
score = $score,
38+
traversal = $traversal,
39+
traversalAsString = $traversalAsString
40+
)
41+
"""
42+
)
43+
}
44+
}

project/Projects.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ object Projects {
1313
lazy val console = project.in(file("console"))
1414
lazy val fuzzyc2cpg = project.in(file("fuzzyc2cpg"))
1515
lazy val fuzzyc2cpgtests = project.in(file("fuzzyc2cpg-tests"))
16+
lazy val macros = project.in(file("macros"))
1617
}

0 commit comments

Comments
 (0)