Skip to content

-Xlint 1l #14753

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

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.rewrites.Rewrites
import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory}

import scala.util.chaining._
import scala.util.chaining.given

class ScalaSettings extends SettingGroup with AllScalaSettings

Expand Down Expand Up @@ -156,6 +156,8 @@ private sealed trait WarningSettings:
self: SettingGroup =>
val Whelp: Setting[Boolean] = BooleanSetting("-W", "Print a synopsis of warning options.")
val XfatalWarnings: Setting[Boolean] = BooleanSetting("-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings"))
val Xlint: Setting[List[String]] = EnumSetting("-Wlint", "warning", "Enable recommended warnings.", LintWarning.values, default = Nil, aliases = List("-Xlint"))
def isLintEnabled(w: LintWarning)(using Context): Boolean = Xlint.value.contains(w.toString)

val Wunused: Setting[List[String]] = MultiChoiceSetting(
name = "-Wunused",
Expand Down Expand Up @@ -339,3 +341,7 @@ private sealed trait YSettings:
val YforceInlineWhileTyping: Setting[Boolean] = BooleanSetting("-Yforce-inline-while-typing", "Make non-transparent inline methods inline when typing. Emulates the old inlining behavior of 3.0.0-M3.")
end YSettings

/** Warnings that may be queried with `ctx.settings.isLintEnabled`. */
enum LintWarning:
case deprecation, longLit
end LintWarning
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import dotty.tools.io.{AbstractFile, Directory, JarArchive, PlainDirectory}

import annotation.tailrec
import collection.mutable.ArrayBuffer
import reflect.ClassTag
import reflect.{ClassTag, Enum}
import scala.util.{Success, Failure}

object Settings:
Expand Down Expand Up @@ -61,7 +61,8 @@ object Settings:
prefix: String = "",
aliases: List[String] = Nil,
depends: List[(Setting[?], Any)] = Nil,
propertyClass: Option[Class[?]] = None)(private[Settings] val idx: Int) {
propertyClass: Option[Class[?]] = None,
)(private[Settings] val idx: Int) {

private var changed: Boolean = false

Expand Down Expand Up @@ -265,6 +266,9 @@ object Settings:
def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] =
publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases))

def EnumSetting[E <: Enum](name: String, helpArg: String, descr: String, choices: Array[E], default: List[String], aliases: List[String] = Nil): Setting[List[String]] =
publish(Setting(name, descr, default, helpArg, Some(choices.toList.map(_.toString)), aliases = aliases))

def IntSetting(name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] =
publish(Setting(name, descr, default, aliases = aliases))

Expand Down
15 changes: 6 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1182,9 +1182,9 @@ object Parsers {
case EXPOLIT => return Number(digits, NumberKind.Floating)
case _ =>
}
import scala.util.FromDigits._
import scala.util.FromDigits.*
val value =
try token match {
try token match
case INTLIT => intFromDigits(digits, in.base)
case LONGLIT => longFromDigits(digits, in.base)
case FLOATLIT => floatFromDigits(digits)
Expand All @@ -1194,15 +1194,11 @@ object Parsers {
case TRUE => true
case FALSE => false
case NULL => null
case _ =>
syntaxErrorOrIncomplete(IllegalLiteral())
null
}
catch {
case ex: FromDigitsException => syntaxErrorOrIncomplete(ex.getMessage)
}
case _ => syntaxErrorOrIncomplete(IllegalLiteral()); null
catch case ex: FromDigitsException => syntaxError(ex.getMessage)
Literal(Constant(value))
}
end literalOf

if (inStringInterpolation) {
val t = in.token match {
Expand Down Expand Up @@ -1252,6 +1248,7 @@ object Parsers {
}
}
}
end literal

private def interpolatedString(inPattern: Boolean = false): Tree = atSpan(in.offset) {
val segmentBuf = new ListBuffer[Tree]
Expand Down
69 changes: 38 additions & 31 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import util.Chars._
import util.{SourcePosition, CharBuffer}
import util.Spans.Span
import config.Config
import config.LintWarning
import Tokens._
import scala.annotation.{switch, tailrec}
import scala.collection.mutable
Expand All @@ -21,6 +22,8 @@ import config.Feature.migrateTo3
import config.SourceVersion.`3.0`
import reporting.{NoProfile, Profile}

import java.lang.Character.isDigit

object Scanners {

/** Offset into source character array */
Expand Down Expand Up @@ -152,9 +155,9 @@ object Scanners {
strVal = litBuf.toString
litBuf.clear()

@inline def isNumberSeparator(c: Char): Boolean = c == '_'
inline def isNumberSeparator(c: Char): Boolean = c == '_'

@inline def removeNumberSeparators(s: String): String = if (s.indexOf('_') == -1) s else s.replace("_", "")
inline def removeNumberSeparators(s: String): String = if s.indexOf('_') == -1 then s else s.replace("_", "")

// disallow trailing numeric separator char, but continue lexing
def checkNoTrailingSeparator(): Unit =
Expand Down Expand Up @@ -887,11 +890,12 @@ object Scanners {
//case 'b' | 'B' => base = 2 ; nextChar()
case _ => base = 10 ; putChar('0')
}
if (base != 10 && !isNumberSeparator(ch) && digit2int(ch, base) < 0)
if base != 10 && !isNumberSeparator(ch) && digit2int(ch, base) < 0 then
error("invalid literal number")
else
getNumber()
}
fetchLeadingZero()
getNumber()
case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
base = 10
getNumber()
Expand Down Expand Up @@ -967,7 +971,7 @@ object Scanners {
case '.' =>
nextChar()
if ('0' <= ch && ch <= '9') {
putChar('.'); getFraction(); setStrVal()
putChar('.'); getFraction()
}
else
token = DOT
Expand Down Expand Up @@ -1427,7 +1431,7 @@ object Scanners {
/** read fractional part and exponent of floating point number
* if one is present.
*/
protected def getFraction(): Unit = {
protected def getFraction(): Unit =
token = DECILIT
while ('0' <= ch && ch <= '9' || isNumberSeparator(ch)) {
putChar(ch)
Expand Down Expand Up @@ -1465,41 +1469,44 @@ object Scanners {
token = FLOATLIT
}
checkNoLetter()
}
setStrVal()
end getFraction
def checkNoLetter(): Unit =
if (isIdentifierPart(ch) && ch >= ' ')
error("Invalid literal number")

/** Read a number into strVal and set base
*/
protected def getNumber(): Unit = {
while (isNumberSeparator(ch) || digit2int(ch, base) >= 0) {
putChar(ch)
nextChar()
}
checkNoTrailingSeparator()
token = INTLIT
if (base == 10 && ch == '.') {
val lch = lookaheadChar()
if ('0' <= lch && lch <= '9') {
putChar('.')
nextChar()
getFraction()
}
}
else (ch: @switch) match {
case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' =>
if (base == 10) getFraction()
case 'l' | 'L' =>
protected def getNumber(): Unit =
def consumeDigits(): Unit =
while isNumberSeparator(ch) || digit2int(ch, base) >= 0 do
putChar(ch)
nextChar()
token = LONGLIT
case _ =>
}
// at dot with digit following
def restOfNonIntegralNumber(): Unit =
putChar('.')
nextChar()
getFraction()
// 1l is an acknowledged bad practice
def lintel(): Unit =
if ch == 'l' && ctx.settings.isLintEnabled(LintWarning.longLit) then
val msg = "Lowercase el for long is not recommended because it is easy to confuse with numeral 1; use uppercase L instead"
report.deprecationWarning(msg, sourcePos(offset + litBuf.length))
// after int: 5e7f, 42L, 42.toDouble but not 42b.
def restOfNumber(): Unit =
ch match
case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' => getFraction()
case 'l' | 'L' => lintel() ; token = LONGLIT ; setStrVal() ; nextChar()
case _ => token = INTLIT ; setStrVal() ; checkNoLetter()

// consume leading digits
consumeDigits()

checkNoTrailingSeparator()

setStrVal()
}
val detectedFloat: Boolean = base == 10 && ch == '.' && isDigit(lookaheadChar())
if detectedFloat then restOfNonIntegralNumber() else restOfNumber()
end getNumber

private def finishCharLit(): Unit = {
nextChar()
Expand Down
30 changes: 12 additions & 18 deletions tests/neg/literals.scala
Original file line number Diff line number Diff line change
@@ -1,52 +1,47 @@
trait RejectedLiterals {
def missingHex: Int = { 0x } // error: invalid literal number
}
/*
// nsc: -Ywarn-octal-literal -Xfatal-warnings -deprecation
trait RejectedLiterals {

def missingHex: Int = { 0x } // line 4: was: not reported, taken as zero
def missingHex: Int = { 0x } // error: invalid literal number

def leadingZeros: Int = { 01 } // line 6: no leading zero

def tooManyZeros: Int = { 00 } // line 8: no leading zero

def zeroOfNine: Int = { 09 } // line 10: no leading zero

def orphanDot: Int = { 9. } // line 12: ident expected
def orphanDot: Int = { 9. } // error: ident expected

def zeroOfNineDot: Int = { 09. } // line 14: malformed integer, ident expected
def zeroOfNineDot: Int = { 09. } // error: malformed integer, ident expected

def noHexFloat: Double = { 0x1.2 } // line 16: ';' expected but double literal found.
def noHexFloat: Double = { 0x1.2 } // error: ';' expected but double literal found.

}

trait Braceless {

def missingHex: Int = 0x // line 22: was: not reported, taken as zero
def missingHex: Int = 0x // error: was: not reported, taken as zero

def leadingZeros: Int = 01 // line 24: no leading zero

def tooManyZeros: Int = 00 // line 26: no leading zero

def zeroOfNine: Int = 09 // line 28: no leading zero

def orphanDot: Int = 9. // line 30: ident expected
def orphanDot: Int = 9. // should be: ident expected

def zeroOfNineDot: Int = 09. // line 32: malformed integer, ident expected
def zeroOfNineDot: Int = 09. // error: an identifier expected, but 'def' found, shoule be ident expected

def noHexFloat: Double = 0x1.2 // line 34: ';' expected but double literal found.
def noHexFloat: Double = 0x1.2 // should be: ';' expected but double literal found.
}

trait MoreSadness {

def tooTiny: Float = { 0.7e-45f } // floating point number too small
def tooTiny: Float = { 0.7e-45f } // error: floating point number too small

def twoTiny: Double = { 2.0e-324 } // double precision floating point number too small
def twoTiny: Double = { 2.0e-324 } // error: double precision floating point number too small

def tooHuge: Float = { 3.4028236E38f } // floating point number too large
def tooHuge: Float = { 3.4028236E38f } // error: floating point number too large

def twoHuge: Double = { 1.7976931348623159e308 } // double precision floating point number too large
def twoHuge: Double = { 1.7976931348623159e308 } // error: double precision floating point number too large
}

trait Lengthy {
Expand All @@ -55,4 +50,3 @@ trait Lengthy {

def worse = 123l
}
*/
11 changes: 11 additions & 0 deletions tests/neg/longlit.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// scalac: -Werror -Xlint:longLit,deprecation -deprecation

trait DejectedLiterals:

def bad = 1l // error

def worse = 123l // error

def worstest = 32l // error

// Lowercase el for long is not recommended because it is easy to confuse with numeral 1; use uppercase L instead
6 changes: 5 additions & 1 deletion tests/neg/t6124.check
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@
| ^
| trailing separator is not allowed
-- Error: tests/neg/t6124.scala:24:12 ----------------------------------------------------------------------------------
24 | val x5 = 0_x52 // error
24 | val x5 = 0_x52 // error // error
| ^
| trailing separator is not allowed
-- Error: tests/neg/t6124.scala:24:11 ----------------------------------------------------------------------------------
24 | val x5 = 0_x52 // error // error
| ^
| Invalid literal number
-- Error: tests/neg/t6124.scala:26:13 ----------------------------------------------------------------------------------
26 | val x8 = 0x52_ // error
| ^
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/t6124.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait T {
val x1 = _52 // error
val x3 = 52_ // error

val x5 = 0_x52 // error
val x5 = 0_x52 // error // error
val x6 = 0x_52
val x8 = 0x52_ // error
val x9 = 0_52
Expand Down