From 7c2079db78c223f6d0fa68d23b2c9db298afac30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E5=A2=83=E8=BF=B7=E7=A6=BB?= Date: Sun, 25 Jul 2021 22:13:46 +0800 Subject: [PATCH] add unittest (#84) * add unittest --- .codecov.yml | 9 ++- README.md | 58 +++++--------- README_CN.md | 78 +++++++++++++++++++ README_EN.md | 62 --------------- .../macros/AbstractMacroProcessor.scala | 8 +- .../dreamylost/macros/builderMacro.scala | 12 ++- .../macros/equalsAndHashCodeMacro.scala | 3 +- .../github/dreamylost/macros/jsonMacro.scala | 1 - .../github/dreamylost/macros/logMacro.scala | 2 +- .../github/dreamylost/ConstructorTest.scala | 16 ++++ 10 files changed, 132 insertions(+), 117 deletions(-) create mode 100644 README_CN.md delete mode 100644 README_EN.md diff --git a/.codecov.yml b/.codecov.yml index ab8fb39..9842ce0 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -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" diff --git a/README.md b/README.md index e79ad75..62820a8 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,27 @@ 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" % "" ``` -该库已发布到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_" % "") ``` -``必须是Scala版本号的完整编号,如`2.12.13`,而不是`2.12`。 +Where `` 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 diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 0000000..2449ccc --- /dev/null +++ b/README_CN.md @@ -0,0 +1,78 @@ +https://dreamylost.cn + +# 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) + +该库的目的 +-- + +学习Scala宏编程(macro)和抽象语法树(ast)。 + +> 本项目目前处于实验阶段,有建议、意见或者问题欢迎提issue。如果本项目对你有帮助,欢迎点个star。 + +**[中文说明](./README_CN.md) | [English](./README.md)** + +# 我能学到什么 + +- Scala2 宏编程 + - 了解Scala反射 + - 熟悉插值语法的应用,了解Scala Spec + - 了解Scala AST相关的基本使用 + - 知道如何编写宏注解(宏拓展) + - 知道如何使用宏生成对象,类,方法,字段 +- 其他 + - 知道劝退人的SBT如何优雅配置,诸如发布到云仓库,多版本构建,多模块项目 + - 了解如何利用Intellij-Scala插件编写自己的Scala插件 + - 了解Intellij是怎样支持语法提示的,插件工作流程 + - 知道如何写好scalatest单元测试 + - 类似的其他思考 + +# 环境 + +- 使用 Java 8, 11 编译通过 +- 使用 Scala 2.11.x ~ 2.13.x 编译通过 + +# 功能 + +- `@toString` +- `@json` +- `@builder` +- `@synchronized` +- `@log` +- `@apply` +- `@constructor` +- `@equalsAndHashCode` + +> 涉及到交互操作的注解在IDEA插件中都得到了支持。在插件市场中搜索`Scala-Macro-Tools`可下载。 + +[各个注解的说明](./docs/howToUse.md) + +# 如何使用 + +添加库依赖,在sbt中 + +> 在gradle,maven中,通常`scala-macro-tools`被替换为`scala-macro-tools_2.12`这种。其中,`2.12`表示Scala版本号。 + +```scala +"io.github.jxnu-liguobin" %% "scala-macro-tools" % "" +``` + +该库已发布到maven中央仓库,请使用最新版本。仅将本库导入构建系统(例如gradle、sbt)是不够的。你需要多走一步。 + +| Scala 2.11 | Scala 2.12 | Scala 2.13 | +| ------------------------ | ------------------------ | ------------------------------------- | +| 导入 macro paradise 插件 | 导入 macro paradise 插件 | 开启 编译器标记 `-Ymacro-annotations` | + +```scala +addCompilerPlugin("org.scalamacros" % "paradise_" % "") +``` + +``必须是Scala版本号的完整编号,如`2.12.13`,而不是`2.12`。 + +如果这不起作用,可以谷歌寻找替代品。 + +在`scala 2.13.x`版本中,macro paradise的功能直接包含在scala编译器中。然而,仍然必须启用编译器标志`-Ymacro annotations`。 \ No newline at end of file diff --git a/README_EN.md b/README_EN.md deleted file mode 100644 index af0f127..0000000 --- a/README_EN.md +++ /dev/null @@ -1,62 +0,0 @@ -https://dreamylost.cn - -# 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 --- - -Learn Scala macro and abstract syntax tree. - -> The project is currently experimental - -[中文说明](./README.md) | [English](./README_EN.md) - -# Environment - -- It is compiled in Java 8, 11 -- It is compiled in Scala 2.11.x ~ 2.13.x - -# Features - -- `@toString` -- `@json` -- `@builder` -- `@synchronized` -- `@log` -- `@apply` -- `@constructor` -- `@equalsAndHashCode` - -> Annotations involving interaction are supported in the idea plug-in (named `Scala-Macro-Tools` in Marketplace). - -**[Description of each annotation](./docs/howToUse_en.md)** - -# How to use - -Add library dependency - -```scala -"io.github.jxnu-liguobin" %% "scala-macro-tools" % "" -``` - -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 | -| ---------------------------- | ---------------------------- | --------------------------------------------------- | -| Import macro paradise plugin | Import macro paradise plugin | Enable compiler flag `-Ymacro-annotations` required | - -```scala -addCompilerPlugin("org.scalamacros" % "paradise_" % "") -``` - -Where `` must be the full scala version. For example 2.12.13, and not 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`. diff --git a/src/main/scala/io/github/dreamylost/macros/AbstractMacroProcessor.scala b/src/main/scala/io/github/dreamylost/macros/AbstractMacroProcessor.scala index 98802ad..9f9401b 100644 --- a/src/main/scala/io/github/dreamylost/macros/AbstractMacroProcessor.scala +++ b/src/main/scala/io/github/dreamylost/macros/AbstractMacroProcessor.scala @@ -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 diff --git a/src/main/scala/io/github/dreamylost/macros/builderMacro.scala b/src/main/scala/io/github/dreamylost/macros/builderMacro.scala index 99893bd..7b2fcf1 100644 --- a/src/main/scala/io/github/dreamylost/macros/builderMacro.scala +++ b/src/main/scala/io/github/dreamylost/macros/builderMacro.scala @@ -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() diff --git a/src/main/scala/io/github/dreamylost/macros/equalsAndHashCodeMacro.scala b/src/main/scala/io/github/dreamylost/macros/equalsAndHashCodeMacro.scala index bf02859..e080837 100644 --- a/src/main/scala/io/github/dreamylost/macros/equalsAndHashCodeMacro.scala +++ b/src/main/scala/io/github/dreamylost/macros/equalsAndHashCodeMacro.scala @@ -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) diff --git a/src/main/scala/io/github/dreamylost/macros/jsonMacro.scala b/src/main/scala/io/github/dreamylost/macros/jsonMacro.scala index 7690f07..40d621a 100644 --- a/src/main/scala/io/github/dreamylost/macros/jsonMacro.scala +++ b/src/main/scala/io/github/dreamylost/macros/jsonMacro.scala @@ -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""" diff --git a/src/main/scala/io/github/dreamylost/macros/logMacro.scala b/src/main/scala/io/github/dreamylost/macros/logMacro.scala index 2f29186..ce9b5d1 100644 --- a/src/main/scala/io/github/dreamylost/macros/logMacro.scala +++ b/src/main/scala/io/github/dreamylost/macros/logMacro.scala @@ -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.") } diff --git a/src/test/scala/io/github/dreamylost/ConstructorTest.scala b/src/test/scala/io/github/dreamylost/ConstructorTest.scala index 224eeac..28690b6 100644 --- a/src/test/scala/io/github/dreamylost/ConstructorTest.scala +++ b/src/test/scala/io/github/dreamylost/ConstructorTest.scala @@ -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 { -- GitLab