未验证 提交 7c2079db 编写于 作者: 梦境迷离's avatar 梦境迷离 提交者: GitHub

add unittest (#84)

* add unittest
上级 55b5b113
......@@ -4,9 +4,12 @@ coverage:
range: "70...100"
status:
project: yes
patch: yes
# changes: no
patch: off
project:
default:
target: auto
threshold: 50%
base: auto
comment:
layout: "header, diff, changes, tree"
......
<img align="right" width="30%" height="30%" src="img.png" alt="https://dreamylost.cn"/>
# scala-macro-tools
# scala-macro-tools
[![Build](https://github.com/jxnu-liguobin/scala-macro-tools/actions/workflows/ScalaCI.yml/badge.svg)](https://github.com/jxnu-liguobin/scala-macro-tools/actions/workflows/ScalaCI.yml)
[![codecov](https://codecov.io/gh/jxnu-liguobin/scala-macro-tools/branch/master/graph/badge.svg?token=IA596YRTOT)](https://codecov.io/gh/jxnu-liguobin/scala-macro-tools)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.jxnu-liguobin%22%20AND%20a:%22scala-macro-tools_2.13%22)
[![Version](https://img.shields.io/jetbrains/plugin/v/17202-scala-macro-tools)](https://plugins.jetbrains.com/plugin/17202-scala-macro-tools)
该库的目的
Motivation
--
学习Scala宏编程(macro)和抽象语法树(ast)。
Learn Scala macro and abstract syntax tree.
> 本项目目前处于实验阶段,有建议、意见或者问题欢迎提issue。如果本项目对你有帮助,欢迎点个star。
> The project is currently experimental
**[中文说明](./README.md) | [English](./README_EN.md)**
[中文说明](./README_CN.md) | [English](./README.md)
# 我能学到什么
# Environment
- Scala2 宏编程
- 了解Scala反射
- 熟悉插值语法的应用,了解Scala Spec
- 了解Scala AST相关的基本使用
- 知道如何编写宏注解(宏拓展)
- 知道如何使用宏生成对象,类,方法,字段
- 其他
- 知道劝退人的SBT如何优雅配置,诸如发布到云仓库,多版本构建,多模块项目
- 了解如何利用Intellij-Scala插件编写自己的Scala插件
- 了解Intellij是怎样支持语法提示的,插件工作流程
- 知道如何写好scalatest单元测试
- 类似的其他思考
# 环境
- It is compiled in Java 8, 11
- It is compiled in Scala 2.11.x ~ 2.13.x
- 使用 Java 8, 11 编译通过
- 使用 Scala 2.11.x ~ 2.13.x 编译通过
# 功能
# Features
- `@toString`
- `@json`
......@@ -47,32 +32,31 @@
- `@constructor`
- `@equalsAndHashCode`
> 涉及到交互操作的注解在IDEA插件中都得到了支持。在插件市场中搜索`Scala-Macro-Tools`可下载。
[各个注解的说明](./docs/howToUse.md)
> Annotations involving interaction are supported in the idea plug-in (named `Scala-Macro-Tools` in Marketplace).
# 如何使用
**[Description of each annotation](./docs/howToUse_en.md)**
添加库依赖,在sbt中
# How to use
> 在gradle,maven中,通常`scala-macro-tools`被替换为`scala-macro-tools_2.12`这种。其中,`2.12`表示Scala版本号。
Add library dependency
```scala
"io.github.jxnu-liguobin" %% "scala-macro-tools" % "<VERSION>"
```
该库已发布到maven中央仓库,请使用最新版本。仅将本库导入构建系统(例如gradle、sbt)是不够的。你需要多走一步。
The artefacts have been uploaded to Maven Central. Importing the library into your build system (e.g gradle, sbt), is not enough. You need to follow an extra step.
| Scala 2.11 | Scala 2.12 | Scala 2.13 |
| ------------------------ | ------------------------ | ------------------------------------- |
| 导入 macro paradise 插件 | 导入 macro paradise 插件 | 开启 编译器标记 `-Ymacro-annotations` |
| Scala 2.11 | Scala 2.12 | Scala 2.13 |
| ---------------------------- | ---------------------------- | --------------------------------------------------- |
| Import macro paradise plugin | Import macro paradise plugin | Enable compiler flag `-Ymacro-annotations` required |
```scala
addCompilerPlugin("org.scalamacros" % "paradise_<your-scala-version>" % "<plugin-version>")
```
`<your-scala-version>`必须是Scala版本号的完整编号,如`2.12.13`,而不是`2.12`
Where `<your-scala-version>` must be the full scala version. For example 2.12.13, and not 2.12.
如果这不起作用,可以谷歌寻找替代品。
If that doesn't work, google for alternatives.
`scala 2.13.x`版本中,macro paradise的功能直接包含在scala编译器中。然而,仍然必须启用编译器标志`-Ymacro annotations`
In version scala`2.13.x`, the functionality of macro paradise has been included in the scala compiler directly. However,
you must still enable the compiler flag `-Ymacro-annotations`.
\ No newline at end of file
<img align="right" width="30%" height="30%" src="img.png" alt="https://dreamylost.cn"/>
# scala-macro-tools
# scala-macro-tools
[![Build](https://github.com/jxnu-liguobin/scala-macro-tools/actions/workflows/ScalaCI.yml/badge.svg)](https://github.com/jxnu-liguobin/scala-macro-tools/actions/workflows/ScalaCI.yml)
[![codecov](https://codecov.io/gh/jxnu-liguobin/scala-macro-tools/branch/master/graph/badge.svg?token=IA596YRTOT)](https://codecov.io/gh/jxnu-liguobin/scala-macro-tools)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.jxnu-liguobin%22%20AND%20a:%22scala-macro-tools_2.13%22)
[![Version](https://img.shields.io/jetbrains/plugin/v/17202-scala-macro-tools)](https://plugins.jetbrains.com/plugin/17202-scala-macro-tools)
[![Version](https://img.shields.io/jetbrains/plugin/v/17202-scala-macro-tools)](https://plugins.jetbrains.com/plugin/17202-scala-macro-tools)
Motivation
该库的目的
--
Learn Scala macro and abstract syntax tree.
学习Scala宏编程(macro)和抽象语法树(ast)。
> The project is currently experimental
> 本项目目前处于实验阶段,有建议、意见或者问题欢迎提issue。如果本项目对你有帮助,欢迎点个star。
[中文说明](./README.md) | [English](./README_EN.md)
**[中文说明](./README_CN.md) | [English](./README.md)**
# Environment
# 我能学到什么
- It is compiled in Java 8, 11
- It is compiled in Scala 2.11.x ~ 2.13.x
- Scala2 宏编程
- 了解Scala反射
- 熟悉插值语法的应用,了解Scala Spec
- 了解Scala AST相关的基本使用
- 知道如何编写宏注解(宏拓展)
- 知道如何使用宏生成对象,类,方法,字段
- 其他
- 知道劝退人的SBT如何优雅配置,诸如发布到云仓库,多版本构建,多模块项目
- 了解如何利用Intellij-Scala插件编写自己的Scala插件
- 了解Intellij是怎样支持语法提示的,插件工作流程
- 知道如何写好scalatest单元测试
- 类似的其他思考
# Features
# 环境
- 使用 Java 8, 11 编译通过
- 使用 Scala 2.11.x ~ 2.13.x 编译通过
# 功能
- `@toString`
- `@json`
......@@ -32,31 +47,32 @@ Learn Scala macro and abstract syntax tree.
- `@constructor`
- `@equalsAndHashCode`
> Annotations involving interaction are supported in the idea plug-in (named `Scala-Macro-Tools` in Marketplace).
> 涉及到交互操作的注解在IDEA插件中都得到了支持。在插件市场中搜索`Scala-Macro-Tools`可下载。
[各个注解的说明](./docs/howToUse.md)
**[Description of each annotation](./docs/howToUse_en.md)**
# 如何使用
# How to use
添加库依赖,在sbt中
Add library dependency
> 在gradle,maven中,通常`scala-macro-tools`被替换为`scala-macro-tools_2.12`这种。其中,`2.12`表示Scala版本号。
```scala
"io.github.jxnu-liguobin" %% "scala-macro-tools" % "<VERSION>"
```
The artefacts have been uploaded to Maven Central. Importing the library into your build system (e.g gradle, sbt), is not enough. You need to follow an extra step.
该库已发布到maven中央仓库,请使用最新版本。仅将本库导入构建系统(例如gradle、sbt)是不够的。你需要多走一步。
| Scala 2.11 | Scala 2.12 | Scala 2.13 |
| ---------------------------- | ---------------------------- | --------------------------------------------------- |
| Import macro paradise plugin | Import macro paradise plugin | Enable compiler flag `-Ymacro-annotations` required |
| Scala 2.11 | Scala 2.12 | Scala 2.13 |
| ------------------------ | ------------------------ | ------------------------------------- |
| 导入 macro paradise 插件 | 导入 macro paradise 插件 | 开启 编译器标记 `-Ymacro-annotations` |
```scala
addCompilerPlugin("org.scalamacros" % "paradise_<your-scala-version>" % "<plugin-version>")
```
Where `<your-scala-version>` must be the full scala version. For example 2.12.13, and not 2.12.
`<your-scala-version>`必须是Scala版本号的完整编号,如`2.12.13`,而不是`2.12`
If that doesn't work, google for alternatives.
如果这不起作用,可以谷歌寻找替代品。
In version scala`2.13.x`, the functionality of macro paradise has been included in the scala compiler directly. However,
you must still enable the compiler flag `-Ymacro-annotations`.
`scala 2.13.x`版本中,macro paradise的功能直接包含在scala编译器中。然而,仍然必须启用编译器标志`-Ymacro annotations`
\ No newline at end of file
......@@ -22,7 +22,7 @@
package io.github.dreamylost.macros
import scala.reflect.macros.whitebox
import io.github.dreamylost.macros.ErrorMessage.UNEXPECTED_PATTERN
/**
*
* @author 梦境迷离
......@@ -113,7 +113,7 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
case (classDecl: ClassDef) :: Nil => None
case (classDecl: ClassDef) :: (compDecl: ModuleDef) :: Nil => Some(compDecl)
case (compDecl: ModuleDef) :: Nil => Some(compDecl)
case _ => None
case _ => c.abort(c.enclosingPosition, UNEXPECTED_PATTERN)
}
}
......@@ -165,7 +165,7 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
c.info(c.enclosingPosition, "Annotation is used on 'case class'.", force = true)
true
} else false
case _ => c.abort(c.enclosingPosition, s"${ErrorMessage.ONLY_CLASS} classDef: $annotateeClass")
case _ => c.abort(c.enclosingPosition, ErrorMessage.ONLY_CLASS)
}
}
......@@ -202,7 +202,7 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
* @param field
* @return
*/
def classParamsIsPrivate(field: Tree): Boolean = {
def classParamsIsNotPrivate(field: Tree): Boolean = {
field match {
case q"$mods val $tname: $tpt = $expr" => if (mods.asInstanceOf[Modifiers].hasFlag(Flag.PRIVATE)) false else true
case q"$mods var $tname: $tpt = $expr" => true
......
......@@ -31,24 +31,22 @@ import scala.reflect.macros.whitebox
*/
object builderMacro {
private final val BUFFER_CLASS_NAME_SUFFIX = "Builder"
class BuilderProcessor(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
import c.universe._
private def getBuilderClassName(classTree: TypeName): TypeName = {
TypeName(classTree.toTermName.decodedName.toString + BUFFER_CLASS_NAME_SUFFIX)
TypeName(classTree.toTermName.decodedName.toString + "Builder")
}
private def fieldDefinition(field: Tree): Tree = {
private def getFieldDefinition(field: Tree): Tree = {
field match {
case q"$mods val $tname: $tpt = $expr" => q"""private var $tname: $tpt = $expr"""
case q"$mods var $tname: $tpt = $expr" => q"""private var $tname: $tpt = $expr"""
}
}
private def fieldSetMethod(typeName: TypeName, field: Tree, classTypeParams: List[Tree]): Tree = {
private def getFieldSetMethod(typeName: TypeName, field: Tree, classTypeParams: List[Tree]): Tree = {
val builderClassName = getBuilderClassName(typeName)
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
field match {
......@@ -72,8 +70,8 @@ object builderMacro {
private def getBuilderClassAndMethod(typeName: TypeName, fieldss: List[List[Tree]], classTypeParams: List[Tree], isCase: Boolean): Tree = {
val fields = fieldss.flatten
val builderClassName = getBuilderClassName(typeName)
val builderFieldMethods = fields.map(f => fieldSetMethod(typeName, f, classTypeParams))
val builderFieldDefinitions = fields.map(f => fieldDefinition(f))
val builderFieldMethods = fields.map(f => getFieldSetMethod(typeName, f, classTypeParams))
val builderFieldDefinitions = fields.map(f => getFieldDefinition(f))
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
q"""
def builder[..$classTypeParams](): $builderClassName[..$returnTypeParams] = new $builderClassName()
......
......@@ -114,7 +114,6 @@ object equalsAndHashCodeMacro {
true
} else false
// the algorithm see https://alvinalexander.com/scala/how-to-define-equals-hashcode-methods-in-scala-object-equality/
// We use default 1.
if (!canEqualsExistsInSuper) {
q"""
override def hashCode(): Int = {
......@@ -139,7 +138,7 @@ object equalsAndHashCodeMacro {
(tpname.asInstanceOf[TypeName], paramss.asInstanceOf[List[List[Tree]]], stats.asInstanceOf[Seq[Tree]], parents.asInstanceOf[Seq[Tree]])
case _ => c.abort(c.enclosingPosition, s"${ErrorMessage.ONLY_CLASS} classDef: $classDecl")
}
val ctorFieldNames = annotteeClassParams.flatten.filter(cf => classParamsIsPrivate(cf))
val ctorFieldNames = annotteeClassParams.flatten.filter(cf => classParamsIsNotPrivate(cf))
val allFieldsTermName = ctorFieldNames.map(f => getFieldTermName(f))
val allTernNames = allFieldsTermName ++ getClassMemberAllTermName(annotteeClassDefinitions)
val hash = getHashcodeMethod(allTernNames, superClasses)
......
......@@ -63,7 +63,6 @@ object jsonMacro {
}
val format = jsonFormatter(className, fields.flatten)
val compDecl = modifiedCompanion(compDeclOpt, format, className)
c.info(c.enclosingPosition, s"format: $format, compDecl: $compDecl", force = true)
// Return both the class and companion object declarations
c.Expr(
q"""
......
......@@ -66,7 +66,7 @@ object logMacro {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: _ =>
LogType.getLogImpl(extractArgumentsDetail._2).getTemplate(c)(tpname.asInstanceOf[TypeName].toTermName.decodedName.toString, isClass = true)
case q"$mods object $tpname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: _ =>
LogType.getLogImpl(extractArgumentsDetail._2).getTemplate(c)(tpname.asInstanceOf[TermName].decodedName.toString, isClass = false)
LogType.getLogImpl(extractArgumentsDetail._2).getTemplate(c)(tpname.asInstanceOf[TermName].toTermName.decodedName.toString, isClass = false)
case _ => c.abort(c.enclosingPosition, s"Annotation is only supported on class or object.")
}
......
......@@ -40,6 +40,13 @@ class ConstructorTest extends AnyFlatSpec with Matchers {
| def helloWorld: String = "hello world"
| }""".stripMargin shouldNot compile
""" @constructor
| object A2 {
| private val a: Int = 1
| var b: Int = 1
| def helloWorld: String = "hello world"
| }""".stripMargin shouldNot compile
""" @apply @toString @builder @constructor(excludeFields=Seq("c"))
| class A2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L)) {
| private val a: Int = 1
......@@ -134,6 +141,15 @@ class ConstructorTest extends AnyFlatSpec with Matchers {
|
| def helloWorld: String = "hello world"
| }""".stripMargin should compile
""" @apply @toString @builder @constructor(verbose = true, excludeFields=Seq("c"))
| class A2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L)) {
| private val a: Int = 1
| var b: Int = 1
| protected var c: Int = _
|
| def helloWorld: String = "hello world"
| }""".stripMargin should compile
}
"constructor3" should "ok when construction is currying" in {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册