Skip to content

Commit

Permalink
Warn on unary methods with parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Jun 9, 2021
1 parent 1d83d0f commit e5ecf14
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 1 deletion.
44 changes: 43 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package typer

import transform._
import core._
import Symbols._, Types._, Contexts._, Flags._, Names._, NameOps._
import Symbols._, Types._, Contexts._, Flags._, Names._, NameOps._, NameKinds._
import StdNames._, Denotations._, SymUtils._, Phases._, SymDenotations._
import NameKinds.DefaultGetterName
import Annotations._
Expand Down Expand Up @@ -1047,6 +1047,47 @@ object RefChecks {
report.error(i"private $sym cannot override ${other.showLocated}", sym.srcPos)
end checkNoPrivateOverrides

/** Check that unary method definition do not receive parameters.
* They can only receive inferred parameters such as type parameters and implicit parameters.
*/
def checkUnaryMethods(sym: Symbol)(using Context): Unit =
/** Check that the only term parameters are contextual or implicit */
def checkParameters(tpe: Type): Unit =
tpe match
case tpe: MethodType =>
if tpe.isImplicitMethod || tpe.isContextualMethod then
checkParameters(tpe.resType)
else
val what =
if tpe.paramNames.isEmpty then "empty parameter list.\n\nPossible fix: remove the `()` arguments."
else "parameters"
report.warning(s"Unary method cannot take $what", sym.sourcePos)
case tpe: PolyType =>
checkParameters(tpe.resType)
case _ =>
// ok

/** Skip leading type and contextual parameters, then skip the
* self parameter, and finally check the parameter
*/
def checkExtensionParameters(tpe: Type): Unit =
tpe match
case tpe: MethodType =>
assert(tpe.paramNames.length == 1)
if tpe.isContextualMethod then checkExtensionParameters(tpe.resType)
else checkParameters(tpe.resType)
case tpe: PolyType =>
checkExtensionParameters(tpe.resType)

if sym.name.startsWith(nme.UNARY_PREFIX.toString) then
if sym.is(Extension) || sym.name.is(ExtMethName) then
// if is method from `extension` or value class
checkExtensionParameters(sym.info)
else
checkParameters(sym.info)

end checkUnaryMethods

type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]

class OptLevelInfo {
Expand Down Expand Up @@ -1254,6 +1295,7 @@ class RefChecks extends MiniPhase { thisPhase =>
checkExperimentalAnnots(tree.symbol)
checkExperimentalSignature(tree.symbol, tree)
checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
checkUnaryMethods(tree.symbol)
tree
}

Expand Down
41 changes: 41 additions & 0 deletions tests/neg-custom-args/fatal-warnings/i9241.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class Foo {
def unary_~() : Foo = this // error
def unary_-(using Int)(): Foo = this // error
def unary_+()(implicit i: Int): Foo = this // error
def unary_![T](): Foo = this // error
}

class Bar {
def unary_~ : Bar = this
def unary_-(using Int): Bar = this
def unary_+(implicit i: Int): Bar = this
def unary_![T]: Bar = this
}

final class Baz private (val x: Int) extends AnyVal {
def unary_- : Baz = ???
def unary_+[T] : Baz = ???
def unary_!() : Baz = ??? // error
def unary_~(using Int) : Baz = ???
}

extension (x: Int)
def unary_- : Int = ???
def unary_+[T] : Int = ???
def unary_!() : Int = ??? // error
def unary_~(using Int) : Int = ???
end extension

extension [T](x: Short)
def unary_- : Int = ???
def unary_+[U] : Int = ???
def unary_!() : Int = ??? // error
def unary_~(using Int) : Int = ???
end extension

extension (using Int)(x: Byte)
def unary_- : Int = ???
def unary_+[U] : Int = ???
def unary_!() : Int = ??? // error
def unary_~(using Int) : Int = ???
end extension

0 comments on commit e5ecf14

Please sign in to comment.