Skip to content

Commit

Permalink
Support Extension Group using parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
kpbochenek committed Oct 6, 2020
1 parent 5ea2e00 commit f590d53
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 38 deletions.
18 changes: 3 additions & 15 deletions community-test/src/test/scala/CommunityDottySuite.scala
Expand Up @@ -163,9 +163,6 @@ class CommunityDottySuite extends FunSuite {
"/compiler/src/dotty/tools/dotc/util/LinearMap.scala", // ???
"src/dotty/tools/dotc/semanticdb/Tools.scala", // ???

// extension (c: Circle)(using Context)
"/tools/dotc/core/Symbols.scala",
"compiler/src/dotty/tools/dotc/typer/ConstFold.scala",
// wrong alignment, PR for dotty issued.
"tools/dotc/quoted/PickledQuotes.scala",
// ident.match { ... }
Expand All @@ -188,28 +185,19 @@ class CommunityDottySuite extends FunSuite {
"tools/dotc/semanticdb/Language.scala",
"tools/dotc/ast/tpd.scala", // comment after extension before def
"tools/dotc/typer/ProtoTypes.scala", // comment after colonEOL
"tools/dotc/ast/Desugar.scala", // if () indented block, missing then!

// @unchecked
"tools/dotc/transform/ContextFunctionResults.scala",
"tools/dotc/transform/Erasure.scala",
// for (a, b) <- lst yield ...
"tools/dotc/transform/BetaReduce.scala",
"compiler/src/dotty/tools/dotc/typer/RefChecks.scala",
"tools/dotc/typer/Checking.scala",
"tools/dotc/typer/QuotesAndSplices.scala",
"tools/dotc/typer/ErrorReporting.scala", // for if then yield
"tools/dotc/ast/Desugar.scala", // for if yield
"tools/dotc/transform/localopt/StringContextChecker.scala", // for flag <- flags ^;^ if flag._1 == '#' do
"compiler/src/dotty/tools/dotc/core/Symbols.scala", // for (tparam ^,^ bound) <- tparams.lazyZip(bounds)
"/tools/dotc/typer/Typer.scala", // case ref @ OrNull(tpnn) ^:^ TermRef
"compiler/src/dotty/tools/dotc/transform/Splicer.scala",
// if () block
"compiler/src/dotty/tools/dotc/typer/Implicits.scala",
// PR waits to be merged
"compiler/src/dotty/tools/dotc/typer/Applications.scala",
// match <indent> case => match <indent> case => (match in match indented)
"tools/dotc/semanticdb/ExtractSemanticDB.scala",
// test reproduces: using-lambda-method-parameter
"test/dotty/tools/dottydoc/GenDocs.scala" // ???
"doc-tool/test/dotty/tools/dottydoc/GenDocs.scala" // +: ^"^-project" +: "Dotty"
)

final def munitExclusionList = List(
Expand Down
Expand Up @@ -1792,7 +1792,12 @@ class ScalametaParser(input: Input, dialect: Dialect) { parser =>

def typedOpt(): Option[Type] =
if (token.is[Colon]) {
next(); Some(typ())
next()
if (token.is[At] && ahead(token.is[Ident])) {
Some(outPattern.annotTypeRest(autoPos(Type.AnonymousName())))
} else {
Some(typ())
}
} else None

def typeOrInfixType(location: Location): Type =
Expand Down Expand Up @@ -1952,12 +1957,12 @@ class ScalametaParser(input: Input, dialect: Dialect) { parser =>
case KwFor() =>
next()
val enums: List[Enumerator] =
if (token.is[LeftBrace]) inBracesOrNil(enumerators())
else if (token.is[LeftParen]) inParensOrNil(enumerators())
if (token.is[LeftBrace]) inBraces(enumerators())
else if (token.is[LeftParen]) inParens(enumerators())
else if (token.is[Indentation.Indent]) {
indented(enumerators())
} else {
enumerator(isFirst = true)
enumerators()
}

newLinesOpt()
Expand Down Expand Up @@ -2639,7 +2644,7 @@ class ScalametaParser(input: Input, dialect: Dialect) { parser =>
def enumerators(): List[Enumerator] = {
val enums = new ListBuffer[Enumerator]
enums ++= enumerator(isFirst = true)
while (token.is[StatSep] && !ahead(token.is[Indentation.Outdent])) {
while (token.is[StatSep] && !ahead(token.is[Indentation.Outdent] || token.is[KwDo])) {
next()
enums ++= enumerator(isFirst = false)
}
Expand Down Expand Up @@ -3622,6 +3627,22 @@ class ScalametaParser(input: Input, dialect: Dialect) { parser =>
param(ownerIsCase = false, ownerIsType = false, isImplicit = false, isUsing = false)
)

var uparams = ListBuffer[List[Term.Param]]()
while (token.is[LeftParen]) {
uparams += inParens {
if (!isSoftKw(token, SkUsing)) syntaxError("expected 'using' keyword", token)
next()
commaSeparated(
param(
ownerIsCase = false,
ownerIsType = true,
isImplicit = false,
isUsing = true
)
)
}
}

val methodsAll: List[Stat] = if (token.is[ColonEol]) {
accept[ColonEol]
indented(templateStats())
Expand All @@ -3632,7 +3653,7 @@ class ScalametaParser(input: Input, dialect: Dialect) { parser =>
List(nonLocalDefOrDcl())
}
val body: Stat = if (methodsAll.size == 1) methodsAll.head else Term.Block(methodsAll)
ExtensionGroup(eparam, tparams, body)
ExtensionGroup(eparam, tparams, uparams.toList, body)
}

def funDefRest(mods: List[Mod]): Stat = atPos(mods, auto) {
Expand Down
2 changes: 2 additions & 0 deletions scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
Expand Up @@ -150,6 +150,7 @@ object Term {
object Type {
@branch trait Ref extends Type with scala.meta.Ref
@ast class Name(value: String @nonEmpty) extends scala.meta.Name with Type.Ref
@ast class AnonymousName() extends Type
@ast class Select(qual: Term.Ref, name: Type.Name) extends Type.Ref {
checkFields(qual.isPath || qual.is[Term.Super] || qual.is[Term.Ref.Quasi])
}
Expand Down Expand Up @@ -331,6 +332,7 @@ object Defn {
@ast class ExtensionGroup(
eparam: Term.Param,
tparams: List[scala.meta.Type.Param],
uparams: List[List[Term.Param]],
body: Stat
) extends Defn
@ast class Def(
Expand Down
Expand Up @@ -577,6 +577,7 @@ object TreeSyntax {
s(w(mods, " "), nameType, t.default.map(s(" ", kw("="), " ", _)).getOrElse(s()))

// Type
case t: Type.AnonymousName => m(Path, s(""))
case t: Type.Name => m(Path, if (guessIsBackquoted(t)) s("`", t.value, "`") else s(t.value))
case t: Type.Select => m(SimpleTyp, s(t.qual, kw("."), t.name))
case t: Type.Project => m(SimpleTyp, s(p(SimpleTyp, t.qual), kw("#"), t.name))
Expand Down Expand Up @@ -893,9 +894,9 @@ object TreeSyntax {
"(",
t.eparam,
")",
t.uparams,
m
)

case t: Defn.Object => s(w(t.mods, " "), kw("object"), " ", t.name, templ(t.templ))
case t: Defn.Def =>
s(w(t.mods, " "), kw("def"), " ", t.name, t.tparams, t.paramss, t.decltpe, " = ", t.body)
Expand Down
Expand Up @@ -364,6 +364,7 @@ class SurfaceSuite extends FunSuite {
|scala.meta.Type.Select
|scala.meta.Type.Singleton
|scala.meta.Type.Tuple
|scala.meta.Type.Unchecked
|scala.meta.Type.Var
|scala.meta.Type.With
""".trim.stripMargin.split('\n').mkString(EOL)
Expand Down
Expand Up @@ -21,7 +21,7 @@ class ReflectionSuite extends FunSuite {
test("root") {
assert(symbolOf[scala.meta.Tree].isRoot)
assertEquals(symbolOf[scala.meta.Tree].asRoot.allBranches.length, 19)
assertEquals(symbolOf[scala.meta.Tree].asRoot.allLeafs.length, 315)
assertEquals(symbolOf[scala.meta.Tree].asRoot.allLeafs.length, 317)
}

test("If") {
Expand Down
Expand Up @@ -567,8 +567,7 @@ class ControlSyntaxSuite extends BaseDottySuite {

test("old-for-yield-single1") {
val code = "for (i <- 1 to 3) yield i"
val output = "for (i <- 1 to 3) yield i"
runTestAssert[Stat](code, assertLayout = Some(output))(
runTestAssert[Stat](code)(
Term.ForYield(
List(
Enumerator.Generator(
Expand Down Expand Up @@ -868,6 +867,27 @@ class ControlSyntaxSuite extends BaseDottySuite {
)
}

test("for-new") {
val code = """|for i <- gen
| x = 3
| if (cnd1) && cnd2
|yield work
|""".stripMargin
val output = "for (i <- gen; x = 3; if cnd1 && cnd2) yield work"
runTestAssert[Stat](code, assertLayout = Some(output))(
Term.ForYield(
List(
Enumerator.Generator(Pat.Var(Term.Name("i")), Term.Name("gen")),
Enumerator.Val(Pat.Var(Term.Name("x")), Lit.Int(3)),
Enumerator.Guard(
Term.ApplyInfix(Term.Name("cnd1"), Term.Name("&&"), Nil, List(Term.Name("cnd2")))
)
),
Term.Name("work")
)
)
}

// --------------------------
// WHILE
// --------------------------
Expand Down
Expand Up @@ -20,6 +20,7 @@ class ExtensionMethodsSuite extends BaseDottySuite {
Defn.ExtensionGroup(
cparam,
Nil,
Nil,
Defn.Def(Nil, tname("crc"), Nil, Nil, Some(pname("Int")), int(2))
)
)
Expand All @@ -30,6 +31,7 @@ class ExtensionMethodsSuite extends BaseDottySuite {
Defn.ExtensionGroup(
cparam,
Nil,
Nil,
Defn.Def(
List(Mod.Private(Name.Anonymous())),
tname("crc"),
Expand All @@ -50,6 +52,7 @@ class ExtensionMethodsSuite extends BaseDottySuite {
Defn.ExtensionGroup(
cparam,
Nil,
Nil,
Defn.Def(Nil, tname("crc"), Nil, Nil, Some(pname("Int")), int(2))
)
)
Expand All @@ -66,6 +69,7 @@ class ExtensionMethodsSuite extends BaseDottySuite {
Defn.ExtensionGroup(
Term.Param(Nil, Term.Name("c"), Some(Type.Name("Circle")), None),
Nil,
Nil,
Defn.Def(
List(Mod.Private(Name(""))),
Term.Name("crc"),
Expand All @@ -88,6 +92,7 @@ class ExtensionMethodsSuite extends BaseDottySuite {
Defn.ExtensionGroup(
Term.Param(Nil, Term.Name("c"), Some(Type.Name("Circle")), None),
Nil,
Nil,
Term.Block(
List(
Defn.Def(Nil, Term.Name("cra"), Nil, Nil, Some(Type.Name("Int")), Lit.Int(2)),
Expand All @@ -108,25 +113,59 @@ class ExtensionMethodsSuite extends BaseDottySuite {
Defn.ExtensionGroup(
cparam,
Nil,
Nil,
Defn.Def(Nil, tname("crc"), Nil, Nil, Some(pname("Int")), int(2))
)
)
}

test("extension-using-combo".ignore) {
val code = """|extension (c: Circle)(using Context) {
test("extension-using-single") {
val code = """|extension (c: Circle)(using Context, x: Int) {
| def crc: Int = 2
|}
|""".stripMargin
runTestAssert[Stat](code, assertLayout = Some("extension (c: Circle) def crc: Int = 2"))(
val output = "extension (c: Circle)(using Context, x: Int) def crc: Int = 2"
runTestAssert[Stat](code, assertLayout = Some(output))(
Defn.ExtensionGroup(
Term.Param(Nil, Term.Name("c"), Some(pname("Circle")), None),
Nil,
List(
List(
Term.Param(List(Mod.Using()), Name.Anonymous(), Some(pname("Context")), None),
Term.Param(List(Mod.Using()), Term.Name("x"), Some(pname("Int")), None)
)
),
Defn.Def(Nil, tname("crc"), Nil, Nil, Some(pname("Int")), int(2))
)
)
}

test("extension-using-multi") {
val code = """|extension (c: Circle)(using Context, x: Int)(using y: String, File) {
| def crc: Int = 2
|}
|""".stripMargin
val output =
"extension (c: Circle)(using Context, x: Int)(using y: String, File) def crc: Int = 2"
runTestAssert[Stat](code, assertLayout = Some(output))(
Defn.ExtensionGroup(
Term.Param(Nil, Term.Name("c"), Some(Type.Name("Circle")), None),
Nil,
List(
List(
Term.Param(List(Mod.Using()), Name(""), Some(Type.Name("Context")), None),
Term.Param(List(Mod.Using()), Term.Name("x"), Some(Type.Name("Int")), None)
),
List(
Term.Param(List(Mod.Using()), Term.Name("y"), Some(Type.Name("String")), None),
Term.Param(List(Mod.Using()), Name(""), Some(Type.Name("File")), None)
)
),
Defn.Def(Nil, Term.Name("crc"), Nil, Nil, Some(Type.Name("Int")), Lit.Int(2))
)
)
}

test("extension-soft-keyword") {
runTestAssert[Stat]("val c = f(a + extension)")(
Defn.Val(
Expand Down
Expand Up @@ -486,15 +486,25 @@ class GivenUsingSuite extends BaseDottySuite {
)
}

test("using-lambda-method-parameter".ignore) {
runTestAssert[Stat]("LazyBody { (using ctx: Context) => 3 }")(
Defn.Val(
Nil,
List(Pat.Var(tname("a"))),
None,
Term.ApplyUsing(
Term.ApplyUsing(Term.Apply(tname("f"), Nil), List(tname("a"))),
List(int(3), Lit.Boolean(true))
test("using-lambda-method-parameter") {
val output = """|LazyBody {
| (using ctx: Context) => 3
|}
|""".stripMargin
runTestAssert[Stat]("LazyBody { (using ctx: Context) => 3 }", assertLayout = Some(output))(
Term.Apply(
Term.Name("LazyBody"),
List(
Term.Block(
List(
Term.Function(
List(
Term.Param(List(Mod.Using()), Term.Name("ctx"), Some(Type.Name("Context")), None)
),
Lit.Int(3)
)
)
)
)
)
)
Expand Down
Expand Up @@ -470,7 +470,21 @@ class MinorDottySuite extends BaseDottySuite {
Lit.Unit()
)
)

}

test("unchecked-annotation") {
runTestAssert[Stat]("val a :: Nil: @unchecked = args")(
Defn.Val(
Nil,
List(Pat.ExtractInfix(Pat.Var(Term.Name("a")), Term.Name("::"), List(Term.Name("Nil")))),
Some(
Type.Annotate(
Type.AnonymousName(),
List(Mod.Annot(Init(Type.Name("unchecked"), Name(""), Nil)))
)
),
Term.Name("args")
)
)
}
}

0 comments on commit f590d53

Please sign in to comment.