Skip to content
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

Route a method whose name is a reserved word. #11890

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,11 @@ private[routes] class RoutesFileParser extends JavaTokenParsers {
// Since the Scala parser is greedy, we can't easily extract this out, so just parse at least 2
def absoluteMethod: Parser[List[String]] =
namedError(
ident ~ "." ~ rep1sep(ident, ".") ^^ {
case first ~ _ ~ rest => first :: rest
ident ~ "." ~ rep1sep(ident, ".") ~ opt(".`" ~> ident <~ "`") ^^ {
case first ~ _ ~ rest ~ None => first :: rest
case first ~ _ ~ rest ~ Some(tickedMethod) =>
val packageAndClass = first :: rest
packageAndClass :+ tickedMethod
},
"Controller method call expected"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ package object templates {
* Generate a controller method call for the given injected route
*/
def injectedControllerMethodCall(r: Route, ident: String, paramFormat: Parameter => String): String = {
val method = safeMethod(r.call.method)
val methodPart = if (r.call.instantiate) {
s"$ident.get.${r.call.method}"
s"$ident.get.${method}"
} else {
s"$ident.${r.call.method}"
s"$ident.${method}"
}
val paramPart = r.call.parameters
.map { params => params.map(paramFormat).mkString(", ") }
Expand Down Expand Up @@ -207,6 +208,16 @@ package object templates {
}
.getOrElse(keyword)

/**
* Ensure that the given method name doesn't clash with any of the keywords that Play is using, including Scala keywords.
*/
def safeMethod(method: String): String =
scalaReservedWords
.collectFirst {
case reserved if reserved == method => s"`$reserved`"
}
.getOrElse(method)

/**
* Calculate the parameters for the reverse route call for the given routes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ package @{packageName.map(_ + ".").getOrElse("")}javascript @ob
@for(((method, _), routes) <- groupRoutesByMethod(routes)) {@routes match {
case Seq(route: Route) => {
@markLines(route)
def @method: JavaScriptReverseRoute = JavaScriptReverseRoute(
def @{safeMethod(method)}: JavaScriptReverseRoute = JavaScriptReverseRoute(
"@{packageName.map(_ + ".").getOrElse("")}@(controller).@(method)",
@tq
function(@reverseParametersJavascript(routes).map(_._1.name).mkString(",")) @ob
Expand All @@ -33,7 +33,7 @@ package @{packageName.map(_ + ".").getOrElse("")}javascript @ob
}
case _ => {
@markLines(routes: _*)
def @method: JavaScriptReverseRoute = JavaScriptReverseRoute(
def @{safeMethod(method)}: JavaScriptReverseRoute = JavaScriptReverseRoute(
"@{packageName.map(_ + ".").getOrElse("")}@(controller).@(method)",
@tq
function(@reverseParametersJavascript(routes).map(_._1.name).mkString(",")) @ob
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import @if(!i.startsWith("_root_.")){_root_.}@i}
@for(((method, _), routes) <- groupRoutesByMethod(routes)) {@routes match {
case Seq(route: Route) => {
@markLines(route)
def @(method)@(reverseSignature(routes)): Call = @ob
def @{safeMethod(method)}@(reverseSignature(routes)): Call = @ob
@reverseRouteContext(route)
@reverseCall(route)
@cb
}
case _ => {
@markLines(routes: _*)
def @(method)@(reverseSignature(routes)): Call = @ob
def @{safeMethod(method)}@(reverseSignature(routes)): Call = @ob
@defining(reverseParameters(routes)) { params =>
(@reverseMatchParameters(params, true)) match @ob
@reverseUniqueConstraints(routes, params) { (route, parameters, parameterConstraints, localNames) =>
Expand Down
116 changes: 116 additions & 0 deletions dev-mode/play-routes-compiler/src/test/resources/reservedWords.routes
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

GET /as controllers.FooController.as()
GET /abstract controllers.FooController.abstract()
GET /case controllers.FooController.case()
GET /catch controllers.FooController.catch()
GET /class controllers.FooController.class()
GET /def controllers.FooController.def()
GET /derives controllers.FooController.derives()
GET /do controllers.FooController.do()
GET /else controllers.FooController.else()
GET /end controllers.FooController.end()
GET /enum controllers.FooController.enum()
GET /erased controllers.FooController.erased()
GET /extends controllers.FooController.extends()
GET /extension controllers.FooController.extension()
GET /export controllers.FooController.export()
GET /false controllers.FooController.false()
GET /final controllers.FooController.final()
GET /finally controllers.FooController.finally()
GET /for controllers.FooController.for()
GET /forSome controllers.FooController.forSome()
GET /given controllers.FooController.given()
GET /if controllers.FooController.if()
GET /implicit controllers.FooController.implicit()
GET /import controllers.FooController.import()
GET /infix controllers.FooController.infix()
GET /inline controllers.FooController.inline()
GET /lazy controllers.FooController.lazy()
GET /macro controllers.FooController.macro()
GET /match controllers.FooController.match()
GET /new controllers.FooController.new()
GET /null controllers.FooController.null()
GET /object controllers.FooController.object()
GET /opaque controllers.FooController.opaque()
GET /open controllers.FooController.open()
GET /override controllers.FooController.override()
GET /package controllers.FooController.package()
GET /private controllers.FooController.private()
GET /protected controllers.FooController.protected()
GET /return controllers.FooController.return()
GET /sealed controllers.FooController.sealed()
GET /super controllers.FooController.super()
GET /then controllers.FooController.then()
GET /this controllers.FooController.this()
GET /throw controllers.FooController.throw()
GET /throws controllers.FooController.throws()
GET /trait controllers.FooController.trait()
GET /transparent controllers.FooController.transparent()
GET /try controllers.FooController.try()
GET /true controllers.FooController.true()
GET /type controllers.FooController.type()
GET /using controllers.FooController.using()
GET /val controllers.FooController.val()
GET /var controllers.FooController.var()
GET /while controllers.FooController.while()
GET /with controllers.FooController.with()
GET /yield controllers.FooController.yield()
GET /queryString controllers.FooController.queryString()
GET /backticks/as controllers.FooController.`as`()
GET /backticks/abstract controllers.FooController.`abstract`()
GET /backticks/case controllers.FooController.`case`()
GET /backticks/catch controllers.FooController.`catch`()
GET /backticks/class controllers.FooController.`class`()
GET /backticks/def controllers.FooController.`def`()
GET /backticks/derives controllers.FooController.`derives`()
GET /backticks/do controllers.FooController.`do`()
GET /backticks/else controllers.FooController.`else`()
GET /backticks/end controllers.FooController.`end`()
GET /backticks/enum controllers.FooController.`enum`()
GET /backticks/erased controllers.FooController.`erased`()
GET /backticks/extends controllers.FooController.`extends`()
GET /backticks/extension controllers.FooController.`extension`()
GET /backticks/export controllers.FooController.`export`()
GET /backticks/false controllers.FooController.`false`()
GET /backticks/final controllers.FooController.`final`()
GET /backticks/finally controllers.FooController.`finally`()
GET /backticks/for controllers.FooController.`for`()
GET /backticks/forSome controllers.FooController.`forSome`()
GET /backticks/given controllers.FooController.`given`()
GET /backticks/if controllers.FooController.`if`()
GET /backticks/implicit controllers.FooController.`implicit`()
GET /backticks/import controllers.FooController.`import`()
GET /backticks/infix controllers.FooController.`infix`()
GET /backticks/inline controllers.FooController.`inline`()
GET /backticks/lazy controllers.FooController.`lazy`()
GET /backticks/macro controllers.FooController.`macro`()
GET /backticks/match controllers.FooController.`match`()
GET /backticks/new controllers.FooController.`new`()
GET /backticks/null controllers.FooController.`null`()
GET /backticks/object controllers.FooController.`object`()
GET /backticks/opaque controllers.FooController.`opaque`()
GET /backticks/open controllers.FooController.`open`()
GET /backticks/override controllers.FooController.`override`()
GET /backticks/package controllers.FooController.`package`()
GET /backticks/private controllers.FooController.`private`()
GET /backticks/protected controllers.FooController.`protected`()
GET /backticks/return controllers.FooController.`return`()
GET /backticks/sealed controllers.FooController.`sealed`()
GET /backticks/super controllers.FooController.`super`()
GET /backticks/then controllers.FooController.`then`()
GET /backticks/this controllers.FooController.`this`()
GET /backticks/throw controllers.FooController.`throw`()
GET /backticks/throws controllers.FooController.`throws`()
GET /backticks/trait controllers.FooController.`trait`()
GET /backticks/transparent controllers.FooController.`transparent`()
GET /backticks/try controllers.FooController.`try`()
GET /backticks/true controllers.FooController.`true`()
GET /backticks/type controllers.FooController.`type`()
GET /backticks/using controllers.FooController.`using`()
GET /backticks/val controllers.FooController.`val`()
GET /backticks/var controllers.FooController.`var`()
GET /backticks/while controllers.FooController.`while`()
GET /backticks/with controllers.FooController.`with`()
GET /backticks/yield controllers.FooController.`yield`()
GET /backticks/queryString controllers.FooController.`queryString`()
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,14 @@ class RoutesCompilerSpec extends Specification with FileMatchers {
tmp
) must beRight
}

"check if routes with reserved words as method name are compiled" in withTempDir { tmp =>
val file = new File(this.getClass.getClassLoader.getResource("reservedWords.routes").toURI)
RoutesCompiler.compile(
RoutesCompilerTask(file, Seq.empty, true, true, false),
InjectedRoutesGenerator,
tmp
) must beRight
}
}
}