Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
梦境迷离
Scala Macro Tools
提交
0094d43e
S
Scala Macro Tools
项目概览
梦境迷离
/
Scala Macro Tools
上一次同步 大约 1 年
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
Scala Macro Tools
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
0094d43e
编写于
6月 22, 2021
作者:
梦境迷离
提交者:
GitHub
6月 22, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
`@tostring` support callSuper (#13)
* support callSuper * rename option
上级
18151b7e
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
98 addition
and
36 deletion
+98
-36
README.md
README.md
+7
-6
examples212/src/main/scala/io/github/dreamylost/Main.scala
examples212/src/main/scala/io/github/dreamylost/Main.scala
+1
-1
examples213/src/main/scala/io/github/dreamylost/Main.scala
examples213/src/main/scala/io/github/dreamylost/Main.scala
+1
-1
src/main/scala/io/github/dreamylost/toString.scala
src/main/scala/io/github/dreamylost/toString.scala
+48
-22
src/test/scala/io/github/dreamylost/ToStringTest.scala
src/test/scala/io/github/dreamylost/ToStringTest.scala
+41
-6
未找到文件。
README.md
浏览文件 @
0094d43e
...
...
@@ -12,10 +12,11 @@ scala macro and abstract syntax tree learning code.
The
`@toString`
used to generate
`toString`
for Scala classes or a
`toString`
with parameter names for the case classes.
-
Note
-
`verbose`
Whether to enable detailed log.
-
`withFieldName`
Whether to include the names of the field in the
`toString`
.
-
`withInternalField`
Whether to include the fields defined within a class.
-
Support
`case class`
and
`class`
.
-
`verbose`
Whether to enable detailed log.
-
`includeFieldNames`
Whether to include the names of the field in the
`toString`
.
-
`includeInternalFields`
Whether to include the fields defined within a class. Not in a primary constructor.
-
`callSuper`
Whether to include the super's
`toString`
. Not support if super class is a trait.
-
Support
`case class`
and
`class`
.
-
Example
...
...
@@ -29,7 +30,7 @@ class TestClass(val i: Int = 0, var j: Int) {
println
(
new
TestClass
(
1
,
2
));
```
|
withInternalField / withFieldName
| false |true
|
includeInternalFields / includeFieldNames
| false |true
| --------------------------------- | ---------------------------------- |----------------------------------|
|false|
```TestClass(1, 2)```
|
```TestClass(i=0, j=2)```
|
|true|
```TestClass(1, 2, 0, hello, world)```
|
```TestClass(i=1, j=2, y=0, z=hello, x=world)```
|
...
...
@@ -67,7 +68,7 @@ The `@builder` used to generate builder pattern for Scala classes.
-
Note
-
Support
`case class`
/
`class`
.
-
It can be used with
`@toString`
. But it needs to be put in the back.
-
If there is no companion object, one will be generated to store the
builder method and
class.
-
If there is no companion object, one will be generated to store the
`builder`
method and
`Builder`
class.
-
IDE support is not very good, a red prompt will appear, but the compilation is OK.
-
Example
...
...
examples212/src/main/scala/io/github/dreamylost/Main.scala
浏览文件 @
0094d43e
...
...
@@ -8,7 +8,7 @@ package io.github.dreamylost
*/
object
Main
extends
App
{
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
@toString
(
includeInternalFields
=
true
,
includeFieldNames
=
true
)
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
var
z
:
String
=
"hello"
...
...
examples213/src/main/scala/io/github/dreamylost/Main.scala
浏览文件 @
0094d43e
...
...
@@ -8,7 +8,7 @@ package io.github.dreamylost
*/
object
Main
extends
App
{
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
@toString
(
includeInternalFields
=
true
,
includeFieldNames
=
true
)
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
var
z
:
String
=
"hello"
...
...
src/main/scala/io/github/dreamylost/toString.scala
浏览文件 @
0094d43e
...
...
@@ -8,31 +8,33 @@ import scala.reflect.macros.whitebox
* toString for classes
*
* @author 梦境迷离
* @param verbose Whether to enable detailed log.
* @param withInternalField Whether to include the fields defined within a class.
* @param withFieldName Whether to include the name of the field in the toString.
* @param verbose Whether to enable detailed log.
* @param includeInternalFields Whether to include the fields defined within a class.
* @param includeFieldNames Whether to include the name of the field in the toString.
* @param callSuper Whether to include the super's toString.
* @since 2021/6/13
* @version 1.0
*/
@compileTimeOnly
(
"enable macro to expand macro annotations"
)
final
class
toString
(
verbose
:
Boolean
=
false
,
withInternalField
:
Boolean
=
true
,
withFieldName
:
Boolean
=
true
verbose
:
Boolean
=
false
,
includeInternalFields
:
Boolean
=
true
,
includeFieldNames
:
Boolean
=
true
,
callSuper
:
Boolean
=
false
)
extends
StaticAnnotation
{
def
macroTransform
(
annottees
:
Any*
)
:
Any
=
macro
stringMacro
.
impl
}
case
class
Argument
(
verbose
:
Boolean
,
withInternalField
:
Boolean
,
withFieldName
:
Boolean
)
final
case
class
Argument
(
verbose
:
Boolean
,
includeInternalFields
:
Boolean
,
includeFieldNames
:
Boolean
,
callSuper
:
Boolean
)
object
stringMacro
{
def
printField
(
c
:
whitebox.Context
)(
argument
:
Argument
,
lastParam
:
Option
[
String
],
field
:
c.universe.Tree
)
:
c.universe.Tree
=
{
import
c.universe._
// Print one field as <name of the field>+"="+fieldName
if
(
argument
.
withFieldName
)
{
if
(
argument
.
includeFieldNames
)
{
lastParam
.
fold
(
q
"$field"
)
{
lp
=>
field
match
{
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
...
...
@@ -57,17 +59,18 @@ object stringMacro {
private
def
toStringTemplateImpl
(
c
:
whitebox.Context
)(
argument
:
Argument
,
annotateeClass
:
c.universe.ClassDef
)
:
c.universe.Tree
=
{
import
c.universe._
// For a given class definition, separate the components of the class
val
(
className
,
annotteeClassParams
,
annotteeClassDefinitions
)
=
{
val
(
className
,
annotteeClassParams
,
superClasses
,
annotteeClassDefinitions
)
=
{
annotateeClass
match
{
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
=>
(
tpname
,
paramss
,
stats
)
c
.
info
(
c
.
enclosingPosition
,
s
"parents: $parents"
,
force
=
argument
.
verbose
)
(
tpname
,
paramss
,
parents
,
stats
)
}
}
// Check the type of the class, whether it already contains its own toString
val
annotteeClassFieldDefinitions
=
annotteeClassDefinitions
.
asInstanceOf
[
List
[
Tree
]].
filter
(
p
=>
p
match
{
case
_:
ValDef
=>
true
case
mem
:
MemberDef
=>
c
.
info
(
c
.
enclosingPosition
,
s
"MemberDef: ${mem.toString}"
,
tru
e
)
c
.
info
(
c
.
enclosingPosition
,
s
"MemberDef: ${mem.toString}"
,
force
=
argument
.
verbos
e
)
if
(
mem
.
toString
().
startsWith
(
"override def toString"
))
{
c
.
abort
(
mem
.
pos
,
"'toString' method has already defined, please remove it or not use'@toString'"
)
}
...
...
@@ -80,9 +83,9 @@ object stringMacro {
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
tree
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
tree
}
c
.
info
(
c
.
enclosingPosition
,
s
"className: $className, ctorParams: ${ctorParams.toString()}
"
,
force
=
tru
e
)
c
.
info
(
c
.
enclosingPosition
,
s
"className: $className, fields: ${annotteeClassFieldDefinitions.toString()}"
,
force
=
tru
e
)
val
member
=
if
(
argument
.
withInternalField
)
ctorParams
++
annotteeClassFieldDefinitions
else
ctorParams
c
.
info
(
c
.
enclosingPosition
,
s
"className: $className, ctorParams: ${ctorParams.toString()}
, superClasses: $superClasses"
,
force
=
argument
.
verbos
e
)
c
.
info
(
c
.
enclosingPosition
,
s
"className: $className, fields: ${annotteeClassFieldDefinitions.toString()}"
,
force
=
argument
.
verbos
e
)
val
member
=
if
(
argument
.
includeInternalFields
)
ctorParams
++
annotteeClassFieldDefinitions
else
ctorParams
val
lastParam
=
member
.
lastOption
.
map
{
case
v
:
ValDef
=>
v
.
name
.
toTermName
.
decodedName
.
toString
...
...
@@ -90,20 +93,43 @@ object stringMacro {
}
val
paramsWithName
=
member
.
foldLeft
(
q
"${""}"
)((
res
,
acc
)
=>
q
"$res + ${printField(c)(argument, lastParam, acc)}"
)
//scala/bug https://github.com/scala/bug/issues/3967 not be 'Foo(i=1,j=2)' in standard library
q
"""override def toString: String = ${className.toString()} + ${"("} + $paramsWithName + ${")"}"""
val
toString
=
q
"""override def toString: String = ${className.toString()} + ${"("} + $paramsWithName + ${")"}"""
// Have super class ?
if
(
argument
.
callSuper
&&
superClasses
.
nonEmpty
)
{
val
superClassDef
=
superClasses
.
head
match
{
case
tree
:
Tree
=>
Some
(
tree
)
// TODO type check better
case
_
=>
None
}
superClassDef
.
fold
(
toString
)(
sc
=>
{
val
superClass
=
q
"${"
super
=
"}"
c
.
info
(
c
.
enclosingPosition
,
s
"member: $member, superClass: $superClass, superClassDef: $superClassDef, paramsWithName: $paramsWithName"
,
force
=
argument
.
verbose
)
q
"override def toString: String = StringContext(${className.toString()} + ${"
(
"} + $superClass, ${if (member.nonEmpty) "
,
" else ""}+$paramsWithName + ${"
)
"}).s(super.toString)"
}
)
}
else
{
toString
}
}
def
impl
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.Expr
[
Any
]
=
{
import
c.universe._
// extract parameters of annotation
// extract parameters of annotation
, must in order
val
arg
=
c
.
prefix
.
tree
match
{
case
q
"new toString($aa, $bb, $cc)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
aa
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)))
case
q
"new toString(withInternalField=$bb, withFieldName=$cc)"
=>
(
false
,
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)))
case
q
"new toString()"
=>
(
false
,
true
,
true
)
case
q
"new toString(includeInternalFields=$bb, includeFieldNames=$cc, callSuper=$dd)"
=>
(
false
,
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
dd
)))
case
q
"new toString($aa, $bb, $cc)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
aa
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)),
false
)
case
q
"new toString(verbose=$aa, includeInternalFields=$bb, includeFieldNames=$cc, callSuper=$dd)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
aa
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
dd
)))
case
q
"new toString(verbose=$aa, includeInternalFields=$bb, includeFieldNames=$cc)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
aa
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)),
false
)
case
q
"new toString($aa, $bb, $cc, $dd)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
aa
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
dd
)))
case
q
"new toString(includeInternalFields=$bb, includeFieldNames=$cc)"
=>
(
false
,
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)),
false
)
case
q
"new toString()"
=>
(
false
,
true
,
true
,
false
)
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"unexpected annotation pattern!"
)
}
c
.
info
(
c
.
enclosingPosition
,
s
"toString annottees: $annottees"
,
true
)
val
argument
=
Argument
(
arg
.
_1
,
arg
.
_2
,
arg
.
_3
)
val
argument
=
Argument
(
arg
.
_1
,
arg
.
_2
,
arg
.
_3
,
arg
.
_4
)
c
.
info
(
c
.
enclosingPosition
,
s
"toString annottees: $annottees"
,
force
=
argument
.
verbose
)
// Check the type of the class, which can only be defined on the ordinary class
val
annotateeClass
:
ClassDef
=
annottees
.
map
(
_
.
tree
).
toList
match
{
case
(
classDecl
:
ClassDef
)
::
Nil
=>
classDecl
...
...
@@ -120,7 +146,7 @@ object stringMacro {
}
}
c
.
info
(
c
.
enclosingPosition
,
s
"impl argument: $argument, isCase: $isCase"
,
force
=
tru
e
)
c
.
info
(
c
.
enclosingPosition
,
s
"impl argument: $argument, isCase: $isCase"
,
force
=
argument
.
verbos
e
)
val
resMethod
=
toStringTemplateImpl
(
c
)(
argument
,
annotateeClass
)
val
resTree
=
annotateeClass
match
{
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
=>
...
...
src/test/scala/io/github/dreamylost/ToStringTest.scala
浏览文件 @
0094d43e
...
...
@@ -204,25 +204,60 @@ class ToStringTest extends FlatSpec with Matchers {
assert
(
s2
==
"TestClass2()"
)
}
"toString15"
should
"
super param not find
"
in
{
"toString15"
should
"
non-contains super toString
"
in
{
@toString
()
class
TestClass1
(
val
i
:
Int
)
@toString
(
withInternalField
=
true
,
withFieldName
=
tru
e
)
case
class
TestClass2
()
extends
TestClass1
(
1
)
@toString
(
verbose
=
true
,
includeInternalFields
=
true
,
includeFieldNames
=
true
,
callSuper
=
fals
e
)
case
class
TestClass2
(
j
:
Int
=
1
)
extends
TestClass1
(
1
)
val
s1
=
TestClass2
().
toString
println
(
s1
)
assert
(
s1
==
"TestClass2(
)"
)
//TODO not support println super fields
assert
(
s1
==
"TestClass2(
j=1)"
)
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
@toString
(
includeInternalFields
=
true
,
includeFieldNames
=
true
)
case
class
TestClass3
(
j
:
Int
)
extends
TestClass1
(
j
)
val
s2
=
TestClass3
(
0
).
toString
println
(
s2
)
assert
(
s2
==
"TestClass3(j=0)"
)
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
@toString
(
includeInternalFields
=
true
,
includeFieldNames
=
true
)
class
TestClass4
(
j
:
Int
)
extends
TestClass1
(
j
)
val
s3
=
new
TestClass4
(
0
).
toString
println
(
s3
)
assert
(
s3
==
"TestClass4(j=0)"
)
}
"toString16"
should
"contains super toString"
in
{
@toString
()
class
TestClass1
(
val
i
:
Int
)
@toString
(
verbose
=
true
,
includeInternalFields
=
true
,
includeFieldNames
=
true
,
callSuper
=
true
)
case
class
TestClass2
(
j
:
Int
=
1
)
extends
TestClass1
(
1
)
val
s1
=
TestClass2
().
toString
println
(
s1
)
assert
(
s1
==
"TestClass2(super=TestClass1(i=1), j=1)"
)
@toString
(
includeInternalFields
=
true
,
includeFieldNames
=
true
,
callSuper
=
true
)
class
TestClass4
()
extends
TestClass1
(
1
)
// StringContext("TestClass5(super=", ")").s(super.toString);
val
s4
=
new
TestClass4
().
toString
println
(
s4
)
assert
(
s4
==
"TestClass4(super=TestClass1(i=1))"
)
@toString
(
includeInternalFields
=
false
,
includeFieldNames
=
true
,
callSuper
=
true
)
class
TestClass4_2
()
extends
TestClass1
(
1
)
val
s4_2
=
new
TestClass4_2
().
toString
println
(
s4_2
)
assert
(
s4_2
==
"TestClass4_2(super=TestClass1(i=1))"
)
trait
A
{
val
i
:
Int
}
@toString
(
includeInternalFields
=
true
,
includeFieldNames
=
false
,
callSuper
=
true
)
class
TestClass5
extends
A
{
override
val
i
=
1
}
val
s5
=
new
TestClass5
().
toString
println
(
s5
)
// Because not support if super class is a trait
assert
(
s5
.
startsWith
(
"TestClass5(super=io.github.dreamylost.ToStringTes"
)
&&
s5
.
endsWith
(
"1)"
))
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录