diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala index 060e01bf62..6030bbbc73 100644 --- a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala +++ b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala @@ -158,7 +158,12 @@ final class Dialect private ( // Scala 3 no longer allows def hello(){} - `=` is always needed val allowProcedureSyntax: Boolean, // Scala 3 no longer allows `do {...} while(...)` - val allowDoWhile: Boolean + val allowDoWhile: Boolean, + /* Kind-project support + * works under -Xsource3 flag + * https://github.com/scala/scala/pull/9605 + */ + val allowPlusMinusUnderscoreIdent: Boolean ) extends Product with Serializable { // NOTE(olafur) checklist for adding a new dialect field in a binary compatible way: @@ -253,7 +258,8 @@ final class Dialect private ( allowAsForImportRename = false, allowStarWildcardImport = false, allowProcedureSyntax = true, - allowDoWhile = true + allowDoWhile = true, + allowPlusMinusUnderscoreIdent = false // NOTE(olafur): declare the default value for new fields above this comment. ) } @@ -455,6 +461,10 @@ final class Dialect private ( def withAllowDoWhile(newValue: Boolean): Dialect = { privateCopy(allowDoWhile = newValue) } + + def withAllowPlusMinusUnderscoreIdent(newValue: Boolean): Dialect = { + privateCopy(allowPlusMinusUnderscoreIdent = newValue) + } // NOTE(olafur): add the next `withX()` method above this comment. Please try // to use consistent formatting, use `newValue` as the parameter name and wrap // the body inside curly braces. @@ -520,7 +530,8 @@ final class Dialect private ( allowAsRenames: Boolean = this.allowAsForImportRename, allowStarWildcardImport: Boolean = this.allowStarWildcardImport, allowProcedureSyntax: Boolean = this.allowProcedureSyntax, - allowDoWhile: Boolean = this.allowDoWhile + allowDoWhile: Boolean = this.allowDoWhile, + allowPlusMinusUnderscoreIdent: Boolean = this.allowPlusMinusUnderscoreIdent // NOTE(olafur): add the next parameter above this comment. ): Dialect = { new Dialect( @@ -583,7 +594,8 @@ final class Dialect private ( allowAsRenames, allowStarWildcardImport, allowProcedureSyntax, - allowDoWhile + allowDoWhile, + allowPlusMinusUnderscoreIdent // NOTE(olafur): add the next argument above this comment. ) } diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala index 15cb065740..01eb884794 100644 --- a/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala +++ b/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala @@ -69,6 +69,7 @@ package object dialects { .withAllowInfixMods(true) .withAllowPostfixStarVarargSplices(true) .withAllowAndTypes(true) + .withAllowPlusMinusUnderscoreIdent(true) /** * Dialect starting with Scala 2.12.14 for `-Xsource:3` option @@ -81,6 +82,7 @@ package object dialects { .withAllowInfixMods(true) .withAllowPostfixStarVarargSplices(true) .withAllowAndTypes(true) + .withAllowPlusMinusUnderscoreIdent(true) implicit val Scala = Scala213 // alias for latest Scala dialect. @@ -152,6 +154,7 @@ package object dialects { .withAllowStarWildcardImport(true) .withAllowProcedureSyntax(false) .withAllowDoWhile(false) + .withAllowPlusMinusUnderscoreIdent(true) @deprecated("Use Scala3 instead", "4.4.2") implicit val Dotty = Scala3 diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala index f9294220be..c93ccc44e4 100644 --- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala +++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala @@ -1729,12 +1729,21 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => Type.Macro(macroSplice()) case Ident("?") if dialect.allowQuestionMarkPlaceholder => next(); atPos(in.prevTokenPos, auto)(Type.Placeholder(typeBounds())) - case Literal() => - if (dialect.allowLiteralTypes) literal() - else syntaxError(s"$dialect doesn't support literal types", at = path()) + case ident: Ident + if (ident.value == "+" || ident.value == "-") && + ahead(token.is[Underscore]) && + dialect.allowPlusMinusUnderscoreIdent => + autoPos { + accept[Ident] + accept[Underscore] + Type.Name(s"${ident.value}_") + } case Ident("-") if ahead { token.is[NumericLiteral] } && dialect.allowLiteralTypes => val term = termName() atPos(term, auto)(literal(isNegated = true)) + case Literal() => + if (dialect.allowLiteralTypes) literal() + else syntaxError(s"$dialect doesn't support literal types", at = path()) case _ => val ref = path() match { case q: Quasi => q.become[Term.Ref.Quasi] diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/TypeSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/TypeSuite.scala index 54c13c30db..5b698b5f66 100644 --- a/tests/shared/src/test/scala/scala/meta/tests/parsers/TypeSuite.scala +++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/TypeSuite.scala @@ -159,4 +159,11 @@ class TypeSuite extends ParseSuite { assertNoDiff(exceptionScala2.shortMessage, "illegal literal type (), use Unit instead") } + + test("plus-minus-then-undescore-source3") { + val Type.Function(List(Type.Name("+_")), Type.Name("Int")) = + tpe("+_ => Int")(dialects.Scala213Source3) + val Type.Apply(Type.Name("Option"), List(Type.Name("-_"))) = + tpe("Option[- _]")(dialects.Scala213Source3) + } }