Skip to content

Commit 575d3d8

Browse files
committed
carry position across ast
1 parent ba97d64 commit 575d3d8

File tree

23 files changed

+349
-165
lines changed

23 files changed

+349
-165
lines changed

quill-core/src/main/scala/io/getquill/context/VerifyFreeVariables.scala

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,28 @@ package io.getquill.context
22

33
import scala.reflect.macros.whitebox.{Context => MacroContext}
44
import io.getquill.quotation.FreeVariables
5-
import io.getquill.ast.Ast
5+
import io.getquill.ast.{Ast, Ident, Pos}
66
import io.getquill.util.MacroContextExt._
77

88
object VerifyFreeVariables {
99

10-
def apply(c: MacroContext)(ast: Ast): Ast =
10+
def apply(c: MacroContext)(ast: Ast): Ast = {
11+
import c.universe.{Ident => _, _}
12+
1113
FreeVariables.verify(ast) match {
1214
case Right(ast) => ast
13-
case Left(msg) => c.fail(msg)
15+
case Left(err) =>
16+
err.freeVars match {
17+
// we we have a single position from the encosing context in the same file we can actually fail
18+
// at the right position and point the compiler to that location since we can modify the position
19+
// by the `point` info that we have from our position
20+
case List(Ident.WithPos(_, Pos.Real(fileName, _, _, point, _))) if (c.enclosingPosition.source.path == fileName) =>
21+
c.failAtPoint(err.msgNoPos, point)
22+
23+
case _ =>
24+
c.fail(err.msg)
25+
}
26+
1427
}
28+
}
1529
}

quill-core/src/main/scala/io/getquill/quotation/Liftables.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ trait Liftables extends QuatLiftable {
124124
case Visibility.Hidden => q"$pack.Visibility.Hidden"
125125
}
126126

127+
implicit val positionLiftable: Liftable[Pos] = Liftable[Pos] {
128+
case Pos.Real(a, b, c, d, e) => q"$pack.Pos.Real($a, $b, $c, $d, $e)"
129+
case Pos.Synthetic => q"$pack.Pos.Synthetic"
130+
}
131+
127132
implicit val queryLiftable: Liftable[Query] = Liftable[Query] {
128133
case Entity.Opinionated(a, b, quat, renameable) => q"$pack.Entity.Opinionated($a, $b, $quat, $renameable)"
129134
case Filter(a, b, c) => q"$pack.Filter($a, $b, $c)"
@@ -206,8 +211,8 @@ trait Liftables extends QuatLiftable {
206211
case CaseClass(n, a) => q"$pack.CaseClass($n, $a)"
207212
}
208213

209-
implicit val identLiftable: Liftable[Ident] = Liftable[Ident] { case Ident(a, quat) =>
210-
q"$pack.Ident($a, $quat)"
214+
implicit val identLiftable: Liftable[Ident] = Liftable[Ident] { case Ident.Opinionated(a, quat, vis, pos) =>
215+
q"$pack.Ident.Opinionated($a, $quat, $vis, $pos)"
211216
}
212217
implicit val externalIdentLiftable: Liftable[ExternalIdent] = Liftable[ExternalIdent] { case ExternalIdent(a, quat) =>
213218
q"$pack.ExternalIdent($a, $quat)"

quill-core/src/main/scala/io/getquill/quotation/Parsing.scala

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait Parsing extends ValueComputation with QuatMaking with MacroUtilBase {
2727

2828
// Variables that need to be sanitized out in various places due to internal conflicts with the way
2929
// macros hard handled in MetaDsl
30-
private[getquill] val dangerousVariables: Set[IdentName] = Set(IdentName("v"))
30+
private[getquill] val dangerousVariables: Set[Ident] = Set(Ident.trivial("v"))
3131

3232
case class Parser[T](p: PartialFunction[Tree, T])(implicit ct: ClassTag[T]) {
3333

@@ -81,14 +81,14 @@ trait Parsing extends ValueComputation with QuatMaking with MacroUtilBase {
8181
case q"{..$exprs}" if exprs.size > 1 => Block(exprs.map(astParser(_)))
8282
}
8383

84-
val valParser: Parser[Val] = Parser[Val] { case q"val $name: $typ = $body" =>
84+
val valParser: Parser[Val] = Parser[Val] { case wholeExpr @ q"val $name: $typ = $body" =>
8585
// for some reason inferQuat(typ.tpe) causes a compile hang in scala.reflect.internal
8686
val bodyAst = astParser(body)
87-
Val(ident(name, bodyAst.quat), bodyAst)
87+
Val(ident(name, bodyAst.quat, wholeExpr.pos), bodyAst)
8888
}
8989

90-
val patMatchValParser: Parser[Val] = Parser[Val] { case q"$mods val $name: $typ = ${patMatchParser(value)}" =>
91-
Val(ident(name, inferQuat(q"$typ".tpe)), value)
90+
val patMatchValParser: Parser[Val] = Parser[Val] { case wholeExpr @ q"$mods val $name: $typ = ${patMatchParser(value)}" =>
91+
Val(ident(name, inferQuat(q"$typ".tpe), wholeExpr.pos), value)
9292
}
9393

9494
val patMatchParser: Parser[Ast] = Parser[Ast] { case q"$expr match { case ($fields) => $body }" =>
@@ -462,16 +462,28 @@ trait Parsing extends ValueComputation with QuatMaking with MacroUtilBase {
462462
val identParser: Parser[Ident] = Parser[Ident] {
463463
// TODO Check to see that all these conditions work
464464
case t: ValDef =>
465-
identClean(Ident(t.name.decodedName.toString, inferQuat(t.symbol.typeSignature)))
466-
case id @ c.universe.Ident(TermName(name)) => identClean(Ident(name, inferQuat(id.symbol.typeSignature)))
467-
case t @ q"$cls.this.$i" => identClean(Ident(i.decodedName.toString, inferQuat(t.symbol.typeSignature)))
465+
identClean(t.name.decodedName.toString, inferQuat(t.symbol.typeSignature), t.pos)
466+
case id @ c.universe.Ident(TermName(name)) =>
467+
identClean(name, inferQuat(id.symbol.typeSignature), id.pos)
468+
case t @ q"$cls.this.$i" =>
469+
identClean(i.decodedName.toString, inferQuat(t.symbol.typeSignature), t.pos)
468470
case t @ c.universe.Bind(TermName(name), c.universe.Ident(termNames.WILDCARD)) =>
469-
identClean(
470-
Ident(name, inferQuat(t.symbol.typeSignature))
471-
) // TODO Verify Quat what is the type of this thing? In what cases does it happen? Do we need to do something more clever with the tree and get a TypeRef?
471+
// TODO Verify Quat what is the type of this thing? In what cases does it happen? Do we need to do something more clever with the tree and get a TypeRef?
472+
identClean(name, inferQuat(t.symbol.typeSignature), t.pos)
472473
}
473-
private def identClean(x: Ident): Ident = x.copy(name = x.name.replace("$", ""))
474-
private def ident(x: TermName, quat: Quat): Ident = identClean(Ident(x.decodedName.toString, quat))
474+
private def identClean(name: String, quat: Quat, pos: Position): Ident =
475+
Ident.Opinionated(
476+
name.replace("$", ""),
477+
quat,
478+
Visibility.Visible,
479+
if (pos != NoPosition)
480+
Pos.Real(pos.source.path, pos.line, pos.column, pos.point, 0)
481+
else
482+
Pos.Synthetic
483+
)
484+
485+
private def ident(x: TermName, quat: Quat, pos: Position): Ident =
486+
identClean(x.decodedName.toString, quat, pos)
475487

476488
/**
477489
* In order to guarantee consistent behavior across multiple databases, we

quill-core/src/main/scala/io/getquill/quotation/Unliftables.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ trait Unliftables extends QuatUnliftable {
155155
case q"$pack.Visibility.Hidden" => Visibility.Hidden
156156
}
157157

158+
implicit val positionUnliftable: Unliftable[Pos] = Unliftable[Pos] {
159+
case q"$pack.Pos.Real.apply(${file: String}, ${row: Int}, ${column: Int}, ${point: Int}, ${width: Int})" => Pos.Real(file, row, column, point, width)
160+
case q"$pack.Pos.Synthetic" => Pos.Synthetic
161+
}
162+
158163
implicit val propertyUnliftable: Unliftable[Property] = Unliftable[Property] {
159164
case q"$pack.Property.apply(${a: Ast}, ${b: String})" => Property(a, b)
160165
case q"$pack.Property.Opinionated.apply(${a: Ast}, ${b: String}, ${renameable: Renameable}, ${visibility: Visibility})" =>
@@ -209,7 +214,8 @@ trait Unliftables extends QuatUnliftable {
209214
}
210215

211216
implicit val identUnliftable: Unliftable[Ident] = Unliftable[Ident] {
212-
case q"$pack.Ident.apply(${a: String}, ${quat: Quat})" => Ident(a, quat)
217+
case q"$pack.Ident.Opinionated.apply(${a: String}, ${quat: Quat}, ${vis: Visibility}, ${pos: Pos})" =>
218+
Ident.Opinionated(a, quat, vis, pos)
213219
}
214220
implicit val externalIdentUnliftable: Unliftable[ExternalIdent] = Unliftable[ExternalIdent] {
215221
case q"$pack.ExternalIdent.apply(${a: String}, ${quat: Quat})" => ExternalIdent(a, quat)

quill-core/src/main/scala/io/getquill/util/MacroContextExt.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import io.getquill.util.IndentUtil._
55
import io.getquill.util.Messages.{debugEnabled, errorPrefix, prettyPrint}
66
import io.getquill.quat.VerifyNoBranches
77

8+
import scala.reflect.api.Position
89
import scala.reflect.macros.blackbox.{Context => MacroContext}
910

1011
object MacroContextExt {
@@ -16,6 +17,11 @@ object MacroContextExt {
1617
def error(msg: String): Unit =
1718
c.error(c.enclosingPosition, if (errorPrefix) s"[quill] $msg" else msg)
1819

20+
def failAtPoint(msg: String, point: Int): Nothing = {
21+
val errorPos = c.enclosingPosition.withPoint(point)
22+
c.abort(errorPos, if (errorPrefix) s"[quill] $msg" else msg)
23+
}
24+
1925
def fail(msg: String): Nothing =
2026
c.abort(c.enclosingPosition, if (errorPrefix) s"[quill] $msg" else msg)
2127

quill-core/src/test/scala/io/getquill/quotation/FreeVariablesSpec.scala

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package io.getquill.quotation
22

3-
import io.getquill.ast.IdentName
43
import io.getquill.base.Spec
54
import io.getquill.MirrorContexts.testContext.implicitOrd
65
import io.getquill.MirrorContexts.testContext.qr1
76
import io.getquill.MirrorContexts.testContext.qr2
87
import io.getquill.MirrorContexts.testContext.quote
98
import io.getquill.MirrorContexts.testContext.unquote
9+
import io.getquill.ast.Ident
1010

1111
class FreeVariablesSpec extends Spec {
1212

@@ -16,77 +16,77 @@ class FreeVariablesSpec extends Spec {
1616
"detects references to values outside of the quotation (free variables)" - {
1717
"ident" in {
1818
val q = quote(s)
19-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
19+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
2020
}
2121
"function" in {
2222
val q =
2323
quote { (a: String) =>
2424
s
2525
}
26-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
26+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
2727
}
2828
"filter" in {
2929
val q =
3030
quote {
3131
qr1.filter(_.s == s)
3232
}
33-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
33+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
3434
}
3535
"map" in {
3636
val q =
3737
quote {
3838
qr1.map(_ => s)
3939
}
40-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
40+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
4141
}
4242
"flatMap" in {
4343
val q =
4444
quote {
4545
qr1.map(_ => s).flatMap(_ => qr2)
4646
}
47-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
47+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
4848
}
4949
"concatMap" in {
5050
val a = Seq(1, 2)
5151
val q =
5252
quote {
5353
qr1.concatMap(_ => a).flatMap(_ => qr2)
5454
}
55-
FreeVariables(q.ast) mustEqual Set(IdentName("a"))
55+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("a"))
5656
}
5757
"sortBy" in {
5858
val q =
5959
quote {
6060
qr1.sortBy(_ => s)
6161
}
62-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
62+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
6363
}
6464
"groupBy" in {
6565
val q =
6666
quote {
6767
qr1.groupBy(_ => s)
6868
}
69-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
69+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
7070
}
7171
"take" in {
7272
val q =
7373
quote {
7474
qr1.take(i)
7575
}
76-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
76+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
7777
}
7878
"conditional outer join" in {
7979
val q =
8080
quote {
8181
qr1.leftJoin(qr2).on((a, b) => a.s == s)
8282
}
83-
FreeVariables(q.ast) mustEqual Set(IdentName("s"))
83+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("s"))
8484
}
8585
"assignment" in {
8686
val q = quote {
8787
qr1.insert(_.i -> i)
8888
}
89-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
89+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
9090
}
9191
"join" in {
9292
val i = 1
@@ -95,37 +95,37 @@ class FreeVariablesSpec extends Spec {
9595
.join(qr2.filter(_.i == i))
9696
.on((t1, t2) => t1.i == t2.i)
9797
}
98-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
98+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
9999
}
100100
"option operators" - {
101101
"map" in {
102102
val i = 1
103103
val q = quote {
104104
qr1.map(_.o.map(_ == i))
105105
}
106-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
106+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
107107
}
108108
"forall" in {
109109
val i = 1
110110
val q = quote {
111111
qr1.filter(_.o.forall(_ == i))
112112
}
113-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
113+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
114114

115115
}
116116
"exists" in {
117117
val i = 1
118118
val q = quote {
119119
qr1.filter(_.o.exists(_ == i))
120120
}
121-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
121+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
122122
}
123123
"contains" in {
124124
val i = 1
125125
val q = quote {
126126
qr1.filter(_.o.contains(i))
127127
}
128-
FreeVariables(q.ast) mustEqual Set(IdentName("i"))
128+
FreeVariables(q.ast) mustEqual Set(Ident.trivial("i"))
129129
}
130130
}
131131
}

quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ trait MirrorIdiomBase extends Idiom {
270270
}
271271

272272
implicit final val identTokenizer: Tokenizer[Ident] = Tokenizer[Ident] {
273-
case Ident.Opinionated(name, _, visibility) =>
273+
case Ident.Opinionated(name, _, visibility, _) =>
274274
stmt"${bracketIfHidden(name, visibility).token}"
275275
}
276276

quill-engine/src/main/scala/io/getquill/MirrorSqlDialect.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.getquill
22

3+
import io.getquill.ast.Ident
34
import io.getquill.context.sql.idiom.{ConcatSupport, QuestionMarkBindVariables, SqlIdiom}
45
import io.getquill.context._
56
import io.getquill.norm.ProductAggregationToken
@@ -56,7 +57,7 @@ object MirrorSqlDialect extends MirrorSqlDialect {
5657
trait StrategizeElements extends SqlIdiom with QuestionMarkBindVariables with ConcatSupport with CanReturnField {
5758

5859
override def tokenizeIdentName(strategy: NamingStrategy, name: String): String = strategy.default(name)
59-
override def tokenizeTableAlias(strategy: NamingStrategy, table: String): String = strategy.default(table)
60+
override def tokenizeTableAlias(strategy: NamingStrategy, table: Ident): String = strategy.default(table.name)
6061
override def tokenizeColumnAlias(strategy: NamingStrategy, column: String): String = strategy.default(column)
6162
override def tokenizeFixedColumn(strategy: NamingStrategy, column: String): String = strategy.default(column)
6263
override def prepareForProbing(string: String) = string

quill-engine/src/main/scala/io/getquill/OracleDialect.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ trait OracleDialect
8282
override protected def tokenizeColumnAlias(strategy: NamingStrategy, column: String): String =
8383
tokenizeEscapeUnderscores(strategy, column, None)
8484

85-
override protected def tokenizeTableAlias(strategy: NamingStrategy, column: String): String =
86-
tokenizeEscapeUnderscores(strategy, column, None)
85+
override protected def tokenizeTableAlias(strategy: NamingStrategy, tableName: Ident): String =
86+
tokenizeEscapeUnderscores(strategy, tableName.name, None)
8787

8888
private def tokenizeEscapeUnderscores(
8989
strategy: NamingStrategy,

0 commit comments

Comments
 (0)