Skip to content

String f interpolator macro #6540

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

Merged
merged 38 commits into from
Jun 19, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5adbe41
Add f-interpolator
May 14, 2019
3ee576e
Add positive tests
May 14, 2019
55a871d
Add negative tests but it fails due to imports
May 16, 2019
098269d
Fix small error found with report.
May 16, 2019
72333e3
Use same macro in negative tests
May 21, 2019
83b7412
Revert last commit as does not work
May 21, 2019
6771222
fix compilation error, out of bounds to fix
May 21, 2019
1b67f43
Fix out of bounds but needs type check
May 21, 2019
efe3b46
Add type widen
May 22, 2019
26a5d2b
Avoid ambiguities between scala.StringContext and internal.StringContext
nicolasstucki May 14, 2019
0ed1c76
Rename src-2.x StringContext to StringContextMacro
nicolasstucki May 23, 2019
d93063f
Rename internal.StringContext to internal.StringContextMacro
nicolasstucki May 23, 2019
d94c856
Merge branch 'master' of https://github.com/lampepfl/dotty into part1
May 23, 2019
fc14fb4
Update StringContextMacro.scala
May 23, 2019
9209761
Delete .check tests for negative testing
May 23, 2019
9578c51
Ignore type checking
May 27, 2019
bd3d88a
Add dealias to have types without packages
May 27, 2019
0d3d83b
Update StringContextMacro.scala
May 29, 2019
bd404ee
Add ' ' for the type expected and actual
May 31, 2019
3de568d
Update StringContextMacro.scala
May 31, 2019
835a701
Update StringContextMacro.scala
May 31, 2019
3a1e403
Fix errors on tests and test/fix code
Jun 3, 2019
c27c7a2
Update StringContextMacro.scala
Jun 3, 2019
956572f
Update StringContextMacro.scala
Jun 3, 2019
d214141
Update StringContextMacro.scala
Jun 4, 2019
5969614
Update StringContextMacro.scala
Jun 4, 2019
e6caa6f
Update Macros_1.scala
Jun 4, 2019
26b5080
Revert "Update Macros_1.scala"
Jun 4, 2019
f4ceaf3
Update StringContextMacro.scala
Jun 4, 2019
a9c8fe3
Fixed indexed argument errors
Jun 4, 2019
7b67153
Merge remote-tracking branch 'upstream/master' into part1
Jun 11, 2019
729c018
Try to set the local for time testing
Jun 11, 2019
b426f35
Remove time local and comment time tests
Jun 11, 2019
0f1c9e1
Remove server dependent tests
Jun 11, 2019
db2b39d
Add flags check for specials
Jun 16, 2019
1e6ce2a
Modify expression extraction
Jun 18, 2019
752ae03
Update Macros_1.scala
Jun 18, 2019
3e38ef3
Update StringContextMacro.scala
Jun 18, 2019
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
5 changes: 1 addition & 4 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1184,10 +1184,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val baseType = tree.tpe.baseType(defn.QuotedExprClass)
val argType =
if (baseType != NoType) baseType.argTypesHi.head
else {
assert(ctx.reporter.hasErrors)
defn.NothingType
}
else defn.NothingType
ref(defn.InternalQuoted_exprSplice).appliedToType(argType).appliedTo(tree)
}
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match {
Expand Down
778 changes: 769 additions & 9 deletions library/src-3.x/dotty/internal/StringContext.scala

Large diffs are not rendered by default.

719 changes: 719 additions & 0 deletions tests/run-macros/f-interpolator-neg/Macros_1.scala

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions tests/run-macros/f-interpolator-neg/Tests_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
object Test {

def main(args: Array[String]): Unit = {
val s = "Scala"
val d = 8
val b = false
val f = 3.14159
val c = 'c'
val t = new java.util.Date
val x = new java.util.Formattable {
def formatTo(ff: java.util.Formatter, g: Int, w: Int, p: Int): Unit = ff format "xxx"
}
numberArgumentsTests(s, d)
interpolationMismatches(s, f, b)
flagMismatches(s, c, d, f, t)
badPrecisions(c, d, f, t)
badIndexes()
warnings(s)
badArgTypes(s)
misunderstoodConversions(t, s)
otherBrainFailures(d)
}

def numberArgumentsTests(s : String, d : Int) = {
import TestFooErrors._
assertEquals(new StringContext().foo(), List((true, 2, -1, 0, "there are no parts")))
assertEquals(new StringContext("", " is ", "%2d years old").foo(s), List((true, 1, 0, 0, "too few arguments for interpolated string")))
assertEquals(new StringContext("", " is ", "%2d years old").foo(s, d, d), List((true, 1, 2, 0, "too many arguments for interpolated string")))
assertEquals(new StringContext("", "").foo(), List((true, 3, -1, 0, "too few arguments for interpolated string")))
}

def interpolationMismatches(s : String, f : Double, b : Boolean) = {
import TestFooErrors._
assertEquals(foo"$s%b", List((true, 1, 1, 0, "type mismatch;\nfound : String\nrequired: Boolean")))
assertEquals(foo"$s%c", List((true, 1, 1, 0, "type mismatch;\nfound : String\nrequired: Char")))
assertEquals(foo"$f%c", List((true, 1, 1, 0, "type mismatch;\nfound : Double\nrequired: Char")))
assertEquals(foo"$s%x", List((true, 1, 1, 0, "type mismatch;\nfound : String\nrequired: Int")))
assertEquals(foo"$b%d", List((true, 1, 1, 0, "type mismatch;\nfound : Boolean\nrequired: Int")))
assertEquals(foo"$s%d", List((true, 1, 1, 0, "type mismatch;\nfound : String\nrequired: Int")))
assertEquals(foo"$f%o", List((true, 1, 1, 0, "type mismatch;\nfound : Double\nrequired: Int")))
assertEquals(foo"$s%e", List((true, 1, 1, 0, "type mismatch;\nfound : String\nrequired: Double")))
assertEquals(foo"$b%f", List((true, 1, 1, 0, "type mismatch;\nfound : Boolean\nrequired: Double")))
{
implicit val strToInt1 = (s: String) => 1
implicit val strToInt2 = (s: String) => 2
assertEquals(foo"$s%d", List((true, 1, 1, 0, "type mismatch;\nfound : String\nrequired: Int\nNote that implicit conversions are not applicable because they are ambiguous:\nboth value strToInt2 of type String => Int\nand value strToInt1 of type String => Int\nare possible conversion functions from String to Int")))
}

assertEquals(foo"$s%i", List((true, 0, 1, 1, "illegal conversion character 'i'")))
}

def flagMismatches(s : String, c : Char, d : Int, f : Double, t : java.util.Date) = {
import TestFooErrors._
assertEquals(foo"$s%+ 0,(s", List((true, 0, 1, 1, "Illegal flag '+'"), (true, 0, 1, 2, "Illegal flag ' '"),
(true, 0, 1, 3, "Illegal flag '0'"), (true, 0, 1, 4, "Illegal flag ','"), (true, 0, 1, 5, "Illegal flag '('")))
assertEquals(foo"$c%#+ 0,(c", List((true, 0, 1, 1, "Only '-' allowed for c conversion")))
assertEquals(foo"$d%#d", List((true, 0, 1, 1, "# not allowed for d conversion")))
assertEquals(foo"$d%,x", List((true, 0, 1, 1, "',' only allowed for d conversion of integral types")))
assertEquals(foo"$d%+ (x", List((true, 0, 1, 1, "only use '+' for BigInt conversions to o, x, X"), (true, 0, 1, 2, "only use ' ' for BigInt conversions to o, x, X"),
(true, 0, 1, 3, "only use '(' for BigInt conversions to o, x, X")))
assertEquals(foo"$f%,(a", List((true, 0, 1, 1, "',' not allowed for a, A"), (true, 0, 1, 2, "'(' not allowed for a, A")))
assertEquals(foo"$t%#+ 0,(tT", List((true, 0, 1, 1, "Only '-' allowed for date/time conversions")))
}

def badPrecisions(c : Char, d : Int, f : Double, t : java.util.Date) = {
import TestFooErrors._
assertEquals(foo"$c%.2c", List((true, 0, 1, 1, "precision not allowed")))
assertEquals(foo"$d%.2d", List((true, 0, 1, 1, "precision not allowed")))
assertEquals(foo"%.2%", List((true, 0, 1, 1, "precision not allowed")))
assertEquals(foo"%.2n", List((true, 0, 1, 1, "precision not allowed")))
assertEquals(foo"$f%.2a", List((true, 0, 1, 1, "precision not allowed")))
assertEquals(foo"$t%.2tT", List((true, 0, 1, 1, "precision not allowed")))
}

def badIndexes() = {
import TestFooErrors._
assertEquals(foo"%<s", List((true, 0, 1, 1, "No last arg")))
assertEquals(foo"%<c", List((true, 0, 1, 1, "No last arg")))
assertEquals(foo"%<tT", List((true, 0, 1, 1, "No last arg")))
assertEquals(foo"${8}%d ${9}%d %3$$d", List((true, 0, 3, 1, "Argument index out of range")))
assertEquals(foo"${8}%d ${9}%d%0$$d", List((true, 0, 3, 1, "Argument index out of range")))
}

def warnings(s : String) = {
import TestFooErrors._
assertEquals(foo"${8}%d ${9}%1$$d", List((false, 0, 2, 1, "Index is not this arg")))
assertEquals(foo"$s%s $s%s %1$$<s", List((false, 0, 3, 1, "Argument index ignored if '<' flag is present")))
assertEquals(foo"$s%s $s%1$$s", List((false, 0, 2, 1, "Index is not this arg")))
}

def badArgTypes(s : String) = {
import TestFooErrors._
assertEquals(foo"$s%#s", List((true, 1, 1, 0, "error: type mismatch;\nfound : String\nrequired: java.util.Formattable")))
}

def misunderstoodConversions(t : java.util.Date, s : String) = {
import TestFooErrors._
assertEquals(foo"$t%tG", List((true, 0, 1, 2, "'G' doesn't seem to be a date or time conversion")))
assertEquals(foo$"$t%t", List((true, 0, 1, 1, "Date/time conversion must have two characters")))
assertEquals(foo$"$s%10.5", List((true, 0, 1, 0, "Missing conversion operator in '%10.5'; use %% for literal %, %n for newline")))
}

def otherBrainFailures(d : Int) = {
import TestFooErrors._
assertEquals(foo"${d}random-leading-junk%d", List((true, 0, 1, 19, "conversions must follow a splice; use %% for literal %, %n for newline")))
assertEquals(StringContext().foo(), List((true, 2, -1, 0, "there are no parts")))
assertEquals(foo"%1$$n", Nil)
assertEquals(foo"%1$$d", List((true, 0, 1, 1, "Argument index out of range")))
assertEquals(foo"blablablabla %% %.2d", List((true, 0, 1, 17, "precision not allowed")))
assertEquals(foo"blablablabla %.2b %%", List((true, 0, 1, 13, "conversions must follow a splice; use %% for literal %, %n for newline")))

assertEquals(foo"ana${3}%.2foo%2${true}%bb", List((true, 0, 3, 1, "Missing conversion operator in '%2'; use %% for literal %, %n for newline")))
assertEquals(foo"ac{2c{2{c.ca ", Nil)
assertEquals(foo"b%c.%2ii%iin", List((true, 0, 1, 1, "conversions must follow a splice; use %% for literal %, %n for newline"),
(true, 0, 1, 5, "illegal conversion character 'i'"), (true, 0, 1, 8, "illegal conversion character 'i'")))
assertEquals(foo"b}22%2.c<{%{" , List((true, 0, 1, 4, "Missing conversion operator in '%2'; use %% for literal %, %n for newline"),
(true, 0, 1, 10, "Missing conversion operator in '%'; use %% for literal %, %n for newline")))
assertEquals(foo"%%bci.2${'i'}%..2c2", List((true, 0, 2, 1, "Missing conversion operator in '%'; use %% for literal %, %n for newline")))
}

def assertEquals(actual: Any, expected: Any): Unit = {
assert(actual == expected, s"actual: $actual\nbut expected: $expected")
}
}
70 changes: 70 additions & 0 deletions tests/run-macros/f-interpolator-tests.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
integer: 5
string: l
5, 6, hello
5
6
Bob is 1 years old
Bob will be 2 years old
1+1 = 2
Bob is 12 years old
Bob will be 13 years old
12+1 = 13
Bob is 123 years old
Bob will be 124 years old
123+1 = 124
Best price: 10.00
10.00% discount included
Best price: 13.35
13.35% discount included
Bob is 1 years old!
Bob is 1 years old!
Bob is 1 years old!
Bob is 1%2d years old!
===============
Bob is 12 years old!
Bob is 12 years old!
Bob is 12 years old!
Bob is 12%2d years old!
===============
Bob is 123 years old!
Bob is 123 years old!
Bob is 123 years old!
Bob is 123%2d years old!
The boolean is false
The boolean is true
a
The string is null
The string is
The string is string1
The string is null
The string is
The string is string2
The unicode character is c
The decimal integer is 2, 2, 2+1 = 3
The octal integer is 2
The octal integer is 10
The hexadecimal integer is 2
The hexadecimal integer is 20
The scientific notation is 2.000000e+00
The scientific notation is 5.430000e-01
The decimal floating point is 1234.567749, 1234.57, 1234.568
The decimal floating point is 10
The float value is 0.000100000
The float value is 1.00000e-05
The float value is -0x1.4f8b58p-17
The float value is 0x0.0p0
The float value is NaN
The float value is Infinity
The float value is -Infinity
10
10
10
10
20
56
000
000000000
am
the percentage is 100 %
we have a line separator now %n and now, we are on the next line
a b b
Loading