-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Semi-Fix scaladoc of extensions methods #14321
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
Closed
Closed
Changes from 4 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
5671d26
Add failing test
Sporarum d0f7300
Add new heuristic for extension methods
Sporarum 43a1a26
Refactor existing methods using new heuristic
Sporarum 36b5c8a
Reorder and Rename methods for consistency
Sporarum 43a79d5
Merge useful changes from scaladoc-typeInterweaving-new
Sporarum efd499f
Almost working
Sporarum 322aa13
Works again when no type params
Sporarum 4682644
It works !
Sporarum File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,44 @@ | ||
package tests.extensionParams | ||
|
||
extension [A](thiz: A) | ||
def toTuple2[B](that: B): (A, B) = thiz -> that | ||
|
||
def toTuple2[B](that: B): (A, B) | ||
= thiz -> that | ||
extension [A](a: A)(using Int) | ||
def f[B](b: B): (A, B) = ??? | ||
def f1[B](b: B): (A, B) | ||
= ??? | ||
|
||
extension [A](a: A)(using Int) | ||
def ff(b: A): (A, A) = ??? | ||
def f2(b: A): (A, A) | ||
= ??? | ||
|
||
extension [A](a: A)(using Int) | ||
def fff(using String)(b: A): (A, A) = ??? | ||
def f3(using String)(b: A): (A, A) | ||
= ??? | ||
|
||
extension (a: Char)(using Int) | ||
def ffff(using String)(b: Int): Unit = ??? | ||
def f4(using String)(b: Int): Unit | ||
= ??? | ||
|
||
extension (a: Char)(using Int) | ||
def fffff[B](using String)(b: B): Unit = ??? | ||
def f5[B](using String)(b: B): Unit | ||
= ??? | ||
|
||
extension [A <: List[Char]](a: A)(using Int) | ||
def ffffff[B](b: B): (A, B) = ??? | ||
def f6[B](b: B): (A, B) | ||
= ??? | ||
|
||
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) | ||
def f7[B, C](b: B)(c: C): (A, B) | ||
= ??? | ||
|
||
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) | ||
def f8(b: Any)(c: Any): Any | ||
= ??? | ||
|
||
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) | ||
def f9[B, C](using Int)(b: B)(c: C): (A, B) | ||
= ??? | ||
|
||
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) | ||
def f10(using Int)(b: Any)(c: Any): Any | ||
= ??? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -142,7 +142,15 @@ object SymOps: | |
import reflect._ | ||
sym.flags.is(Flags.Artifact) | ||
|
||
def isLeftAssoc: Boolean = !sym.name.endsWith(":") | ||
/** | ||
* note that this is not the right criterion: | ||
* An extension method is treated as a right-associative operator (as in SLS §6.12.3) | ||
* if it has a name ending in : and is immediately followed by a single parameter. | ||
* https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html | ||
*/ | ||
def isRightAssoc: Boolean = sym.name.endsWith(":") | ||
|
||
def isLeftAssoc: Boolean = !sym.isRightAssoc | ||
|
||
def extendedSymbol: Option[reflect.ValDef] = | ||
import reflect.* | ||
|
@@ -152,52 +160,68 @@ object SymOps: | |
else termParamss(1).params(0) | ||
} | ||
|
||
def extendedTypeParams: List[reflect.TypeDef] = | ||
import reflect.* | ||
val method = sym.tree.asInstanceOf[DefDef] | ||
method.leadingTypeParams | ||
def splitExtensionParamLists: (List[reflect.ParamClause], List[reflect.ParamClause]) = | ||
if sym.isRightAssoc && sym.isExtensionMethod then | ||
val unswapped@(extPart, defPart) = sym.splitExtensionParamListsAssumingLeftAssoc | ||
def nonUsingClauses(clauses: List[reflect.ParamClause]) = clauses.zipWithIndex.collect{case (terms: reflect.TermParamClause, i) if !terms.isGiven => (terms, i)} | ||
val extNonUsingClause = nonUsingClauses(extPart) | ||
val defNonUsingClauses = nonUsingClauses(defPart) | ||
assert(extNonUsingClause.size == 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assert seems to be causing some of the tests to fail, but I don't understand why |
||
|
||
def extendedTermParamLists: List[reflect.TermParamClause] = | ||
import reflect.* | ||
if sym.nonExtensionLeadingTypeParams.nonEmpty then | ||
sym.nonExtensionParamLists.takeWhile { | ||
case _: TypeParamClause => false | ||
case _ => true | ||
}.collect { | ||
case tpc: TermParamClause => tpc | ||
} | ||
if defNonUsingClauses.lift(0).map(_._1.params.size != 1).getOrElse(true) // was not really right associative, see comment of isRightAssoc | ||
then unswapped | ||
else | ||
val (first, i1) = extNonUsingClause(0) | ||
val (second, i2) = defNonUsingClauses(0) // since cond is false, we know lift(0) returned Some(_) | ||
(extPart.updated(i1, second), defPart.updated(i2, first)) | ||
else | ||
List.empty | ||
sym.splitExtensionParamListsAssumingLeftAssoc | ||
|
||
/** | ||
* This uses the assumption that there is the following "pos hierachy": extension paramss < DefDef < extMethod paramss | ||
* /!\ where DefDef is the tree containing the paramss | ||
* It wouldn't really make sense for the Def's position not to be either the "def" or the method name, | ||
* but is not enforced | ||
*/ | ||
def splitExtensionParamListsAssumingLeftAssoc: (List[reflect.ParamClause], List[reflect.ParamClause]) = | ||
val method = sym.tree.asInstanceOf[reflect.DefDef] | ||
val paramss = method.paramss //List[ParamClause[T]] //ParamClause[T] = List[ValDef[T]] | List[TypeDef[T]] | ||
val defCoord = method.symbol.pos.get.start //.span.point | ||
|
||
val res = paramss.span{ | ||
case reflect.TypeParamClause(params) => params.head.symbol.pos.get.start < defCoord //.span.start | ||
case reflect.TermParamClause(params) => | ||
params.headOption | ||
.map(_.symbol.pos.get.start < defCoord) //.span.start | ||
.getOrElse(false) // () is only allowed on the RHS of extensions | ||
} | ||
//println(method.name) | ||
//println(res._1.map(_.params.map(_.show)).mkString("ExtensionPart:\n","\n","\n")) | ||
//println(res._2.map(_.params.map(_.show)).mkString("NonExtensionPart:\n","\n","\n")) | ||
res | ||
|
||
|
||
def extendedParamLists: List[reflect.ParamClause] = | ||
sym.splitExtensionParamLists._1 | ||
|
||
def extendedTermParamLists: List[reflect.TermParamClause] = | ||
sym.extendedParamLists.collect{case terms: reflect.TermParamClause => terms} | ||
|
||
def extendedTypeParamList: List[reflect.TypeDef] = | ||
val typeParamss: List[reflect.TypeParamClause] = sym.extendedParamLists.collect{case types: reflect.TypeParamClause => types} | ||
typeParamss.headOption.map(_.params).getOrElse(List()) // only one type param clause on LHS | ||
|
||
def nonExtensionTermParamLists: List[reflect.TermParamClause] = | ||
import reflect.* | ||
if sym.nonExtensionLeadingTypeParams.nonEmpty then | ||
sym.nonExtensionParamLists.dropWhile { | ||
case _: TypeParamClause => false | ||
case _ => true | ||
}.drop(1).collect { | ||
case tpc: TermParamClause => tpc | ||
} | ||
else | ||
sym.nonExtensionParamLists.collect { | ||
case tpc: TermParamClause => tpc | ||
} | ||
|
||
def nonExtensionParamLists: List[reflect.ParamClause] = | ||
import reflect.* | ||
val method = sym.tree.asInstanceOf[DefDef] | ||
if sym.isExtensionMethod then | ||
val params = method.paramss | ||
val toDrop = if method.leadingTypeParams.nonEmpty then 2 else 1 | ||
if sym.isLeftAssoc || params.size == 1 then params.drop(toDrop) | ||
else params.head :: params.tail.drop(toDrop) | ||
else method.paramss | ||
|
||
def nonExtensionLeadingTypeParams: List[reflect.TypeDef] = | ||
import reflect.* | ||
sym.nonExtensionParamLists.collectFirst { | ||
case TypeParamClause(params) => params | ||
}.toList.flatten | ||
sym.splitExtensionParamLists._2 | ||
|
||
def nonExtensionTermParamLists: List[reflect.TermParamClause] = | ||
sym.nonExtensionParamLists.collect{case terms: reflect.TermParamClause => terms} | ||
|
||
def nonExtensionTypeParamList: List[reflect.TypeDef] = | ||
val typeParamss: List[reflect.TypeParamClause] = sym.nonExtensionParamLists.collect{case types: reflect.TypeParamClause => types} | ||
typeParamss.headOption.map(_.params).getOrElse(List()) // only one type param clause on RHS | ||
|
||
|
||
end extension | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extendedSymbol
should probably also be updated, as it implicitly uses the old heuristic