Skip to content

Warn on unary methods with parameters #12749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
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