Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Lint function arrow intended context function
  • Loading branch information
som-snytt committed Sep 2, 2025
commit f1d5ced6371ae310d5cbcce39ac85ca526bd22f3
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ private sealed trait WarningSettings:
private val WunstableInlineAccessors = BooleanSetting(WarningSetting, "WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.")
private val WtoStringInterpolated = BooleanSetting(WarningSetting, "Wtostring-interpolated", "Warn a standard interpolator used toString on a reference type.")
private val WrecurseWithDefault = BooleanSetting(WarningSetting, "Wrecurse-with-default", "Warn when a method calls itself with a default argument.")
private val WdubiousContextual = BooleanSetting(WarningSetting, "Wwrong-arrow", "Warn if function arrow was used instead of context literal ?=>.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private val WdubiousContextual = BooleanSetting(WarningSetting, "Wwrong-arrow", "Warn if function arrow was used instead of context literal ?=>.")
private val WwrongArrow = BooleanSetting(WarningSetting, "Wwrong-arrow", "Warn if function arrow was used instead of context literal ?=>.")

private val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting(
WarningSetting,
name = "Wunused",
Expand Down Expand Up @@ -311,6 +312,7 @@ private sealed trait WarningSettings:
def unstableInlineAccessors(using Context): Boolean = allOr(WunstableInlineAccessors)
def toStringInterpolated(using Context): Boolean = allOr(WtoStringInterpolated)
def recurseWithDefault(using Context): Boolean = allOr(WrecurseWithDefault)
def dubiousContextual(using Context): Boolean = allOr(WdubiousContextual)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def dubiousContextual(using Context): Boolean = allOr(WdubiousContextual)
def wrongArrow(using Context): Boolean = allOr(WdubiousContextual)

def checkInit(using Context): Boolean = allOr(WcheckInit)

/** -X "Extended" or "Advanced" settings */
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3824,6 +3824,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val ifun = desugar.makeContextualFunction(paramTypes, paramNamesOrNil, tree, erasedParams)
typr.println(i"make contextual function $tree / $pt ---> $ifun")
typedFunctionValue(ifun, pt)
.tap:
case tree @ Block((m1: DefDef) :: _, _: Closure) if ctx.settings.Whas.dubiousContextual =>
m1.rhs match
case Block((m2: DefDef) :: _, _: Closure) if m1.paramss.lengthCompare(m2.paramss) == 0 =>
val p1s = m1.symbol.info.asInstanceOf[MethodType].paramInfos
val p2s = m2.symbol.info.asInstanceOf[MethodType].paramInfos
if p1s.corresponds(p2s)(_ =:= _) then
report.warning(em"Context function adapts a lambda with the same parameter types, possibly ?=> was intended.", tree.srcPos)
case _ =>
case _ =>
}

/** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */
Expand Down
22 changes: 22 additions & 0 deletions tests/warn/i21187.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//> using options -Wall

def oops(msg: String) = sys.error(msg)

class Zone
object Zone:
inline def apply[T](inline f: Zone ?=> T): T = f(using new Zone)

inline def zone[A](inline f: Zone ?=> A) = Zone.apply(z => f(using z)) // warn suspicious contextualizing

def zone_?[A](f: Zone ?=> A) = Zone.apply(z => f(using z)) // warn

// intended
//inline def zone[A](inline f: Zone ?=> A): A = Zone.apply(z ?=> f(using z))

@main def hello =
// this swallows exceptions!
zone(oops("here")) // warn function value is not used
zone_?(oops("here")) // warn

// this doesn't
Zone(oops("not here"))
Loading