Skip to content

Commit 304b18d

Browse files
Backport "Add missing span to extension method select" to LTS (#20675)
Backports #18557 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 0b851b2 + 10f2e71 commit 304b18d

File tree

13 files changed

+130
-37
lines changed

13 files changed

+130
-37
lines changed

compiler/src/dotty/tools/dotc/inlines/Inliner.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ class Inliner(val call: tpd.Tree)(using Context):
771771

772772
override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
773773
val locked = ctx.typerState.ownedVars
774-
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
774+
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
775775
val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
776776
val reducedProjection = reducer.reduceProjection(resNoReduce)
777777
if reducedProjection.isType then

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ trait Applications extends Compatibility {
959959
val resultType =
960960
if !originalResultType.isRef(defn.ObjectClass) then originalResultType
961961
else AvoidWildcardsMap()(proto.resultType.deepenProtoTrans) match
962-
case SelectionProto(nme.asInstanceOf_, PolyProto(_, resTp), _, _) => resTp
962+
case SelectionProto(nme.asInstanceOf_, PolyProto(_, resTp), _, _, _) => resTp
963963
case resTp if isFullyDefined(resTp, ForceDegree.all) => resTp
964964
case _ => defn.ObjectType
965965
val methType = MethodType(proto.typedArgs().map(_.tpe.widen), resultType)

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._
88
import Implicits._, Flags._, Constants.Constant
99
import Trees._
1010
import NameOps._
11+
import util.Spans.NoSpan
1112
import util.SrcPos
1213
import config.Feature
1314
import reporting._
@@ -266,7 +267,7 @@ object ErrorReporting {
266267
else
267268
val add = suggestImports(
268269
ViewProto(qualType.widen,
269-
SelectionProto(tree.name, WildcardType, NoViewsAllowed, privateOK = false)))
270+
SelectionProto(tree.name, WildcardType, NoViewsAllowed, privateOK = false, NoSpan)))
270271
if add.isEmpty then ""
271272
else ", but could be made available as an extension method." ++ add
272273
end selectErrorAddendum

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ object Implicits:
7575
* method with the selecting name? False otherwise.
7676
*/
7777
def hasExtMethod(tp: Type, expected: Type)(using Context) = expected match
78-
case selProto @ SelectionProto(selName: TermName, _, _, _) =>
78+
case selProto @ SelectionProto(selName: TermName, _, _, _, _) =>
7979
tp.memberBasedOnFlags(selName, required = ExtensionMethod).exists
8080
case _ =>
8181
false
@@ -437,7 +437,7 @@ object Implicits:
437437
def clarify(tp: Type)(using Context): Type = tp
438438

439439
final protected def qualify(using Context): String = expectedType match {
440-
case SelectionProto(name, mproto, _, _) if !argument.isEmpty =>
440+
case SelectionProto(name, mproto, _, _, _) if !argument.isEmpty =>
441441
i"provide an extension method `$name` on ${argument.tpe}"
442442
case NoType =>
443443
if (argument.isEmpty) i"match expected type"
@@ -842,8 +842,8 @@ trait Implicits:
842842
NoMatchingImplicitsFailure
843843
else {
844844
def adjust(to: Type) = to.stripTypeVar.widenExpr match {
845-
case SelectionProto(name, memberProto, compat, true) =>
846-
SelectionProto(name, memberProto, compat, privateOK = false)
845+
case SelectionProto(name, memberProto, compat, true, nameSpan) =>
846+
SelectionProto(name, memberProto, compat, privateOK = false, nameSpan)
847847
case tp => tp
848848
}
849849

@@ -1137,10 +1137,10 @@ trait Implicits:
11371137
pt, locked)
11381138
}
11391139
pt match
1140-
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
1140+
case selProto @ SelectionProto(selName: TermName, mbrType, _, _, nameSpan) =>
11411141

11421142
def tryExtension(using Context) =
1143-
extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType)
1143+
extMethodApply(untpd.Select(untpdGenerated, selName).withSpan(nameSpan), argument, mbrType)
11441144

11451145
def tryConversionForSelection(using Context) =
11461146
val converted = tryConversion

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ trait ImportSuggestions:
237237
// don't suggest things that are imported by default
238238

239239
def extensionImports = pt match
240-
case ViewProto(argType, SelectionProto(name: TermName, _, _, _)) =>
240+
case ViewProto(argType, SelectionProto(name: TermName, _, _, _, _)) =>
241241
roots.flatMap(extensionMethod(_, name, argType))
242242
case _ =>
243243
Nil

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

+20-19
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import util.SourceFile
1919
import TypeComparer.necessarySubType
2020

2121
import scala.annotation.internal.sharable
22+
import dotty.tools.dotc.util.Spans.{NoSpan, Span}
2223

2324
object ProtoTypes {
2425

@@ -165,7 +166,7 @@ object ProtoTypes {
165166
*
166167
* [ ].name: proto
167168
*/
168-
abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)
169+
abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)
169170
extends CachedProxyType with ProtoType with ValueTypeOrProto {
170171

171172
/** Is the set of members of this type unknown, in the sense that we
@@ -230,24 +231,24 @@ object ProtoTypes {
230231

231232
def underlying(using Context): Type = WildcardType
232233

233-
def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(using Context): SelectionProto =
234-
if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this
235-
else SelectionProto(name, memberProto, compat, privateOK)
234+
def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, nameSpan: Span)(using Context): SelectionProto =
235+
if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat) && (nameSpan == this.nameSpan)) this
236+
else SelectionProto(name, memberProto, compat, privateOK, nameSpan)
236237

237238
override def isErroneous(using Context): Boolean =
238239
memberProto.isErroneous
239240

240241
override def unusableForInference(using Context): Boolean =
241242
memberProto.unusableForInference
242243

243-
def map(tm: TypeMap)(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat)
244+
def map(tm: TypeMap)(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat, nameSpan)
244245
def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T = ta(x, memberProto)
245246

246247
override def deepenProto(using Context): SelectionProto =
247-
derivedSelectionProto(name, memberProto.deepenProto, compat)
248+
derivedSelectionProto(name, memberProto.deepenProto, compat, nameSpan)
248249

249250
override def deepenProtoTrans(using Context): SelectionProto =
250-
derivedSelectionProto(name, memberProto.deepenProtoTrans, compat)
251+
derivedSelectionProto(name, memberProto.deepenProtoTrans, compat, nameSpan)
251252

252253
override def computeHash(bs: Hashable.Binders): Int = {
253254
val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0)
@@ -268,24 +269,24 @@ object ProtoTypes {
268269
}
269270
}
270271

271-
class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)
272-
extends SelectionProto(name, memberProto, compat, privateOK)
272+
class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)
273+
extends SelectionProto(name, memberProto, compat, privateOK, nameSpan)
273274

274275
object SelectionProto {
275-
def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)(using Context): SelectionProto = {
276-
val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK)
276+
def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)(using Context): SelectionProto = {
277+
val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK, nameSpan)
277278
if (compat eq NoViewsAllowed) unique(selproto) else selproto
278279
}
279280
}
280281

281282
/** Create a selection proto-type, but only one level deep;
282283
* treat constructors specially
283284
*/
284-
def shallowSelectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
285+
def shallowSelectionProto(name: Name, tp: Type, typer: Typer, nameSpan: Span)(using Context): TermType =
285286
if (name.isConstructorName) WildcardType
286287
else tp match
287-
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
288-
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true)
288+
case tp: UnapplyFunProto => new UnapplySelectionProto(name, nameSpan)
289+
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true, nameSpan)
289290

290291
/** A prototype for expressions [] that are in some unspecified selection operation
291292
*
@@ -295,12 +296,12 @@ object ProtoTypes {
295296
* operation is further selection. In this case, the expression need not be a value.
296297
* @see checkValue
297298
*/
298-
@sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true)
299+
@sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
299300

300-
@sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true)
301+
@sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
301302

302303
/** A prototype for selections in pattern constructors */
303-
class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed, true)
304+
class UnapplySelectionProto(name: Name, nameSpan: Span) extends SelectionProto(name, WildcardType, NoViewsAllowed, true, nameSpan)
304305

305306
trait ApplyingProto extends ProtoType // common trait of ViewProto and FunProto
306307
trait FunOrPolyProto extends ProtoType: // common trait of PolyProto and FunProto
@@ -599,7 +600,7 @@ object ProtoTypes {
599600
def isMatchedBy(tp: Type, keepConstraint: Boolean)(using Context): Boolean =
600601
ctx.typer.isApplicableType(tp, argType :: Nil, resultType) || {
601602
resType match {
602-
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
603+
case selProto @ SelectionProto(selName: TermName, mbrType, _, _, _) =>
603604
ctx.typer.hasExtensionMethodNamed(tp, selName, argType, mbrType)
604605
//.reporting(i"has ext $tp $name $argType $mbrType: $result")
605606
case _ =>
@@ -921,7 +922,7 @@ object ProtoTypes {
921922
}
922923
approxOr
923924
case tp: SelectionProto =>
924-
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed)
925+
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed, tp.nameSpan)
925926
case tp: ViewProto =>
926927
tp.derivedViewProto(
927928
wildApprox(tp.argType, theMap, seen, internal),

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
752752
record("typedSelect")
753753

754754
def typeSelectOnTerm(using Context): Tree =
755-
val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
755+
val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
756756
typedSelect(tree, pt, qual).withSpan(tree.span).computeNullable()
757757

758758
def javaSelectOnType(qual: Tree)(using Context) =
@@ -782,7 +782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
782782
tryAlternatively(typeSelectOnTerm)(fallBack)
783783

784784
if (tree.qualifier.isType) {
785-
val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
785+
val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
786786
assignType(cpy.Select(tree)(qual1, tree.name), qual1)
787787
}
788788
else if (ctx.isJava && tree.name.isTypeName)
@@ -3442,7 +3442,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
34423442
then
34433443
Some(adapt(tree, pt, locked))
34443444
else
3445-
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false)
3445+
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false, tree.nameSpan)
34463446
if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then
34473447
None
34483448
else
@@ -3467,7 +3467,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
34673467
(tree: untpd.Select, pt: Type, mbrProto: Type, qual: Tree, locked: TypeVars, compat: Compatibility, inSelect: Boolean)
34683468
(using Context): Tree =
34693469

3470-
def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect)
3470+
def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect, tree.nameSpan)
34713471

34723472
def tryExtension(using Context): Tree =
34733473
val altImports = new mutable.ListBuffer[TermRef]()
@@ -3897,7 +3897,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38973897
* function prototype `(...)R`. Otherwise `pt`.
38983898
*/
38993899
def ptWithoutRedundantApply: Type = pt.revealIgnored match
3900-
case SelectionProto(nme.apply, mpt, _, _) =>
3900+
case SelectionProto(nme.apply, mpt, _, _, _) =>
39013901
mpt.revealIgnored match
39023902
case fpt: FunProto => fpt
39033903
case _ => pt

presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala

+10
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,13 @@ class PcDefinitionSuite extends BasePcDefinitionSuite:
401401
|
402402
|""".stripMargin
403403
)
404+
405+
@Test def `implicit-extension` =
406+
check(
407+
"""|class MyIntOut(val value: Int)
408+
|object MyIntOut:
409+
| extension (i: MyIntOut) def <<uneven>> = i.value % 2 == 1
410+
|
411+
|val a = MyIntOut(1).un@@even
412+
|""".stripMargin,
413+
)

presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala

+31
Original file line numberDiff line numberDiff line change
@@ -1143,3 +1143,34 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite:
11431143
| case MySome[<<AA>>](value: <<A@@A>>) extends MyOption[Int]
11441144
|""".stripMargin,
11451145
)
1146+
1147+
@Test def `implicit-extension` =
1148+
check(
1149+
"""|class MyIntOut(val value: Int)
1150+
|object MyIntOut:
1151+
| extension (i: MyIntOut) def <<uneven>> = i.value % 2 == 1
1152+
|
1153+
|val a = MyIntOut(1)
1154+
|val m = a.<<un@@even>>
1155+
|""".stripMargin,
1156+
)
1157+
1158+
@Test def `implicit-extension-2` =
1159+
check(
1160+
"""|class MyIntOut(val value: Int)
1161+
|object MyIntOut:
1162+
| extension (i: MyIntOut) def <<uneven>>(u: Int) = i.value % 2 == 1
1163+
|
1164+
|val a = MyIntOut(1).<<un@@even>>(3)
1165+
|""".stripMargin,
1166+
)
1167+
1168+
@Test def `implicit-extension-infix` =
1169+
check(
1170+
"""|class MyIntOut(val value: Int)
1171+
|object MyIntOut:
1172+
| extension (i: MyIntOut) def <<++>>(u: Int) = i.value + u
1173+
|
1174+
|val a = MyIntOut(1) <<+@@+>> 3
1175+
|""".stripMargin,
1176+
)

presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala

+12
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,15 @@ class HoverTypeSuite extends BaseHoverSuite:
355355
"""|val ddd: Int
356356
|""".stripMargin.hover,
357357
)
358+
359+
@Test def `infix-extension` =
360+
check(
361+
"""|class MyIntOut(val value: Int)
362+
|object MyIntOut:
363+
| extension (i: MyIntOut) def uneven = i.value % 2 == 1
364+
|
365+
|val a = MyIntOut(1).un@@even
366+
|""".stripMargin,
367+
"""|extension (i: MyIntOut) def uneven: Boolean
368+
|""".stripMargin.hover,
369+
)

tests/semanticdb/expect/Extension.expect.scala

+9
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ extension (s/*<-ext::Extension$package.readInto().(s)*/: String/*->scala::Predef
1616

1717
trait Functor/*<-ext::Functor#*/[F/*<-ext::Functor#[F]*/[_]]:
1818
extension [T/*<-ext::Functor#map().[T]*/](t/*<-ext::Functor#map().(t)*/: F/*->ext::Functor#[F]*/[T/*->ext::Functor#map().[T]*/]) def map/*<-ext::Functor#map().*/[U/*<-ext::Functor#map().[U]*/](f/*<-ext::Functor#map().(f)*/: T/*->ext::Functor#map().[T]*/ => U/*->ext::Functor#map().[U]*/): F/*->ext::Functor#[F]*/[U/*->ext::Functor#map().[U]*/]
19+
20+
opaque type Deck/*<-ext::Extension$package.Deck#*/ = Long/*->scala::Long#*/
21+
object Deck/*<-ext::Extension$package.Deck.*/:
22+
extension (data/*<-ext::Extension$package.Deck.fooSize().(data)*/: Deck/*->ext::Extension$package.Deck#*/)
23+
def fooSize/*<-ext::Extension$package.Deck.fooSize().*/: Int/*->scala::Int#*/ = ???/*->scala::Predef.`???`().*/
24+
25+
object DeckUsage/*<-ext::DeckUsage.*/:
26+
val deck/*<-ext::DeckUsage.deck.*/: Deck/*->ext::Extension$package.Deck#*/ = ???/*->scala::Predef.`???`().*/
27+
deck/*->ext::DeckUsage.deck.*/.fooSize/*->ext::Extension$package.Deck.fooSize().*/

tests/semanticdb/expect/Extension.scala

+9
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ extension (s: String)
1616

1717
trait Functor[F[_]]:
1818
extension [T](t: F[T]) def map[U](f: T => U): F[U]
19+
20+
opaque type Deck = Long
21+
object Deck:
22+
extension (data: Deck)
23+
def fooSize: Int = ???
24+
25+
object DeckUsage:
26+
val deck: Deck = ???
27+
deck.fooSize

tests/semanticdb/metac.expect

+23-3
Original file line numberDiff line numberDiff line change
@@ -1450,12 +1450,18 @@ Schema => SemanticDB v4
14501450
Uri => Extension.scala
14511451
Text => empty
14521452
Language => Scala
1453-
Symbols => 26 entries
1454-
Occurrences => 52 entries
1453+
Symbols => 32 entries
1454+
Occurrences => 66 entries
14551455
Synthetics => 1 entries
14561456

14571457
Symbols:
1458-
ext/Extension$package. => final package object ext extends Object { self: ext.type => +6 decls }
1458+
ext/DeckUsage. => final object DeckUsage extends Object { self: DeckUsage.type => +2 decls }
1459+
ext/DeckUsage.deck. => val method deck Deck
1460+
ext/Extension$package. => final package object ext extends Object { self: ext.type { opaque type Deck } => +9 decls }
1461+
ext/Extension$package.Deck# => opaque type Deck
1462+
ext/Extension$package.Deck. => final object Deck extends Object { self: Deck.type => +2 decls }
1463+
ext/Extension$package.Deck.fooSize(). => method fooSize (param data: Deck): Int
1464+
ext/Extension$package.Deck.fooSize().(data) => param data: Deck
14591465
ext/Extension$package.`#*#`(). => method #*# (param s: String)(param i: Int): Tuple2[String, Int]
14601466
ext/Extension$package.`#*#`().(i) => param i: Int
14611467
ext/Extension$package.`#*#`().(s) => param s: String
@@ -1535,6 +1541,20 @@ Occurrences:
15351541
[17:44..17:45): U -> ext/Functor#map().[U]
15361542
[17:48..17:49): F -> ext/Functor#[F]
15371543
[17:50..17:51): U -> ext/Functor#map().[U]
1544+
[19:12..19:16): Deck <- ext/Extension$package.Deck#
1545+
[19:19..19:23): Long -> scala/Long#
1546+
[20:7..20:11): Deck <- ext/Extension$package.Deck.
1547+
[21:13..21:17): data <- ext/Extension$package.Deck.fooSize().(data)
1548+
[21:19..21:23): Deck -> ext/Extension$package.Deck#
1549+
[22:8..22:15): fooSize <- ext/Extension$package.Deck.fooSize().
1550+
[22:17..22:20): Int -> scala/Int#
1551+
[22:23..22:26): ??? -> scala/Predef.`???`().
1552+
[24:7..24:16): DeckUsage <- ext/DeckUsage.
1553+
[25:6..25:10): deck <- ext/DeckUsage.deck.
1554+
[25:12..25:16): Deck -> ext/Extension$package.Deck#
1555+
[25:19..25:22): ??? -> scala/Predef.`???`().
1556+
[26:2..26:6): deck -> ext/DeckUsage.deck.
1557+
[26:7..26:14): fooSize -> ext/Extension$package.Deck.fooSize().
15381558

15391559
Synthetics:
15401560
[14:46..14:61):summon[Read[T]] => *(x$2)

0 commit comments

Comments
 (0)