diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 74a9454a80b5..f2f10792e7d6 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1142,6 +1142,12 @@ class Global(var currentSettings: Settings, reporter0: Reporter) val profiler: Profiler = Profiler(settings) keepPhaseStack = settings.log.isSetByUser + // We hit these checks regularly. They shouldn't change inside the same run, so cache the comparisons here. + val isScala211: Boolean = settings.isScala211 + val isScala212: Boolean = settings.isScala212 + val isScala213: Boolean = settings.isScala213 + val isScala3: Boolean = settings.isScala3 + // used in sbt def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings // used in sbt diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 650dc1722ba6..479fad69a2aa 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -673,6 +673,24 @@ self => case _ => false } + def isSoftModifier: Boolean = + currentRun.isScala3 && in.token == IDENTIFIER && softModifierNames.contains(in.name) + + /** Is the current token a soft modifier in a position where such a modifier is allowed? */ + def isValidSoftModifier: Boolean = + isSoftModifier && { + val mod = in.name + lookingAhead { + while (in.token == NEWLINE || isModifier || isSoftModifier) in.nextToken() + + in.token match { + case CLASS | CASECLASS => true + case DEF | TRAIT | TYPE => mod == nme.infix + case _ => false + } + } + } + def isAnnotation: Boolean = in.token == AT def isLocalModifier: Boolean = in.token match { @@ -704,6 +722,10 @@ self => def isRawBar = isRawIdent && in.name == raw.BAR def isRawIdent = in.token == IDENTIFIER + def isWildcardType = + in.token == USCORE || + currentRun.isScala3 && isRawIdent && in.name == raw.QMARK + def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT def isMacro = in.token == IDENTIFIER && in.name == nme.MACROkw @@ -723,12 +745,13 @@ self => def isSimpleExprIntro: Boolean = isExprIntroToken(in.token) - def isExprIntroToken(token: Token): Boolean = isLiteralToken(token) || (token match { + def isExprIntroToken(token: Token): Boolean = + !isValidSoftModifier && (isLiteralToken(token) || (token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true case _ => false - }) + })) def isExprIntro: Boolean = isExprIntroToken(in.token) @@ -870,6 +893,16 @@ self => } } + /** Is current ident a `*`, and is it followed by a `)` or `, )`? */ + def followingIsScala3Vararg(): Boolean = + currentRun.isScala3 && isRawStar && lookingAhead { + in.token == RPAREN || + in.token == COMMA && { + in.nextToken() + in.token == RPAREN + } + } + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ /** Modes for infix types. */ @@ -1042,12 +1075,14 @@ self => val start = in.offset simpleTypeRest(in.token match { case LPAREN => atPos(start)(makeSafeTupleType(inParens(types()), start)) - case USCORE => wildcardType(in.skipToken()) case _ => - path(thisOK = false, typeOK = true) match { - case r @ SingletonTypeTree(_) => r - case r => convertToTypeId(r) - } + if (isWildcardType) + wildcardType(in.skipToken()) + else + path(thisOK = false, typeOK = true) match { + case r @ SingletonTypeTree(_) => r + case r => convertToTypeId(r) + } }) } @@ -1648,7 +1683,7 @@ self => val start = in.offset val base = opstack - def loop(top: Tree): Tree = if (!isIdent) top else { + def loop(top: Tree): Tree = if (!isIdent || followingIsScala3Vararg()) top else { pushOpInfo(reduceExprStack(base, top)) newLineOptWhenFollowing(isExprIntroToken) if (isExprIntro) @@ -1659,7 +1694,12 @@ self => else finishPostfixOp(start, base, popOpInfo()) } - reduceExprStack(base, loop(prefixExpr())) + val expr = reduceExprStack(base, loop(prefixExpr())) + if (followingIsScala3Vararg()) + atPos(expr.pos.start) { + Typed(expr, atPos(in.skipToken()) { Ident(tpnme.WILDCARD_STAR) }) + } + else expr } /** {{{ @@ -1860,6 +1900,12 @@ self => */ def generator(eqOK: Boolean, allowNestedIf: Boolean = true): List[Tree] = { val start = in.offset + val hasCase = in.token == CASE + if (hasCase) { + if (!currentRun.isScala3) syntaxError(in.offset, s"`case` keyword in for comprehension requires the -Xsource:3 flag.") + in.skipCASE() + } + val hasVal = in.token == VAL if (hasVal) in.nextToken() @@ -1873,7 +1919,7 @@ self => else syntaxError(in.offset, "val in for comprehension must be followed by assignment") } - if (hasEq && eqOK) in.nextToken() + if (hasEq && eqOK && !hasCase) in.nextToken() else accept(LARROW) val rhs = expr() @@ -1909,18 +1955,16 @@ self => def functionArgType(): Tree = argType() def argType(): Tree = { val start = in.offset - in.token match { - case USCORE => + if (isWildcardType) { in.nextToken() if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start) else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) } - case _ => - typ() match { - case Ident(name: TypeName) if nme.isVariableName(name) => - atPos(start) { Bind(name, EmptyTree) } - case t => t - } - } + } else + typ() match { + case Ident(name: TypeName) if nme.isVariableName(name) => + atPos(start) { Bind(name, EmptyTree) } + case t => t + } } /** {{{ @@ -2010,7 +2054,12 @@ self => if (isCloseDelim) atPos(top.pos.start, in.prev.offset)(Star(stripParens(top))) else EmptyTree ) - case _ => EmptyTree + case Ident(name) if isSequenceOK && followingIsScala3Vararg() => + atPos(top.pos.start) { + Bind(name, atPos(in.skipToken()) { Star(Ident(nme.WILDCARD)) }) + } + case _ => + EmptyTree } def loop(top: Tree): Tree = reducePatternStack(base, top) match { case next if isIdent && !isRawBar => pushOpInfo(next) ; loop(simplePattern(badPattern3)) @@ -2213,7 +2262,10 @@ self => in.nextToken() loop(mods) case _ => - mods + if (isValidSoftModifier) { + in.nextToken() + loop(mods) + } else mods } loop(NoMods) } @@ -2298,7 +2350,7 @@ self => if (vds.isEmpty) syntaxError(start, s"case classes must have a parameter list; try 'case class $name()' or 'case object $name'") else if (vds.head.nonEmpty && vds.head.head.mods.isImplicit) { - if (settings.isScala213) + if (currentRun.isScala213) syntaxError(start, s"case classes must have a non-implicit parameter list; try 'case class $name()$elliptical'") else { deprecationWarning(start, s"case classes should have a non-implicit parameter list; adapting to 'case class $name()$elliptical'", "2.12.2") @@ -2503,19 +2555,27 @@ self => def loop(expr: Tree): Tree = { expr setPos expr.pos.makeTransparent val selectors: List[ImportSelector] = in.token match { - case USCORE => List(importSelector()) // import foo.bar._; - case LBRACE => importSelectors() // import foo.bar.{ x, y, z } - case _ => - val nameOffset = in.offset - val name = ident() - if (in.token == DOT) { - // import foo.bar.ident. and so create a select node and recurse. - val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name)) - in.nextToken() - return loop(t) + case USCORE => + List(importSelector()) // import foo.bar._ + case IDENTIFIER if currentRun.isScala3 && in.name == raw.STAR => + List(importSelector()) // import foo.bar.* + case LBRACE => + importSelectors() // import foo.bar.{ x, y, z } + case _ => + if (settings.isScala3 && lookingAhead { isRawIdent && in.name == nme.as }) + List(importSelector()) // import foo.bar as baz + else { + val nameOffset = in.offset + val name = ident() + if (in.token == DOT) { + // import foo.bar.ident. and so create a select node and recurse. + val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name)) + in.nextToken() + return loop(t) + } + // import foo.bar.Baz; + else List(makeImportSelector(name, nameOffset)) } - // import foo.bar.Baz; - else List(makeImportSelector(name, nameOffset)) } // reaching here means we're done walking. atPos(start)(Import(expr, selectors)) @@ -2558,18 +2618,25 @@ self => */ def importSelector(): ImportSelector = { val start = in.offset - val name = wildcardOrIdent() + val name = + if (currentRun.isScala3 && isRawIdent && in.name == raw.STAR) { + in.nextToken() + nme.WILDCARD + } + else wildcardOrIdent() var renameOffset = -1 - val rename = in.token match { - case ARROW => + val rename = + if (in.token == ARROW || (currentRun.isScala3 && isRawIdent && in.name == nme.as)) { in.nextToken() renameOffset = in.offset wildcardOrIdent() - case _ if name == nme.WILDCARD => null - case _ => + } + else if (name == nme.WILDCARD) null + else { renameOffset = start name - } + } + ImportSelector(name, start, rename, renameOffset) } @@ -3117,7 +3184,7 @@ self => case IMPORT => in.flushDoc importClause() - case _ if isAnnotation || isTemplateIntro || isModifier => + case _ if isAnnotation || isTemplateIntro || isModifier || isValidSoftModifier => joinComment(topLevelTmplDef :: Nil) } @@ -3167,7 +3234,7 @@ self => case IMPORT => in.flushDoc importClause() - case _ if isDefIntro || isModifier || isAnnotation => + case _ if isDefIntro || isModifier || isAnnotation || isValidSoftModifier => joinComment(nonLocalDefOrDcl) case _ if isExprIntro => in.flushDoc diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 26abb5b837f6..408f74be55f2 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -331,6 +331,16 @@ trait Scanners extends ScannersCommon { } } + /** Advance beyond a case token without marking the CASE in sepRegions. + * This method should be called to skip beyond CASE tokens that are + * not part of matches, i.e. no ARROW is expected after them. + */ + def skipCASE(): Unit = { + assert(token == CASE, s"Internal error: skipCASE() called on non-case token $token") + nextToken() + sepRegions = sepRegions.tail + } + /** Produce next token, filling TokenData fields of Scanner. */ def nextToken(): Unit = { @@ -610,7 +620,7 @@ trait Scanners extends ScannersCommon { val isEmptyCharLit = (ch == '\'') getLitChar() if (ch == '\'') { - if (isEmptyCharLit && settings.isScala213) + if (isEmptyCharLit && currentRun.isScala213) syntaxError("empty character literal (use '\\'' for single quote)") else { if (isEmptyCharLit) @@ -1282,6 +1292,8 @@ trait Scanners extends ScannersCommon { final val token2name = (allKeywords map (_.swap)).toMap + final val softModifierNames = Set(nme.open, nme.infix) + // Token representation ---------------------------------------------------- /** Returns the string representation of given token. */ diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index e2598d1c0b6c..46e9497cebc5 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -106,6 +106,8 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett def isScala212: Boolean = source.value >= version212 private[this] val version213 = ScalaVersion("2.13.0") def isScala213: Boolean = source.value >= version213 + private[this] val version3 = ScalaVersion("3.0.0") + def isScala3: Boolean = source.value >= version3 /** * -X "Advanced" settings diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 305b723752f5..a90d9aa701eb 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -435,7 +435,7 @@ abstract class UnCurry extends InfoTransform (sym ne null) && sym.elisionLevel.exists { level => if (sym.isMethod) level < settings.elidebelow.value else { - if (settings.isScala213) reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable!") + if (currentRun.isScala213) reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable!") false } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 0fea82c35a95..700f154a4bfd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -114,7 +114,7 @@ trait ContextErrors { def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } def typeErrorMsg(context: Context, found: Type, req: Type) = - if (context.openImplicits.nonEmpty && !settings.XlogImplicits.value && settings.isScala213) + if (context.openImplicits.nonEmpty && !settings.XlogImplicits.value && currentRun.isScala213) // OPT: avoid error string creation for errors that won't see the light of day, but predicate // this on -Xsource:2.13 for bug compatibility with https://github.com/scala/scala/pull/7147#issuecomment-418233611 "type mismatch" diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index bb6f19138449..e22983a712a7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -866,7 +866,7 @@ trait Contexts { self: Analyzer => isAccessible(sym, pre) && !(imported && { val e = scope.lookupEntry(name) - (e ne null) && (e.owner == scope) && (!settings.isScala212 || e.sym.exists) + (e ne null) && (e.owner == scope) && (!currentRun.isScala212 || e.sym.exists) }) /** Do something with the symbols with name `name` imported via the import in `imp`, diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index bfb3446874fc..7309cf5d9f3c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1113,7 +1113,7 @@ trait Implicits { if(isView || wildPtNotInstantiable || matchesPtInst(firstPending)) typedImplicit(firstPending, ptChecked = true, isLocalToCallsite) else SearchFailure - if (typedFirstPending.isFailure && settings.isScala213) + if (typedFirstPending.isFailure && currentRun.isScala213) undoLog.undoTo(mark) // Don't accumulate constraints from typechecking or type error message creation for failed candidates // Pass the errors to `DivergentImplicitRecovery` so that it can note @@ -1214,7 +1214,7 @@ trait Implicits { * bound, the implicits infos which are members of these companion objects. */ private def companionImplicitMap(tp: Type): InfoMap = { - val isScala213 = settings.isScala213 + val isScala213 = currentRun.isScala213 /* Populate implicit info map by traversing all parts of type `tp`. * Parameters as for `getParts`. @@ -1626,9 +1626,9 @@ trait Implicits { val outSym = out.typeSymbol val fail = - if (out.annotations.isEmpty && (outSym == ObjectClass || (settings.isScala211 && outSym == AnyValClass))) + if (out.annotations.isEmpty && (outSym == ObjectClass || (currentRun.isScala211 && outSym == AnyValClass))) maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than $out") - else if (settings.isScala211 && in.annotations.isEmpty && in.typeSymbol == NullClass) + else if (currentRun.isScala211 && in.annotations.isEmpty && in.typeSymbol == NullClass) maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion") else false diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 60de89831879..5cad833c0bcc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1707,7 +1707,7 @@ trait Namers extends MethodSynthesis { val valOwner = owner.owner // there's no overriding outside of classes, and we didn't use to do this in 2.11, so provide opt-out - if (!settings.isScala212 || !valOwner.isClass) WildcardType + if (!currentRun.isScala212 || !valOwner.isClass) WildcardType else { // normalize to getter so that we correctly consider a val overriding a def // (a val's name ends in a " ", so can't compare to def) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 37ccf6bd58e8..822583029945 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -609,7 +609,7 @@ trait NamesDefaults { self: Analyzer => case _ => false } params indexWhere (p => matchesName(p, name, argIndex)) match { - case -1 if positionalAllowed && !settings.isScala213 => + case -1 if positionalAllowed && !currentRun.isScala213 => if (isVariableInScope(context0, name)) { // only issue the deprecation warning if `name` is in scope, this avoids the warning when mis-spelling a parameter name. context0.deprecationWarning( @@ -629,7 +629,7 @@ trait NamesDefaults { self: Analyzer => case AssignOrNamedArg(Ident(oName), _) if oName != name => oName } DoubleParamNamesDefaultError(arg, name, existingArgIndex+1, otherName) - case paramPos if !settings.isScala213 && !invokesDefault && isAmbiguousAssignment(typer, params(paramPos), arg) => + case paramPos if !currentRun.isScala213 && !invokesDefault && isAmbiguousAssignment(typer, params(paramPos), arg) => AmbiguousReferenceInNamesDefaultError(arg, name) case paramPos if paramPos != argIndex => positionalAllowed = false // named arg is not in original parameter order: require names after this diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 0b02e96a58ae..122d85d7f2eb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -143,7 +143,7 @@ abstract class RefChecks extends Transform { case _ => false } val haveDefaults = methods filter ( - if (settings.isScala211) + if (currentRun.isScala211) (sym => mexists(sym.info.paramss)(_.hasDefault) && !nme.isProtectedAccessorName(sym.name)) else (sym => hasDefaultParam(sym.info) && !nme.isProtectedAccessorName(sym.name)) @@ -1486,7 +1486,7 @@ abstract class RefChecks extends Transform { if (!sym.isMethod || sym.isAccessor || sym.isLazy || sym.isDeferred) reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable.") } - if (settings.isScala213) checkIsElisible(tree.symbol) + if (currentRun.isScala213) checkIsElisible(tree.symbol) tree match { case m: MemberDef => diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0a88e8e1a56c..a0bc729890eb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1893,7 +1893,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val impl2 = finishMethodSynthesis(impl1, clazz, context) - if (settings.isScala211 && mdef.symbol == PredefModule) + if (currentRun.isScala211 && mdef.symbol == PredefModule) ensurePredefParentsAreInSameSourceFile(impl2) treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType @@ -3472,7 +3472,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // and lubbing the argument types (we treat SAM and FunctionN types equally, but non-function arguments // do not receive special treatment: they are typed under WildcardType.) val altArgPts = - if (settings.isScala212 && args.exists(treeInfo.isFunctionMissingParamType)) + if (currentRun.isScala212 && args.exists(treeInfo.isFunctionMissingParamType)) try alts.map(alt => formalTypes(alt.info.paramTypes, argslen).map(ft => (ft, alt))).transpose // do least amount of work up front catch { case _: IllegalArgumentException => args.map(_ => Nil) } // fail safe in case formalTypes fails to align to argslen else args.map(_ => Nil) // will type under argPt == WildcardType diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index e4862d6872f6..200a92bfdeaf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -159,7 +159,7 @@ trait Unapplies extends ast.TreeDSL { case _ => nme.unapply } val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), unapplyParamName, classType(cdef, tparams), EmptyTree)) - val resultType = if (!settings.isScala212) TypeTree() else { // fix for scala/bug#6541 under -Xsource:2.12 + val resultType = if (!currentRun.isScala212) TypeTree() else { // fix for scala/bug#6541 under -Xsource:2.12 def repeatedToSeq(tp: Tree) = tp match { case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS_NAME), tps) => AppliedTypeTree(gen.rootScalaDot(tpnme.Seq), tps) case _ => tp diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index ab988783bd4e..b7e4e901fbd5 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -627,6 +627,13 @@ trait StdNames { val long2Long: NameType = "long2Long" val boolean2Boolean: NameType = "boolean2Boolean" + // Scala 3 import syntax + val as: NameType = "as" + + // Scala 3 soft keywords + val infix: NameType = "infix" + val open: NameType = "open" + // Compiler utilized names val AnnotatedType: NameType = "AnnotatedType" @@ -924,6 +931,7 @@ trait StdNames { final val PLUS : NameType = "+" final val STAR : NameType = "*" final val TILDE: NameType = "~" + final val QMARK: NameType = "?" final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG) } diff --git a/test/files/neg/for-comprehension-case-future.check b/test/files/neg/for-comprehension-case-future.check new file mode 100644 index 000000000000..02dab922e0d4 --- /dev/null +++ b/test/files/neg/for-comprehension-case-future.check @@ -0,0 +1,7 @@ +for-comprehension-case-future.scala:22: error: '<-' expected but '=' found. + case y = x + 1 + ^ +for-comprehension-case-future.scala:23: error: illegal start of simple expression + } yield x + y + ^ +two errors found diff --git a/test/files/neg/for-comprehension-case-future.scala b/test/files/neg/for-comprehension-case-future.scala new file mode 100644 index 000000000000..05602e537759 --- /dev/null +++ b/test/files/neg/for-comprehension-case-future.scala @@ -0,0 +1,24 @@ +// scalac: -Xsource:3 +// +class A { + // ok + val a = + for { + case Some(x) <- List(Some(1), None) + y = x + 1 + } yield x + y + + // ok + val b = + for { + Some(x) <- List(Some(1), None) + Some(y) <- List(None, Some(2)) + } yield x+y + + // fail + val c = + for { + case Some(x) <- List(Some(1), None) + case y = x + 1 + } yield x + y +} diff --git a/test/files/neg/for-comprehension-case.check b/test/files/neg/for-comprehension-case.check new file mode 100644 index 000000000000..b1f2eb0849c5 --- /dev/null +++ b/test/files/neg/for-comprehension-case.check @@ -0,0 +1,13 @@ +for-comprehension-case.scala:5: error: `case` keyword in for comprehension requires the -Xsource:3 flag. + case Some(x) <- List(Some(1), None) + ^ +for-comprehension-case.scala:12: error: `case` keyword in for comprehension requires the -Xsource:3 flag. + case y = x + 1 + ^ +for-comprehension-case.scala:12: error: '<-' expected but '=' found. + case y = x + 1 + ^ +for-comprehension-case.scala:13: error: illegal start of simple expression + } yield x+y + ^ +four errors found diff --git a/test/files/neg/for-comprehension-case.scala b/test/files/neg/for-comprehension-case.scala new file mode 100644 index 000000000000..55e8d44a40e3 --- /dev/null +++ b/test/files/neg/for-comprehension-case.scala @@ -0,0 +1,14 @@ +class A { + // fail + val a = + for { + case Some(x) <- List(Some(1), None) + } yield x + + // fail + val b = + for { + Some(x) <- List(Some(1), None) + case y = x + 1 + } yield x+y +} diff --git a/test/files/neg/import-future.check b/test/files/neg/import-future.check new file mode 100644 index 000000000000..282b1ae95e4c --- /dev/null +++ b/test/files/neg/import-future.check @@ -0,0 +1,4 @@ +import-future.scala:15: error: not found: value unrelated + unrelated(1) // error + ^ +one error found diff --git a/test/files/neg/import-future.scala b/test/files/neg/import-future.scala new file mode 100644 index 000000000000..288fd3d0e240 --- /dev/null +++ b/test/files/neg/import-future.scala @@ -0,0 +1,27 @@ +// scalac: -Xsource:3 +// + +class D { + def *(y: Int): Int = y + def unrelated(y: Int): Int = y +} + +object Test { + val d = new D + + def one: Int = { + import d.`*` + + unrelated(1) // error + + *(1) + } + + def two: Int = { + import d.* + + unrelated(1) + + *(1) + } +} diff --git a/test/files/neg/open-infix-future.check b/test/files/neg/open-infix-future.check new file mode 100644 index 000000000000..b39489cabad0 --- /dev/null +++ b/test/files/neg/open-infix-future.check @@ -0,0 +1,22 @@ +open-infix-future.scala:4: error: expected class or object definition +open trait A // error +^ +open-infix-future.scala:5: error: expected class or object definition +open object B // error +^ +open-infix-future.scala:8: error: ';' expected but 'val' found. + infix val a: Int = 1 // error + ^ +open-infix-future.scala:9: error: ';' expected but 'var' found. + infix var b: Int = 1 // error + ^ +open-infix-future.scala:11: error: ';' expected but 'type' found. + open type D // error + ^ +open-infix-future.scala:14: error: illegal start of statement + open class E // error + ^ +open-infix-future.scala:15: error: ';' expected but 'def' found. + open def bla(y: Int) = y // error + ^ +7 errors found diff --git a/test/files/neg/open-infix-future.scala b/test/files/neg/open-infix-future.scala new file mode 100644 index 000000000000..2a250f3b006e --- /dev/null +++ b/test/files/neg/open-infix-future.scala @@ -0,0 +1,17 @@ +// scalac: -Xsource:3 +// + +open trait A // error +open object B // error + +class C { + infix val a: Int = 1 // error + infix var b: Int = 1 // error + + open type D // error + + def foo: Unit = { + open class E // error + open def bla(y: Int) = y // error + } +} diff --git a/test/files/pos/import-future.scala b/test/files/pos/import-future.scala new file mode 100644 index 000000000000..cfaff804af02 --- /dev/null +++ b/test/files/pos/import-future.scala @@ -0,0 +1,25 @@ +// scalac: -Xsource:3 +// + +import java.io as jio +import scala.{collection as c} + +import c.mutable as mut +import mut.ArrayBuffer as Buf + +object O { + val x: jio.IOException = ??? + val y = Buf(1, 2, 3) + + type OString = String + def foo22(x: Int) = x +} + +class C { + import O.{ foo22 as foo, OString as OS } + println(foo(22)) + val s: OS = "" + + import mut.* + val ab = ArrayBuffer(1) +} diff --git a/test/files/pos/open-infix-future.scala b/test/files/pos/open-infix-future.scala new file mode 100644 index 000000000000..8fee778d40cb --- /dev/null +++ b/test/files/pos/open-infix-future.scala @@ -0,0 +1,36 @@ +// scalac: -Xsource:3 +// + +open class A +infix class B[T, S] + +open infix class C[T, S] +open infix case class CC[T, S](x: Int) +infix open class D[T, S] +infix trait DT[T, S] + +open +infix +private +class E + +class F { + open infix class C1[T, S] + infix type X + + infix def foo(x: Int): Int = x +} + +object G { + open infix class C2[T, S] +} + +object Test { + val infix: Int = 1 + infix + 1 + val open: Int => Int = x => x + open(1) + open { + 2 + } +} diff --git a/test/files/pos/varargs-future.scala b/test/files/pos/varargs-future.scala new file mode 100644 index 000000000000..e8c9057e564b --- /dev/null +++ b/test/files/pos/varargs-future.scala @@ -0,0 +1,22 @@ +// scalac: -Xsource:3 +// + +class Test { + def foo(xs: Int*): Seq[Int] = xs + + val s: Seq[Int] = Seq(1, 2, 3) + foo(s*) + + // not very useful, but supported by Scala 3 (and matches what works with `: _*` syntax) + foo( + s*, + ) + + s match { + case Seq(elems*) => println(elems) + } + + s match { + case Seq(x, rest*) => println(rest) + } +} diff --git a/test/files/pos/wildcards-future.scala b/test/files/pos/wildcards-future.scala new file mode 100644 index 000000000000..928cab3648b0 --- /dev/null +++ b/test/files/pos/wildcards-future.scala @@ -0,0 +1,21 @@ +// scalac: -Xsource:3 +// +object Test { + val xs: List[?] = List(1, 2, 3) + val ys: Map[? <: AnyRef, ? >: Null] = Map() + + def foo(x: Any) = x match { + case x: List[?] => x + case _ => x + } + + // Only allowed in Scala 3 under -source 3.0-migration + type ? = Int + + val xs2: List[`?`] = List(1) + val xs3: List[Int] = xs2 + + def foo2(x: List[`?`]): List[Int] = x match { + case x: List[`?`] => x + } +}