diff options
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/javac/JavaParsers.scala | 61 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/javac/JavaScanners.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 60 |
3 files changed, 105 insertions, 18 deletions
diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index eb25eb6e06..876247510b 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -567,10 +567,48 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName): ValDef = { val tpt1 = optArrayBrackets(tpt) - if (in.token == EQUALS && !mods.isParameter) skipTo(COMMA, SEMI) + + /** Tries to detect final static literals syntactically and returns a constant type replacement */ + def optConstantTpe(): Tree = { + def constantTpe(const: Constant): Tree = TypeTree(ConstantType(const)) + + def forConst(const: Constant): Tree = { + if (in.token != SEMI) tpt1 + else { + def isStringTyped = tpt1 match { + case Ident(TypeName("String")) => true + case _ => false + } + if (const.tag == StringTag && isStringTyped) constantTpe(const) + else if (tpt1.tpe != null && (const.tag == BooleanTag || const.isNumeric)) { + // for example, literal 'a' is ok for float. 127 is ok for byte, but 128 is not. + val converted = const.convertTo(tpt1.tpe) + if (converted == null) tpt1 + else constantTpe(converted) + } else tpt1 + } + } + + in.nextToken() // EQUALS + if (mods.hasFlag(Flags.STATIC) && mods.isFinal) { + val neg = in.token match { + case MINUS | BANG => in.nextToken(); true + case _ => false + } + tryLiteral(neg).map(forConst).getOrElse(tpt1) + } else tpt1 + } + + val tpt2: Tree = + if (in.token == EQUALS && !mods.isParameter) { + val res = optConstantTpe() + skipTo(COMMA, SEMI) + res + } else tpt1 + val mods1 = if (mods.isFinal) mods &~ Flags.FINAL else mods | Flags.MUTABLE atPos(pos) { - ValDef(mods1, name, tpt1, blankExpr) + ValDef(mods1, name, tpt2, blankExpr) } } @@ -843,6 +881,25 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree) } + def tryLiteral(negate: Boolean = false): Option[Constant] = { + val l = in.token match { + case TRUE => !negate + case FALSE => negate + case CHARLIT => in.name.charAt(0) + case INTLIT => in.intVal(negate).toInt + case LONGLIT => in.intVal(negate) + case FLOATLIT => in.floatVal(negate).toFloat + case DOUBLELIT => in.floatVal(negate) + case STRINGLIT => in.name.toString + case _ => null + } + if (l == null) None + else { + in.nextToken() + Some(Constant(l)) + } + } + /** CompilationUnit ::= [package QualId semi] TopStatSeq */ def compilationUnit(): Tree = { diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index ac86dfd665..94c9d07939 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -89,6 +89,7 @@ trait JavaScanners extends ast.parser.ScannersCommon { javanme.ELSEkw -> ELSE, javanme.ENUMkw -> ENUM, javanme.EXTENDSkw -> EXTENDS, + javanme.FALSEkw -> FALSE, javanme.FINALkw -> FINAL, javanme.FINALLYkw -> FINALLY, javanme.FLOATkw -> FLOAT, @@ -118,6 +119,7 @@ trait JavaScanners extends ast.parser.ScannersCommon { javanme.THROWkw -> THROW, javanme.THROWSkw -> THROWS, javanme.TRANSIENTkw -> TRANSIENT, + javanme.TRUEkw -> TRUE, javanme.TRYkw -> TRY, javanme.VOIDkw -> VOID, javanme.VOLATILEkw -> VOLATILE, diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 508d205424..00e0517df6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4501,20 +4501,55 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null val opeqStart = if (Statistics.canEnable) Statistics.startTimer(failedOpEqNanos) else null - def onError(reportError: => Tree): Tree = fun match { - case Select(qual, name) if !mode.inPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => + def isConversionCandidate(qual: Tree, name: Name): Boolean = + !mode.inPatternMode && nme.isOpAssignmentName(TermName(name.decode)) && !qual.exists(_.isErroneous) + + def reportError(error: SilentTypeError): Tree = { + error.reportableErrors foreach context.issue + error.warnings foreach { case (p, m) => context.warning(p, m) } + args foreach (arg => typed(arg, mode, ErrorType)) + setError(tree) + } + def advice1(convo: Tree, errors: List[AbsTypeError], err: SilentTypeError): List[AbsTypeError] = + errors.map { e => + if (e.errPos == tree.pos) { + val header = f"${e.errMsg}%n Expression does not convert to assignment because:%n " + val expansion = f"%n expansion: ${show(convo)}" + NormalTypeError(tree, err.errors.flatMap(_.errMsg.lines.toList).mkString(header, f"%n ", expansion)) + } else e + } + def advice2(errors: List[AbsTypeError]): List[AbsTypeError] = + errors.map { e => + if (e.errPos == tree.pos) { + val msg = f"${e.errMsg}%n Expression does not convert to assignment because receiver is not assignable." + NormalTypeError(tree, msg) + } else e + } + def onError(error: SilentTypeError): Tree = fun match { + case Select(qual, name) if isConversionCandidate(qual, name) => val qual1 = typedQualifier(qual) if (treeInfo.isVariableOrGetter(qual1)) { if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart) - convertToAssignment(fun, qual1, name, args) + val erred = qual1.isErroneous || args.exists(_.isErroneous) + if (erred) reportError(error) else { + val convo = convertToAssignment(fun, qual1, name, args) + silent(op = _.typed1(convo, mode, pt)) match { + case SilentResultValue(t) => t + case err: SilentTypeError => reportError(SilentTypeError(advice1(convo, error.errors, err), error.warnings)) + } + } } else { if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) - reportError + val Apply(Select(qual2, _), args2) = tree + val erred = qual2.isErroneous || args2.exists(_.isErroneous) + reportError { + if (erred) error else SilentTypeError(advice2(error.errors), error.warnings) + } } case _ => if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) - reportError + reportError(error) } val silentResult = silent( op = _.typed(fun, mode.forFunMode, funpt), @@ -4539,13 +4574,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tryTypedApply(fun2, args) else doTypedApply(tree, fun2, args, mode, pt) - case err: SilentTypeError => - onError({ - err.reportableErrors foreach context.issue - err.warnings foreach { case (p, m) => context.warning(p, m) } - args foreach (arg => typed(arg, mode, ErrorType)) - setError(tree) - }) + case err: SilentTypeError => onError(err) } } @@ -4588,7 +4617,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper Select(vble.duplicate, prefix) setPos fun.pos.focus, args) setPos tree.pos.makeTransparent ) setPos tree.pos - def mkUpdate(table: Tree, indices: List[Tree]) = { + def mkUpdate(table: Tree, indices: List[Tree]) = gen.evalOnceAll(table :: indices, context.owner, context.unit) { case tab :: is => def mkCall(name: Name, extraArgs: Tree*) = ( @@ -4603,9 +4632,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ) case _ => EmptyTree } - } - val tree1 = qual match { + val assignment = qual match { case Ident(_) => mkAssign(qual) @@ -4621,7 +4649,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => UnexpectedTreeAssignmentConversionError(qual) } } - typed1(tree1, mode, pt) + assignment } def typedSuper(tree: Super) = { |