Skip to content

Commit e5ecf14

Browse files
committed
Warn on unary methods with parameters
Fixes #9241
1 parent 1d83d0f commit e5ecf14

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package typer
44

55
import transform._
66
import core._
7-
import Symbols._, Types._, Contexts._, Flags._, Names._, NameOps._
7+
import Symbols._, Types._, Contexts._, Flags._, Names._, NameOps._, NameKinds._
88
import StdNames._, Denotations._, SymUtils._, Phases._, SymDenotations._
99
import NameKinds.DefaultGetterName
1010
import Annotations._
@@ -1047,6 +1047,47 @@ object RefChecks {
10471047
report.error(i"private $sym cannot override ${other.showLocated}", sym.srcPos)
10481048
end checkNoPrivateOverrides
10491049

1050+
/** Check that unary method definition do not receive parameters.
1051+
* They can only receive inferred parameters such as type parameters and implicit parameters.
1052+
*/
1053+
def checkUnaryMethods(sym: Symbol)(using Context): Unit =
1054+
/** Check that the only term parameters are contextual or implicit */
1055+
def checkParameters(tpe: Type): Unit =
1056+
tpe match
1057+
case tpe: MethodType =>
1058+
if tpe.isImplicitMethod || tpe.isContextualMethod then
1059+
checkParameters(tpe.resType)
1060+
else
1061+
val what =
1062+
if tpe.paramNames.isEmpty then "empty parameter list.\n\nPossible fix: remove the `()` arguments."
1063+
else "parameters"
1064+
report.warning(s"Unary method cannot take $what", sym.sourcePos)
1065+
case tpe: PolyType =>
1066+
checkParameters(tpe.resType)
1067+
case _ =>
1068+
// ok
1069+
1070+
/** Skip leading type and contextual parameters, then skip the
1071+
* self parameter, and finally check the parameter
1072+
*/
1073+
def checkExtensionParameters(tpe: Type): Unit =
1074+
tpe match
1075+
case tpe: MethodType =>
1076+
assert(tpe.paramNames.length == 1)
1077+
if tpe.isContextualMethod then checkExtensionParameters(tpe.resType)
1078+
else checkParameters(tpe.resType)
1079+
case tpe: PolyType =>
1080+
checkExtensionParameters(tpe.resType)
1081+
1082+
if sym.name.startsWith(nme.UNARY_PREFIX.toString) then
1083+
if sym.is(Extension) || sym.name.is(ExtMethName) then
1084+
// if is method from `extension` or value class
1085+
checkExtensionParameters(sym.info)
1086+
else
1087+
checkParameters(sym.info)
1088+
1089+
end checkUnaryMethods
1090+
10501091
type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]
10511092

10521093
class OptLevelInfo {
@@ -1254,6 +1295,7 @@ class RefChecks extends MiniPhase { thisPhase =>
12541295
checkExperimentalAnnots(tree.symbol)
12551296
checkExperimentalSignature(tree.symbol, tree)
12561297
checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
1298+
checkUnaryMethods(tree.symbol)
12571299
tree
12581300
}
12591301

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class Foo {
2+
def unary_~() : Foo = this // error
3+
def unary_-(using Int)(): Foo = this // error
4+
def unary_+()(implicit i: Int): Foo = this // error
5+
def unary_![T](): Foo = this // error
6+
}
7+
8+
class Bar {
9+
def unary_~ : Bar = this
10+
def unary_-(using Int): Bar = this
11+
def unary_+(implicit i: Int): Bar = this
12+
def unary_![T]: Bar = this
13+
}
14+
15+
final class Baz private (val x: Int) extends AnyVal {
16+
def unary_- : Baz = ???
17+
def unary_+[T] : Baz = ???
18+
def unary_!() : Baz = ??? // error
19+
def unary_~(using Int) : Baz = ???
20+
}
21+
22+
extension (x: Int)
23+
def unary_- : Int = ???
24+
def unary_+[T] : Int = ???
25+
def unary_!() : Int = ??? // error
26+
def unary_~(using Int) : Int = ???
27+
end extension
28+
29+
extension [T](x: Short)
30+
def unary_- : Int = ???
31+
def unary_+[U] : Int = ???
32+
def unary_!() : Int = ??? // error
33+
def unary_~(using Int) : Int = ???
34+
end extension
35+
36+
extension (using Int)(x: Byte)
37+
def unary_- : Int = ???
38+
def unary_+[U] : Int = ???
39+
def unary_!() : Int = ??? // error
40+
def unary_~(using Int) : Int = ???
41+
end extension

0 commit comments

Comments
 (0)