Skip to content

Commit f1d5ced

Browse files
committed
Lint function arrow intended context function
1 parent 13d4963 commit f1d5ced

File tree

3 files changed

+34
-0
lines changed

3 files changed

+34
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ private sealed trait WarningSettings:
168168
private val WunstableInlineAccessors = BooleanSetting(WarningSetting, "WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.")
169169
private val WtoStringInterpolated = BooleanSetting(WarningSetting, "Wtostring-interpolated", "Warn a standard interpolator used toString on a reference type.")
170170
private val WrecurseWithDefault = BooleanSetting(WarningSetting, "Wrecurse-with-default", "Warn when a method calls itself with a default argument.")
171+
private val WdubiousContextual = BooleanSetting(WarningSetting, "Wwrong-arrow", "Warn if function arrow was used instead of context literal ?=>.")
171172
private val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting(
172173
WarningSetting,
173174
name = "Wunused",
@@ -311,6 +312,7 @@ private sealed trait WarningSettings:
311312
def unstableInlineAccessors(using Context): Boolean = allOr(WunstableInlineAccessors)
312313
def toStringInterpolated(using Context): Boolean = allOr(WtoStringInterpolated)
313314
def recurseWithDefault(using Context): Boolean = allOr(WrecurseWithDefault)
315+
def dubiousContextual(using Context): Boolean = allOr(WdubiousContextual)
314316
def checkInit(using Context): Boolean = allOr(WcheckInit)
315317

316318
/** -X "Extended" or "Advanced" settings */

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3824,6 +3824,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38243824
val ifun = desugar.makeContextualFunction(paramTypes, paramNamesOrNil, tree, erasedParams)
38253825
typr.println(i"make contextual function $tree / $pt ---> $ifun")
38263826
typedFunctionValue(ifun, pt)
3827+
.tap:
3828+
case tree @ Block((m1: DefDef) :: _, _: Closure) if ctx.settings.Whas.dubiousContextual =>
3829+
m1.rhs match
3830+
case Block((m2: DefDef) :: _, _: Closure) if m1.paramss.lengthCompare(m2.paramss) == 0 =>
3831+
val p1s = m1.symbol.info.asInstanceOf[MethodType].paramInfos
3832+
val p2s = m2.symbol.info.asInstanceOf[MethodType].paramInfos
3833+
if p1s.corresponds(p2s)(_ =:= _) then
3834+
report.warning(em"Context function adapts a lambda with the same parameter types, possibly ?=> was intended.", tree.srcPos)
3835+
case _ =>
3836+
case _ =>
38273837
}
38283838

38293839
/** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */

tests/warn/i21187.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//> using options -Wall
2+
3+
def oops(msg: String) = sys.error(msg)
4+
5+
class Zone
6+
object Zone:
7+
inline def apply[T](inline f: Zone ?=> T): T = f(using new Zone)
8+
9+
inline def zone[A](inline f: Zone ?=> A) = Zone.apply(z => f(using z)) // warn suspicious contextualizing
10+
11+
def zone_?[A](f: Zone ?=> A) = Zone.apply(z => f(using z)) // warn
12+
13+
// intended
14+
//inline def zone[A](inline f: Zone ?=> A): A = Zone.apply(z ?=> f(using z))
15+
16+
@main def hello =
17+
// this swallows exceptions!
18+
zone(oops("here")) // warn function value is not used
19+
zone_?(oops("here")) // warn
20+
21+
// this doesn't
22+
Zone(oops("not here"))

0 commit comments

Comments
 (0)