summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaParsers.scala61
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaScanners.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala60
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) = {