Skip to content

Commit 23a6b76

Browse files
authored
Merge pull request #635 from scala/backport-lts-3.3-22707
Backport "Warn about encoded pkg obj names" to 3.3 LTS
2 parents cc18792 + 895a950 commit 23a6b76

File tree

17 files changed

+132
-32
lines changed

17 files changed

+132
-32
lines changed

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.io.AbstractFile
1010
import reporting.*
1111
import core.Decorators.*
1212
import config.Feature
13+
import util.chaining.*
1314

1415
import scala.util.control.NonFatal
1516
import fromtasty.{TASTYCompiler, TastyFileUtil}
@@ -78,15 +79,26 @@ class Driver {
7879
MacroClassLoader.init(ictx)
7980
Positioned.init(using ictx)
8081

81-
inContext(ictx) {
82+
inContext(ictx):
8283
if !ctx.settings.YdropComments.value || ctx.settings.YreadComments.value then
8384
ictx.setProperty(ContextDoc, new ContextDocstrings)
8485
val fileNamesOrNone = command.checkUsage(summary, sourcesRequired)(using ctx.settings)(using ctx.settingsState)
85-
fileNamesOrNone.map { fileNames =>
86+
fileNamesOrNone.map: fileNames =>
8687
val files = fileNames.map(ctx.getFile)
8788
(files, fromTastySetup(files))
88-
}
89-
}
89+
.tap: _ =>
90+
if !ctx.settings.Yreporter.isDefault then
91+
ctx.settings.Yreporter.value match
92+
case "help" =>
93+
case reporterClassName =>
94+
try
95+
Class.forName(reporterClassName).getDeclaredConstructor().newInstance() match
96+
case userReporter: Reporter =>
97+
ictx.setReporter(userReporter)
98+
case badReporter => report.error:
99+
em"Not a reporter: ${ctx.settings.Yreporter.value}"
100+
catch case e: ReflectiveOperationException => report.error:
101+
em"Could not create reporter ${ctx.settings.Yreporter.value}: ${e}"
90102
}
91103

92104
/** Setup extra classpath of tasty and jar files */

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,25 +1024,27 @@ object desugar {
10241024
else tree
10251025
}
10261026

1027-
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
1028-
1029-
def check(name: Name, errSpan: Span): Unit = name match
1030-
case name: SimpleName if !errSpan.isSynthetic && name.exists(Chars.willBeEncoded) =>
1031-
report.warning(em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.", mdef.source.atSpan(errSpan))
1032-
case _ =>
1033-
1034-
def loop(part: RefTree): Unit = part match
1035-
case part @ Ident(name) => check(name, part.span)
1036-
case part @ Select(qual: RefTree, name) =>
1037-
check(name, part.nameSpan)
1038-
loop(qual)
1027+
def checkSimplePackageName(name: Name, errSpan: Span, source: SourceFile, isPackageObject: Boolean)(using Context) =
1028+
if !ctx.isAfterTyper then
1029+
name match
1030+
case name: SimpleName if (isPackageObject || !errSpan.isSynthetic) && name.exists(Chars.willBeEncoded) =>
1031+
report.warning(EncodedPackageName(name), source.atSpan(errSpan))
10391032
case _ =>
10401033

1034+
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
1035+
def check(name: Name, errSpan: Span) = checkSimplePackageName(name, errSpan, mdef.source, isPackageObject = false)
10411036
mdef match
1042-
case pdef: PackageDef => loop(pdef.pid)
1043-
case mdef: ModuleDef if mdef.mods.is(Package) => check(mdef.name, mdef.nameSpan)
1044-
case _ =>
1045-
end checkPackageName
1037+
case pdef: PackageDef =>
1038+
def loop(part: RefTree): Unit = part match
1039+
case part @ Ident(name) => check(name, part.span)
1040+
case part @ Select(qual: RefTree, name) =>
1041+
check(name, part.nameSpan)
1042+
loop(qual)
1043+
case _ =>
1044+
loop(pdef.pid)
1045+
case mdef: ModuleDef if mdef.mods.is(Package) =>
1046+
check(mdef.name, mdef.nameSpan)
1047+
case _ =>
10461048

10471049
/** The normalized name of `mdef`. This means
10481050
* 1. Check that the name does not redefine a Scala core class.

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ private sealed trait YSettings:
377377
val YdisableFlatCpCaching: Setting[Boolean] = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
378378

379379
val Yscala2Unpickler: Setting[String] = StringSetting("-Yscala2-unpickler", "", "Control where we may get Scala 2 symbols from. This is either \"always\", \"never\", or a classpath.", "always")
380+
val YnoReporter: Setting[Boolean] = BooleanSetting("-Yno-reporter", "Diagnostics are silently consumed")
381+
val Yreporter: Setting[String] = StringSetting(name = "-Yreporter", helpArg = "<class>", descr = "Specify a dotty.tools.dotc.reporting.Reporter", default = "dotty.tools.dotc.reporting.ConsoleReporter")
380382

381383
val YnoImports: Setting[Boolean] = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
382384
val Yimports: Setting[List[String]] = MultiStringSetting("-Yimports", helpArg="", "Custom root imports. If set, none of scala.*, java.lang.*, or Predef.* will be imported unless explicitly included.")

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
235235
case CannotInstantiateQuotedTypeVarID // errorNumber: 219
236236
case DefaultShadowsGivenID // errorNumber: 220
237237
case RecurseWithDefaultID // errorNumber: 221
238+
case EncodedPackageNameID // errorNumber: 222
238239

239240
def errorNumber = ordinal - 1
240241

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ object Reporter {
3030
override def report(dia: Diagnostic)(using Context): Unit = ()
3131
}
3232

33+
/** A silent reporter for testing */
34+
class SilentReporter extends Reporter:
35+
def doReport(dia: Diagnostic)(using Context): Unit = ()
36+
3337
type ErrorHandler = (Diagnostic, Context) => Unit
3438

3539
private val defaultIncompleteHandler: ErrorHandler =

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3391,4 +3391,16 @@ final class RecurseWithDefault(name: Name)(using Context) extends TypeMsg(Recurs
33913391
override protected def msg(using Context): String =
33923392
i"Recursive call used a default argument for parameter $name."
33933393
override protected def explain(using Context): String =
3394-
"It's more explicit to pass current or modified arguments in a recursion."
3394+
"It's more explicit to pass current or modified arguments in a recursion."
3395+
3396+
final class EncodedPackageName(name: Name)(using Context) extends SyntaxMsg(EncodedPackageNameID):
3397+
override protected def msg(using Context): String =
3398+
i"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour."
3399+
override protected def explain(using Context): String =
3400+
i"""Tools may not handle directories whose names differ from their corresponding package names.
3401+
|For example, `p-q` is encoded as `p$$minusq` when written to the file system.
3402+
|
3403+
|Package objects derive their names from the file names, so files such as `myfile.test.scala`
3404+
|or `myfile-test.scala` can produce encoded names for the generated package objects.
3405+
|
3406+
|In this case, the name `$name` is encoded as `${name.encode}`."""

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import EtaExpansion.etaExpand
3131
import TypeComparer.CompareResult
3232
import inlines.{Inlines, PrepareInlineable}
3333
import util.Spans.*
34+
import util.chaining.*
3435
import util.common.*
3536
import util.{Property, SimpleIdentityMap, SrcPos}
3637
import Applications.{tupleComponentTypes, wrapDefs, defaultArgument}
@@ -2921,12 +2922,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29212922
inContext(ctx.packageContext(tree, pkg)) {
29222923
// If it exists, complete the class containing the top-level definitions
29232924
// before typing any statement in the package to avoid cycles as in i13669.scala
2924-
val topLevelClassName = desugar.packageObjectName(ctx.source).moduleClassName
2925-
pkg.moduleClass.info.decls.lookup(topLevelClassName).ensureCompleted()
2925+
val packageObjectName = desugar.packageObjectName(ctx.source)
2926+
val topLevelClassSymbol = pkg.moduleClass.info.decls.lookup(packageObjectName.moduleClassName)
2927+
topLevelClassSymbol.ensureCompleted()
29262928
var stats1 = typedStats(tree.stats, pkg.moduleClass)._1
29272929
if (!ctx.isAfterTyper)
29282930
stats1 = stats1 ++ typedBlockStats(MainProxies.proxies(stats1))._1
29292931
cpy.PackageDef(tree)(pid1, stats1).withType(pkg.termRef)
2932+
.tap: _ =>
2933+
if !ctx.isAfterTyper
2934+
&& pkg != defn.EmptyPackageVal
2935+
&& !topLevelClassSymbol.info.decls.filter(sym => !sym.isConstructor && !sym.is(Synthetic)).isEmpty
2936+
then
2937+
desugar.checkSimplePackageName(packageObjectName, tree.span, ctx.source, isPackageObject = true)
29302938
}
29312939
case _ =>
29322940
// Package will not exist if a duplicate type has already been entered, see `tests/neg/1708.scala`

compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ class SemanticdbTests:
143143
"-classpath", target.toString,
144144
"-Xignore-scala2-macros",
145145
"-usejavacp",
146-
"-Wunused:all"
146+
"-Wunused:all",
147+
"-Yreporter:dotty.tools.dotc.reporting.Reporter$SilentReporter",
147148
) ++ inputFiles().map(_.toString)
148149
val exit = Main.process(args)
149150
assertFalse(s"dotc errors: ${exit.errorCount}", exit.hasErrors)

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ object TestConfiguration {
3030
Properties.dottyLibrary
3131
))
3232

33+
val silenceOptions = Array(
34+
"-Wconf:id=E222:s", // name=EncodedPackageName don't warn about file names with hyphens
35+
)
36+
3337
val withCompilerClasspath = mkClasspath(List(
3438
Properties.scalaLibrary,
3539
Properties.scalaAsm,
@@ -63,7 +67,7 @@ object TestConfiguration {
6367

6468
val yCheckOptions = Array("-Ycheck:all")
6569

66-
val commonOptions = Array("-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions
70+
val commonOptions = Array("-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions ++ silenceOptions
6771
val defaultOptions = TestFlags(basicClasspath, commonOptions)
6872
val unindentOptions = TestFlags(basicClasspath, Array("-no-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions)
6973
val withCompilerOptions =

tests/neg/i22670.check

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- [E222] Syntax Error: tests/neg/i22670/i22670-macro.scala:4:8 --------------------------------------------------------
2+
4 |package xy // error named package required for warning
3+
|^
4+
|The package name `i22670-macro$package` will be encoded on the classpath, and can lead to undefined behaviour.
5+
5 |import scala.quoted.*
6+
6 |transparent inline def foo =
7+
7 | ${ fooImpl }
8+
8 |def fooImpl(using Quotes): Expr[Any] =
9+
9 | Expr("hello")
10+
|--------------------------------------------------------------------------------------------------------------------
11+
| Explanation (enabled by `-explain`)
12+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
13+
| Tools may not handle directories whose names differ from their corresponding package names.
14+
| For example, `p-q` is encoded as `p$minusq` when written to the file system.
15+
|
16+
| Package objects derive their names from the file names, so files such as `myfile.test.scala`
17+
| or `myfile-test.scala` can produce encoded names for the generated package objects.
18+
|
19+
| In this case, the name `i22670-macro$package` is encoded as `i22670$minusmacro$package`.
20+
--------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)