Skip to content

Commit b806daa

Browse files
mboveldwijnanditielsa
committed
Warn when calling synchronized on Predef or on synthetic file objects
Fixes #17266 Co-Authored-By: Dale Wijnand <[email protected]> Co-Authored-By: itielsa <[email protected]>
1 parent 274babf commit b806daa

File tree

6 files changed

+81
-1
lines changed

6 files changed

+81
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
194194
case MissingArgumentListID // errorNumber: 178
195195
case MatchTypeScrutineeCannotBeHigherKindedID // errorNumber: 179
196196
case AmbiguousExtensionMethodID // errorNumber 180
197+
case CallToPredefSynchronizedID // errorNumber: 181
198+
case CallToPackageObjectSynchronizedID // errorNumber: 182
197199

198200
def errorNumber = ordinal - 1
199201

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,26 @@ class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol)
22842284
|It can be removed without changing the semantics of the program. This may indicate an error."""
22852285
}
22862286

2287+
class CallToPredefSynchronized(stat: untpd.Tree)(using Context)
2288+
extends Message(CallToPredefSynchronizedID) {
2289+
def kind = MessageKind.PotentialIssue
2290+
def msg(using Context) = i"Suspicious call to ${hl("Predef.synchronized")}"
2291+
def explain(using Context) =
2292+
i"""Top-level unqualifed calls to ${hl("synchronized")} are resolved to ${hl("Predef.synchronized")}.
2293+
|Therefore, the call above will globally lock using the ${hl("Predef")} object as a monitor.
2294+
|This might not be what you intented ."""
2295+
}
2296+
2297+
class CallToPackageObjectSynchronized(stat: untpd.Tree)(using Context)
2298+
extends Message(CallToPackageObjectSynchronizedID) {
2299+
def kind = MessageKind.PotentialIssue
2300+
def msg(using Context) = i"Suspicious top-level call to ${hl("this.synchronized")}"
2301+
def explain(using Context) =
2302+
i"""Top-level calls to ${hl("this.synchronized")} are resolved to ${hl("<package object>.synchronized")}.
2303+
|Therefore, the call above will lock using the generated package object as a monitor.
2304+
|This might not be what you intented ."""
2305+
}
2306+
22872307
class TraitCompanionWithMutableStatic()(using Context)
22882308
extends SyntaxMsg(TraitCompanionWithMutableStaticID) {
22892309
def msg(using Context) = i"Companion of traits cannot define mutable @static fields"

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,17 @@ object RefChecks {
10901090

10911091
end checkImplicitNotFoundAnnotation
10921092

1093+
def checkSynchronizedCall(tree: Tree, symbol: Symbol)(using Context) =
1094+
if symbol == defn.Object_synchronized then
1095+
tree.tpe match
1096+
case tp: NamedType =>
1097+
val prefixSymbol = tp.prefix.typeSymbol
1098+
if prefixSymbol == defn.ScalaPredefModuleClass then
1099+
report.warning(CallToPredefSynchronized(tree), tree)
1100+
if prefixSymbol.isPackageObject && !tp.symbol.isConstructor then
1101+
report.warning(CallToPackageObjectSynchronized(tree), tree)
1102+
case _ => ()
1103+
10931104
}
10941105
import RefChecks._
10951106

@@ -1168,6 +1179,15 @@ class RefChecks extends MiniPhase { thisPhase =>
11681179
report.error(ex, tree.srcPos)
11691180
tree
11701181
}
1182+
1183+
override def transformSelect(tree: Select)(using Context): Tree =
1184+
checkSynchronizedCall(tree, tree.denot.symbol)
1185+
tree
1186+
1187+
override def transformIdent(tree: Ident)(using Context): Tree =
1188+
checkSynchronizedCall(tree, tree.symbol)
1189+
tree
1190+
11711191
}
11721192

11731193
/* todo: rewrite and re-enable

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ class CompilationTests {
145145
compileFilesInDir("tests/neg-custom-args/feature", defaultOptions.and("-Xfatal-warnings", "-feature")),
146146
compileFilesInDir("tests/neg-custom-args/no-experimental", defaultOptions.and("-Yno-experimental")),
147147
compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")),
148-
compileFilesInDir("tests/neg-custom-args/explain", defaultOptions.and("-explain")),
148+
compileFilesInDir("tests/neg-custom-args/explain", defaultOptions.and("-Xfatal-warnings", "-explain")),
149149
compileFile("tests/neg-custom-args/avoid-warn-deprecation.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
150150
compileFile("tests/neg-custom-args/i3246.scala", scala2CompatMode),
151151
compileFile("tests/neg-custom-args/overrideClass.scala", scala2CompatMode),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- [E181] Potential Issue Error: tests/neg-custom-args/explain/i17266.scala:2:2 ----------------------------------------
2+
2 | synchronized { // error
3+
| ^^^^^^^^^^^^
4+
| Suspicious call to Predef.synchronized
5+
|---------------------------------------------------------------------------------------------------------------------
6+
| Explanation (enabled by `-explain`)
7+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
8+
| Top-level unqualifed calls to synchronized are resolved to Predef.synchronized.
9+
| Therefore, the call above will globally lock using the Predef object as a monitor.
10+
| This might not be what you intented .
11+
---------------------------------------------------------------------------------------------------------------------
12+
-- [E182] Potential Issue Error: tests/neg-custom-args/explain/i17266.scala:7:7 ----------------------------------------
13+
7 | this.synchronized { // error
14+
| ^^^^^^^^^^^^^^^^^
15+
| Suspicious top-level call to this.synchronized
16+
|---------------------------------------------------------------------------------------------------------------------
17+
| Explanation (enabled by `-explain`)
18+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19+
| Top-level calls to this.synchronized are resolved to <package object>.synchronized.
20+
| Therefore, the call above will lock using the generated package object as a monitor.
21+
| This might not be what you intented .
22+
---------------------------------------------------------------------------------------------------------------------
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
def test1 =
2+
synchronized { // error
3+
println("hello")
4+
}
5+
6+
def test2 =
7+
this.synchronized { // error
8+
println("hello")
9+
}
10+
11+
object MyLib
12+
def test3 =
13+
import MyLib.*
14+
synchronized { // not an error yet, but might be in the future
15+
println("hello")
16+
}

0 commit comments

Comments
 (0)