Skip to content
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

Add missing span to extension method select #18557

Merged
merged 1 commit into from
Sep 20, 2023
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ class Inliner(val call: tpd.Tree)(using Context):

override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
val locked = ctx.typerState.ownedVars
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
val reducedProjection = reducer.reduceProjection(resNoReduce)
if reducedProjection.isType then
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ trait Applications extends Compatibility {
val resultType =
if !originalResultType.isRef(defn.ObjectClass) then originalResultType
else AvoidWildcardsMap()(proto.resultType.deepenProtoTrans) match
case SelectionProto(nme.asInstanceOf_, PolyProto(_, resTp), _, _) => resTp
case SelectionProto(nme.asInstanceOf_, PolyProto(_, resTp), _, _, _) => resTp
case resTp if isFullyDefined(resTp, ForceDegree.all) => resTp
case _ => defn.ObjectType
val methType = MethodType(proto.typedArgs().map(_.tpe.widen), resultType)
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._
import Implicits._, Flags._, Constants.Constant
import Trees._
import NameOps._
import util.Spans.NoSpan
import util.SrcPos
import config.Feature
import reporting._
Expand Down Expand Up @@ -275,7 +276,7 @@ object ErrorReporting {
else
val add = suggestImports(
ViewProto(qualType.widen,
SelectionProto(tree.name, WildcardType, NoViewsAllowed, privateOK = false)))
SelectionProto(tree.name, WildcardType, NoViewsAllowed, privateOK = false, NoSpan)))
dwijnand marked this conversation as resolved.
Show resolved Hide resolved
if add.isEmpty then ""
else ", but could be made available as an extension method." ++ add
end selectErrorAddendum
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ object Implicits:
* method with the selecting name? False otherwise.
*/
def hasExtMethod(tp: Type, expected: Type)(using Context) = expected match
case selProto @ SelectionProto(selName: TermName, _, _, _) =>
case selProto @ SelectionProto(selName: TermName, _, _, _, _) =>
tp.memberBasedOnFlags(selName, required = ExtensionMethod).exists
case _ =>
false
Expand Down Expand Up @@ -454,7 +454,7 @@ object Implicits:
def clarify(tp: Type)(using Context): Type = tp

final protected def qualify(using Context): String = expectedType match {
case SelectionProto(name, mproto, _, _) if !argument.isEmpty =>
case SelectionProto(name, mproto, _, _, _) if !argument.isEmpty =>
i"provide an extension method `$name` on ${argument.tpe}"
case NoType =>
if (argument.isEmpty) i"match expected type"
Expand Down Expand Up @@ -866,8 +866,8 @@ trait Implicits:
NoMatchingImplicitsFailure
else {
def adjust(to: Type) = to.stripTypeVar.widenExpr match {
case SelectionProto(name, memberProto, compat, true) =>
SelectionProto(name, memberProto, compat, privateOK = false)
case SelectionProto(name, memberProto, compat, true, nameSpan) =>
SelectionProto(name, memberProto, compat, privateOK = false, nameSpan)
case tp => tp
}

Expand Down Expand Up @@ -1161,10 +1161,10 @@ trait Implicits:
pt, locked)
}
pt match
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
case selProto @ SelectionProto(selName: TermName, mbrType, _, _, nameSpan) =>

def tryExtension(using Context) =
extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType)
extMethodApply(untpd.Select(untpdGenerated, selName).withSpan(nameSpan), argument, mbrType)

def tryConversionForSelection(using Context) =
val converted = tryConversion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ trait ImportSuggestions:
// don't suggest things that are imported by default

def extensionImports = pt match
case ViewProto(argType, SelectionProto(name: TermName, _, _, _)) =>
case ViewProto(argType, SelectionProto(name: TermName, _, _, _, _)) =>
roots.flatMap(extensionMethod(_, name, argType))
case _ =>
Nil
Expand Down
39 changes: 20 additions & 19 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import dotty.tools.dotc.core.Flags.Transparent
import dotty.tools.dotc.config.{ Feature, SourceVersion }

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

object ProtoTypes {

Expand Down Expand Up @@ -180,7 +181,7 @@ object ProtoTypes {
*
* [ ].name: proto
*/
abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)
abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)
extends CachedProxyType with ProtoType with ValueTypeOrProto {

/** Is the set of members of this type unknown, in the sense that we
Expand Down Expand Up @@ -243,24 +244,24 @@ object ProtoTypes {

def underlying(using Context): Type = WildcardType

def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(using Context): SelectionProto =
if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this
else SelectionProto(name, memberProto, compat, privateOK)
def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, nameSpan: Span)(using Context): SelectionProto =
if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat) && (nameSpan == this.nameSpan)) this
else SelectionProto(name, memberProto, compat, privateOK, nameSpan)

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

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

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

override def deepenProto(using Context): SelectionProto =
derivedSelectionProto(name, memberProto.deepenProto, compat)
derivedSelectionProto(name, memberProto.deepenProto, compat, nameSpan)

override def deepenProtoTrans(using Context): SelectionProto =
derivedSelectionProto(name, memberProto.deepenProtoTrans, compat)
derivedSelectionProto(name, memberProto.deepenProtoTrans, compat, nameSpan)

override def computeHash(bs: Hashable.Binders): Int = {
val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0)
Expand All @@ -281,24 +282,24 @@ object ProtoTypes {
}
}

class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)
extends SelectionProto(name, memberProto, compat, privateOK)
class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)
extends SelectionProto(name, memberProto, compat, privateOK, nameSpan)

object SelectionProto {
def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)(using Context): SelectionProto = {
val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK)
def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)(using Context): SelectionProto = {
val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK, nameSpan)
if (compat eq NoViewsAllowed) unique(selproto) else selproto
}
}

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

/** A prototype for expressions [] that are in some unspecified selection operation
*
Expand All @@ -308,12 +309,12 @@ object ProtoTypes {
* operation is further selection. In this case, the expression need not be a value.
* @see checkValue
*/
@sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true)
@sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)

@sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true)
@sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)

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

trait ApplyingProto extends ProtoType // common trait of ViewProto and FunProto
trait FunOrPolyProto extends ProtoType: // common trait of PolyProto and FunProto
Expand Down Expand Up @@ -612,7 +613,7 @@ object ProtoTypes {
def isMatchedBy(tp: Type, keepConstraint: Boolean)(using Context): Boolean =
ctx.typer.isApplicableType(tp, argType :: Nil, resultType) || {
resType match {
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
case selProto @ SelectionProto(selName: TermName, mbrType, _, _, _) =>
ctx.typer.hasExtensionMethodNamed(tp, selName, argType, mbrType)
//.reporting(i"has ext $tp $name $argType $mbrType: $result")
case _ =>
Expand Down Expand Up @@ -934,7 +935,7 @@ object ProtoTypes {
}
approxOr
case tp: SelectionProto =>
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed)
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed, tp.nameSpan)
case tp: ViewProto =>
tp.derivedViewProto(
wildApprox(tp.argType, theMap, seen, internal),
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
record("typedSelect")

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

def javaSelectOnType(qual: Tree)(using Context) =
Expand Down Expand Up @@ -790,7 +790,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
tryAlternatively(typeSelectOnTerm)(fallBack)

if (tree.qualifier.isType) {
val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
assignType(cpy.Select(tree)(qual1, tree.name), qual1)
}
else if (ctx.isJava && tree.name.isTypeName)
Expand Down Expand Up @@ -3513,7 +3513,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
then
Some(adapt(tree, pt, locked))
else
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false)
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false, tree.nameSpan)
if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then
None
else
Expand All @@ -3538,7 +3538,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
(tree: untpd.Select, pt: Type, mbrProto: Type, qual: Tree, locked: TypeVars, compat: Compatibility, inSelect: Boolean)
(using Context): Tree =

def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect)
def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect, tree.nameSpan)

def tryExtension(using Context): Tree =
val altImports = new mutable.ListBuffer[TermRef]()
Expand Down Expand Up @@ -3968,7 +3968,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
* function prototype `(...)R`. Otherwise `pt`.
*/
def ptWithoutRedundantApply: Type = pt.revealIgnored match
case SelectionProto(nme.apply, mpt, _, _) =>
case SelectionProto(nme.apply, mpt, _, _, _) =>
mpt.revealIgnored match
case fpt: FunProto => fpt
case _ => pt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,13 @@ class PcDefinitionSuite extends BasePcDefinitionSuite:
|
|""".stripMargin
)

@Test def `implicit-extension` =
check(
"""|class MyIntOut(val value: Int)
|object MyIntOut:
| extension (i: MyIntOut) def <<uneven>> = i.value % 2 == 1
|
|val a = MyIntOut(1).un@@even
|""".stripMargin,
)
Original file line number Diff line number Diff line change
Expand Up @@ -1143,3 +1143,34 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite:
| case MySome[<<AA>>](value: <<A@@A>>) extends MyOption[Int]
|""".stripMargin,
)

@Test def `implicit-extension` =
check(
"""|class MyIntOut(val value: Int)
|object MyIntOut:
| extension (i: MyIntOut) def <<uneven>> = i.value % 2 == 1
|
|val a = MyIntOut(1)
|val m = a.<<un@@even>>
|""".stripMargin,
)

@Test def `implicit-extension-2` =
check(
"""|class MyIntOut(val value: Int)
|object MyIntOut:
| extension (i: MyIntOut) def <<uneven>>(u: Int) = i.value % 2 == 1
|
|val a = MyIntOut(1).<<un@@even>>(3)
|""".stripMargin,
)

@Test def `implicit-extension-infix` =
check(
"""|class MyIntOut(val value: Int)
|object MyIntOut:
| extension (i: MyIntOut) def <<++>>(u: Int) = i.value + u
|
|val a = MyIntOut(1) <<+@@+>> 3
|""".stripMargin,
)
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,15 @@ class HoverTypeSuite extends BaseHoverSuite:
"""|val ddd: Int
|""".stripMargin.hover,
)

@Test def `infix-extension` =
check(
"""|class MyIntOut(val value: Int)
|object MyIntOut:
| extension (i: MyIntOut) def uneven = i.value % 2 == 1
|
|val a = MyIntOut(1).un@@even
|""".stripMargin,
"""|extension (i: MyIntOut) def uneven: Boolean
|""".stripMargin.hover,
)
9 changes: 9 additions & 0 deletions tests/semanticdb/expect/Extension.expect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ extension (s/*<-ext::Extension$package.readInto().(s)*/: String/*->scala::Predef

trait Functor/*<-ext::Functor#*/[F/*<-ext::Functor#[F]*/[_]]:
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]*/]

opaque type Deck/*<-ext::Extension$package.Deck#*/ = Long/*->scala::Long#*/
object Deck/*<-ext::Extension$package.Deck.*/:
extension (data/*<-ext::Extension$package.Deck.fooSize().(data)*/: Deck/*->ext::Extension$package.Deck#*/)
def fooSize/*<-ext::Extension$package.Deck.fooSize().*/: Int/*->scala::Int#*/ = ???/*->scala::Predef.`???`().*/

object DeckUsage/*<-ext::DeckUsage.*/:
val deck/*<-ext::DeckUsage.deck.*/: Deck/*->ext::Extension$package.Deck#*/ = ???/*->scala::Predef.`???`().*/
deck/*->ext::DeckUsage.deck.*/.fooSize/*->ext::Extension$package.Deck.fooSize().*/
9 changes: 9 additions & 0 deletions tests/semanticdb/expect/Extension.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ extension (s: String)

trait Functor[F[_]]:
extension [T](t: F[T]) def map[U](f: T => U): F[U]

opaque type Deck = Long
object Deck:
extension (data: Deck)
def fooSize: Int = ???

object DeckUsage:
val deck: Deck = ???
deck.fooSize
26 changes: 23 additions & 3 deletions tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -1450,12 +1450,18 @@ Schema => SemanticDB v4
Uri => Extension.scala
Text => empty
Language => Scala
Symbols => 26 entries
Occurrences => 52 entries
Symbols => 32 entries
Occurrences => 66 entries
Synthetics => 1 entries

Symbols:
ext/Extension$package. => final package object ext extends Object { self: ext.type => +6 decls }
ext/DeckUsage. => final object DeckUsage extends Object { self: DeckUsage.type => +2 decls }
ext/DeckUsage.deck. => val method deck Deck
ext/Extension$package. => final package object ext extends Object { self: ext.type { opaque type Deck } => +9 decls }
ext/Extension$package.Deck# => opaque type Deck
ext/Extension$package.Deck. => final object Deck extends Object { self: Deck.type => +2 decls }
ext/Extension$package.Deck.fooSize(). => method fooSize (param data: Deck): Int
ext/Extension$package.Deck.fooSize().(data) => param data: Deck
ext/Extension$package.`#*#`(). => method #*# (param s: String)(param i: Int): Tuple2[String, Int]
ext/Extension$package.`#*#`().(i) => param i: Int
ext/Extension$package.`#*#`().(s) => param s: String
Expand Down Expand Up @@ -1535,6 +1541,20 @@ Occurrences:
[17:44..17:45): U -> ext/Functor#map().[U]
[17:48..17:49): F -> ext/Functor#[F]
[17:50..17:51): U -> ext/Functor#map().[U]
[19:12..19:16): Deck <- ext/Extension$package.Deck#
[19:19..19:23): Long -> scala/Long#
[20:7..20:11): Deck <- ext/Extension$package.Deck.
[21:13..21:17): data <- ext/Extension$package.Deck.fooSize().(data)
[21:19..21:23): Deck -> ext/Extension$package.Deck#
[22:8..22:15): fooSize <- ext/Extension$package.Deck.fooSize().
[22:17..22:20): Int -> scala/Int#
[22:23..22:26): ??? -> scala/Predef.`???`().
[24:7..24:16): DeckUsage <- ext/DeckUsage.
[25:6..25:10): deck <- ext/DeckUsage.deck.
[25:12..25:16): Deck -> ext/Extension$package.Deck#
[25:19..25:22): ??? -> scala/Predef.`???`().
[26:2..26:6): deck -> ext/DeckUsage.deck.
[26:7..26:14): fooSize -> ext/Extension$package.Deck.fooSize().

Synthetics:
[14:46..14:61):summon[Read[T]] => *(x$2)
Expand Down
Loading