├── .github └── workflows │ ├── sbt-devops.yml │ └── scala.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── NOTICE ├── README.md ├── build.sbt ├── input └── src │ └── test │ ├── java │ └── fix │ │ └── scala213 │ │ └── explicitNonNullaryApply │ │ └── Fooable.java │ ├── scala-2.12 │ └── fix │ │ └── scala213 │ │ ├── Console212.scala │ │ ├── NullaryHashHash212.scala │ │ └── Varargs212.scala │ └── scala │ └── fix │ └── scala213 │ ├── Any2StringAdd.scala │ ├── ConstructorProcedureSyntax.scala │ ├── Core.scala │ ├── FinalObject.scala │ ├── MultiArgInfix.scala │ ├── NoAutoApply.scala │ ├── NullaryEtaExpansion.scala │ ├── NullaryEtaExpansionJava.scala │ ├── NullaryHashHash.scala │ ├── ParensAroundLambda.scala │ ├── ScalaSeq.scala │ ├── Varargs.scala │ ├── explicitNonNullaryApply │ ├── ApplyType.scala │ ├── Foo.scala │ ├── Implicit.scala │ ├── Infix.scala │ ├── NonNullary.scala │ ├── Nullary.scala │ ├── Override.scala │ ├── Postfix.scala │ ├── ToString.scala │ └── package.scala │ └── nullaryOverride │ ├── ImplicitAsNullary.scala │ ├── Local.scala │ └── NullaryOverride.scala ├── output └── src │ └── test │ ├── java │ └── fix │ │ └── scala213 │ │ └── explicitNonNullaryApply │ │ └── Fooable.java │ ├── scala-2.12 │ └── fix │ │ └── scala213 │ │ ├── Console212.scala │ │ ├── NullaryHashHash212.scala │ │ └── Varargs212.scala │ └── scala │ └── fix │ └── scala213 │ ├── Any2StringAdd.scala │ ├── ConstructorProcedureSyntax.scala │ ├── Core.scala │ ├── FinalObject.scala │ ├── MultiArgInfix.scala │ ├── NoAutoApply.scala │ ├── NullaryEtaExpansion.scala │ ├── NullaryEtaExpansionJava.scala │ ├── NullaryHashHash.scala │ ├── ParensAroundLambda.scala │ ├── ScalaSeq.scala │ ├── Varargs.scala │ ├── explicitNonNullaryApply │ ├── ApplyType.scala │ ├── Foo.scala │ ├── Implicit.scala │ ├── Infix.scala │ ├── NonNullary.scala │ ├── Nullary.scala │ ├── Override.scala │ ├── Postfix.scala │ ├── ToString.scala │ └── package.scala │ └── nullaryOverride │ ├── ImplicitAsNullary.scala │ ├── Local.scala │ └── NullaryOverride.scala ├── project ├── ScalaNightlyPlugin.scala ├── build.properties └── plugins.sbt ├── rewrites └── src │ └── main │ ├── resources │ └── META-INF │ │ └── services │ │ └── scalafix.v1.Rule │ └── scala │ ├── fix │ ├── explicittypes │ │ ├── ExplicitImplicitTypes.scala │ │ └── PatchEmptyBody.scala │ └── scala213 │ │ ├── Any2StringAdd.scala │ │ ├── ConstructorProcedureSyntax.scala │ │ ├── Core.scala │ │ ├── DottyMigrate.scala │ │ ├── ExplicitNonNullaryApply.scala │ │ ├── ExplicitNullaryEtaExpansion.scala │ │ ├── FinalObject.scala │ │ ├── MultiArgInfix.scala │ │ ├── NullaryHashHash.scala │ │ ├── NullaryOverride.scala │ │ ├── NullaryOverrideConfig.scala │ │ ├── ParensAroundLambda.scala │ │ ├── ScalaSeq.scala │ │ └── Varargs.scala │ ├── impl │ ├── CollectingTraverser.scala │ ├── CompilerDependentRule.scala │ ├── GlobalImports.scala │ ├── NullaryPower.scala │ ├── Power.scala │ ├── SignatureMatcher.scala │ └── TreeReplacers.scala │ └── scalafixinternal │ └── ProcedureSyntax.scala ├── tests └── src │ └── test │ └── scala │ └── fix │ ├── BeforeAndAfterAllConfigMapAlt.scala │ └── RuleSuite.scala └── tests3 └── src └── test └── scala └── fix └── DottyMigrateApplier.scala /.github/workflows/sbt-devops.yml: -------------------------------------------------------------------------------- 1 | # @see https://github.com/ohze/sbt-devops/blob/main/files/sbt-devops.yml 2 | name: sbt-devops 3 | on: [push, pull_request] 4 | jobs: 5 | build: 6 | runs-on: ubuntu-20.04 7 | outputs: 8 | commitMsg: ${{ steps.commitMsg.outputs.msg }} 9 | strategy: 10 | matrix: 11 | java: [ '8', '11', '17' ] 12 | steps: 13 | - uses: actions/checkout@v2 14 | - id: commitMsg 15 | run: echo "::set-output name=msg::$(git show -s --format=%s $GITHUB_SHA)" 16 | # - uses: actions/setup-java@v2 17 | # with: 18 | # java-version: ${{ matrix.java }} 19 | # distribution: 'temurin' 20 | # - uses: coursier/cache-action@v6 21 | # # - run: sbt devopsQA # dont scalafmt to keep sync with upstream code 22 | # - run: sbt +test 23 | # # https://www.scala-sbt.org/1.x/docs/GitHub-Actions-with-sbt.html#Caching 24 | # - run: | 25 | # rm -rf "$HOME/.ivy2/local" || true 26 | # find $HOME/Library/Caches/Coursier/v1 -name "ivydata-*.properties" -delete || true 27 | # find $HOME/.ivy2/cache -name "ivydata-*.properties" -delete || true 28 | # find $HOME/.cache/coursier/v1 -name "ivydata-*.properties" -delete || true 29 | # find $HOME/.sbt -name "*.lock" -delete || true 30 | # shell: bash 31 | # 32 | # publish: 33 | # needs: build 34 | # if: | 35 | # success() && 36 | # github.event_name == 'push' && 37 | # (github.ref == 'refs/heads/main' || 38 | # github.ref == 'refs/heads/dotty' || 39 | # startsWith(github.ref, 'refs/tags/')) 40 | # runs-on: ubuntu-20.04 41 | # outputs: 42 | # info: ${{ steps.info.outputs.info }} 43 | # steps: 44 | # - uses: actions/checkout@v2 45 | # with: 46 | # fetch-depth: 0 47 | # - uses: actions/setup-java@v2 48 | # with: 49 | # distribution: 'temurin' 50 | # java-version: '8' 51 | # - run: sbt ci-release 52 | # env: 53 | # PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 54 | # PGP_SECRET: ${{ secrets.PGP_SECRET }} 55 | # SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 56 | # SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 57 | # # optional 58 | # #CI_CLEAN: '; clean ; sonatypeBundleClean' 59 | # #CI_RELEASE: '+publishSigned' 60 | # #CI_SONATYPE_RELEASE: 'sonatypeBundleRelease' 61 | # #CI_SNAPSHOT_RELEASE: '+publish' 62 | # - id: info 63 | # run: echo "::set-output name=info::$(cat "$GITHUB_WORKSPACE/target/publish.info")" 64 | # 65 | # notify: 66 | # needs: [build, publish] 67 | # if: always() 68 | # runs-on: ubuntu-latest 69 | # steps: 70 | # - uses: docker://ohze/devops-notify 71 | # env: 72 | # # You can use `DEVOPS_` | `SLACK_` prefix for `MATTERMOST_*` envs below 73 | # # ex SLACK_WEBHOOK_URL instead of MATTERMOST_WEBHOOK_URL 74 | # MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }} 75 | # # optional. See https://developers.mattermost.com/integrate/incoming-webhooks/#parameters 76 | # #MATTERMOST_ICON: icon_url or icon_emoji 77 | # #MATTERMOST_CHANNEL: use default of the webhook if not set 78 | # #MATTERMOST_USERNAME: use default of the webhook if not set 79 | # #MATTERMOST_PRETEXT: Message shown above the CI status attachment, ex to mention some user. Default empty. 80 | # _DEVOPS_NEEDS: ${{ toJSON(needs) }} 81 | -------------------------------------------------------------------------------- /.github/workflows/scala.yml: -------------------------------------------------------------------------------- 1 | name: Scala CI 2 | 3 | on: 4 | push: 5 | branches: [ dotty ] 6 | pull_request: 7 | branches: [ dotty ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up JDK 11 17 | uses: actions/setup-java@v2 18 | with: 19 | java-version: '11' 20 | distribution: 'adopt' 21 | - name: Run tests 22 | run: sbt test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | target-2.13 3 | /.nonNullary.NullaryOverride 4 | /.nullary.NullaryOverride 5 | /.idea/ 6 | /.bsp 7 | /.vscode 8 | .bloop 9 | /.metals 10 | metals.sbt 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | all repositories in these organizations: 2 | 3 | * [scala](https://github.com/scala) 4 | * [scalacenter](https://github.com/scalacenter) 5 | * [lampepfl](https://github.com/lampepfl) 6 | 7 | are covered by the Scala Code of Conduct: https://scala-lang.org/conduct/ 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | scala-rewrites 2 | Copyright (c) 2019-2021 Lightbend, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | Unless required by applicable law or agreed to in writing, software 6 | distributed under the License is distributed on an "AS IS" BASIS, 7 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | See the License for the specific language governing permissions and 9 | limitations under the License. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scalafix Rewrites to migrate scala 2 -> 3 (dotty) | latest scala 2.13.x 2 | 3 | [![CI](https://github.com/ohze/scala-rewrites/actions/workflows/sbt-devops.yml/badge.svg)](https://github.com/ohze/scala-rewrites/actions/workflows/sbt-devops.yml) 4 | 5 | ## What? 6 | This repo contains the following scalafix rules: 7 | 8 | #### fix.scala213.DottyMigrate 9 | Combine and run all following rules when traveling the scalameta trees represent scala files **once** 10 | ```hocon 11 | rules = [ 12 | ProcedureSyntax # built-in scalafix rule 13 | fix.scala213.ConstructorProcedureSyntax 14 | fix.scala213.ParensAroundLambda 15 | fix.scala213.NullaryOverride 16 | fix.explicittypes.ExplicitImplicitTypes 17 | fix.scala213.MultiArgInfix 18 | fix.scala213.Any2StringAdd 19 | fix.scala213.ExplicitNonNullaryApply 20 | fix.scala213.ExplicitNullaryEtaExpansion 21 | ] 22 | ``` 23 | 24 | This rule is [successfully used][3] 25 | to migrate akka project to scala 2.13.3/ scala 3 (dotty) 26 | 27 | #### fix.scala213.ConstructorProcedureSyntax 28 | Remove constructor procedure syntax: `def this(..) {..}` => `def this(..) = {..}` 29 | 30 | This rule complement to the built-in [ProcedureSyntax][4] rule. 31 | 32 | #### fix.scala213.ParensAroundLambda 33 | Fix: parentheses are required around the parameter of a lambda 34 | ```scala 35 | Seq(1).map { i: Int => // rewrite to: Seq(1).map { (i: Int) => 36 | i + 1 37 | } 38 | Seq(1).map { i => i + 1 } // keep 39 | ``` 40 | 41 | #### fix.scala213.NullaryOverride 42 | Consistent nullary overriding 43 | ```scala 44 | trait A { 45 | def i: Int 46 | def u(): Unit 47 | } 48 | trait B extends A { 49 | def i() = 1 // fix by remove `()`: def i = 1 50 | def u = println("hi") // fix by add `()`: def u() = println("hi") 51 | } 52 | ``` 53 | 54 | #### fix.explicittypes.ExplicitImplicitTypes 55 | Explicitly add type to `implicit def/val/var`s (required by dotty) 56 | ```scala 57 | trait Foo { 58 | // rewrite implicit members of class/trait 59 | // rewrite to `implicit val s: Seq[String] = Nil` 60 | implicit val s = Seq.empty[String] 61 | // rewrite to `implicit def i: Int = 1` 62 | implicit def i = 1 63 | 64 | def f() = { 65 | class C { 66 | // Also rewrite implicit local `def/val/var`s that its parent is a trait/class (required by dotty) 67 | // rewrite to `implicit val i: Int = 1` 68 | implicit val i = 1 69 | } 70 | // local implicits like this don't need to be rewritten 71 | implicit val s = "" 72 | ??? 73 | } 74 | } 75 | ``` 76 | 77 | Optional. Using `symbolReplacements` with this config in `.scalafix.conf`: 78 | ```hocon 79 | ExplicitImplicitTypes.symbolReplacements { 80 | "scala/concurrent/ExecutionContextExecutor#" = "scala/concurrent/ExecutionContext#" 81 | } 82 | ``` 83 | Then: 84 | ```scala 85 | import scala.concurrent.ExecutionContextExecutor 86 | 87 | trait T { 88 | def someEc(): ExecutionContextExecutor 89 | } 90 | trait A { 91 | def t: T 92 | // rewrite to `implicit def ec: ExecutionContext = t.someEc()` 93 | implicit def ec = t.someEc() 94 | } 95 | ``` 96 | 97 | #### fix.scala213.MultiArgInfix 98 | ```scala 99 | trait PipeToSupport { 100 | def to(recipient: Int, sender: Int): Unit 101 | } 102 | def p: PipeToSupport = ??? 103 | // rewrite to `p.to(1, 2) 104 | p to (1, 2) 105 | ``` 106 | 107 | #### fix.scala213.FinalObject 108 | Remove redundant `final` modifier for objects: 109 | ```scala 110 | final object Abc 111 | ``` 112 | 113 | #### fix.scala213.Any2StringAdd 114 | ```scala 115 | Nil + "foo" // => String.valueOf(Nil) + "foo" 116 | 117 | type A 118 | def x: A = ??? 119 | x + "foo" // => String.valueOf(x) + "foo" 120 | 121 | 1 + "foo" // => "" + 1 + "foo" 122 | ``` 123 | 124 | #### fix.scala213.ExplicitNonNullaryApply 125 | ```scala 126 | // Given: 127 | def meth() = ??? 128 | def meth2()(implicit s: String) = ??? 129 | object meth3 { 130 | def apply[A]()(implicit a: A) = ??? 131 | } 132 | def prop(implicit s: String) = ??? 133 | 134 | // Then: 135 | meth // rewrite to `meth()` 136 | meth2 // rewrite to `meth2()` 137 | meth3[Int] // rewrite to: meth3[Int]() 138 | prop // keep 139 | ``` 140 | 141 | #### fix.scala213.ExplicitNullaryEtaExpansion 142 | ```scala 143 | def prop = "" 144 | def meth() = "" 145 | def void(x: Any) = "" 146 | 147 | def def_prop = prop _ // rewrite to: def def_prop = () => prop 148 | def def_meth = meth _ // leave 149 | def def_idty = void _ // leave 150 | 151 | def lzy(f: => Int) = { 152 | val k = f _ // rewrite to: val k = () => f 153 | ??? 154 | } 155 | ``` 156 | 157 | #### Other rules to migrate scala 2.12 -> 2.13 158 | - fix.scala213.Core 159 | - fix.scala213.NullaryHashHash 160 | - fix.scala213.ScalaSeq 161 | - fix.scala213.Varargs 162 | 163 | ## How 164 | ### Migrate sbt projects to latest scala 2.13 or to dotty 165 | _See also [this commit message][5]_ 166 | 167 | 0. Optional. Add to `.jvmopts` 168 | ``` 169 | -Xss8m 170 | -Xms1G 171 | -Xmx8G 172 | ``` 173 | 174 | 1. Add the `sbt-scalafix` sbt plugin, with the SemanticDB compiler plugin enabled ([official docs][1]): 175 | 176 | ```scala 177 | // project/plugins.sbt 178 | addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.31") 179 | ``` 180 | 181 | ```scala 182 | // build.sbt 183 | inThisBuild(List( 184 | semanticdbEnabled := true, 185 | semanticdbOptions += "-P:semanticdb:synthetics:on", // make sure to add this 186 | semanticdbVersion := scalafixSemanticdb.revision, 187 | scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value), 188 | )) 189 | ``` 190 | 191 | 2. Run `fix.scala213.DottyMigrate` rule ([official docs][2]), in sbt: 192 | _Replace `` with an actual value, eg `0.1.6-sd`. See [releases](https://github.com/ohze/scala-rewrites/releases)_ 193 | ```scala 194 | > scalafixAll dependency:fix.scala213.DottyMigrate@com.sandinh:scala-rewrites: 195 | ``` 196 | 197 | You can also add the following to your `build.sbt`: 198 | ```scala 199 | ThisBuild / scalafixDependencies += "com.sandinh" %% "scala-rewrites" % "" 200 | ``` 201 | and then: 202 | ```scala 203 | > scalafixAll fix.scala213.DottyMigrate 204 | ``` 205 | 206 | 3. Run `fix.scala213.NullaryOverride` rule 207 | 208 | (bash) 209 | ```bash 210 | echo 'rules = [ fix.scala213.NullaryOverride ]' > .NullaryOverride.conf 211 | echo 'NullaryOverride.mode = Rewrite' >> .NullaryOverride.conf 212 | ``` 213 | (sbt) 214 | ```scala 215 | > set scalafixConfig := Some(file(".NullaryOverride.conf")) 216 | > scalafixAll 217 | ``` 218 | 219 | 4. Optional. Run other rules 220 | ```scala 221 | > scalafixAll fix.scala213.FinalObject 222 | > // similar for other rules 223 | ``` 224 | 225 | [1]: https://scalacenter.github.io/scalafix/docs/users/installation.html 226 | [2]: https://scalacenter.github.io/scalafix/docs/rules/external-rules.html 227 | [3]: https://github.com/akka/akka/pull/29367 228 | [4]: https://github.com/scalacenter/scalafix/blob/master/scalafix-rules/src/main/scala/scalafix/internal/rule/ProcedureSyntax.scala 229 | [5]: https://github.com/akka/akka/pull/29367/commits/b4e3f2bd 230 | 231 | ## To develop/contribute to any of the rewrites 232 | 233 | ``` 234 | sbt ~tests/test 235 | # edit rewrites/src/main/scala/... 236 | ``` 237 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import _root_.scalafix.sbt.BuildInfo._ 2 | import sbt.librarymanagement.Configurations.CompilerPlugin 3 | 4 | def scalametaVersion = "4.4.28" 5 | 6 | inThisBuild(List( 7 | organization := "com.sandinh", 8 | licenses := List("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0")), 9 | developers := List(Developer("", "", "", url("https://github.com/scala/scala-rewrites/graphs/contributors"))), 10 | homepage := Some(url("https://github.com/scala/scala-rewrites")), 11 | scalaVersion := (sys.env.get("TRAVIS_SCALA_VERSION") match { 12 | case Some("2.13") => scala213 13 | case Some("2.12") => scala212 14 | case Some("2.12.next") => scala212 // and then overriden by ScalaNightlyPlugin 15 | case None => scala213 16 | case tsv => sys.error(s"Unknown TRAVIS_SCALA_VERSION $tsv") 17 | }), 18 | scalacOptions ++= List("-deprecation", "-feature", "-language:_", "-unchecked", "-Xlint"), 19 | crossScalaVersions := Seq(scala212, scala213), 20 | publish / skip := true, 21 | )) 22 | 23 | val rewrites = project.enablePlugins(ScalaNightlyPlugin).settings( 24 | moduleName := "scala-rewrites", 25 | libraryDependencies += "ch.epfl.scala" %% "scalafix-rules" % scalafixVersion, 26 | libraryDependencies ++= ( 27 | if (scalaVersion.value.startsWith("2.13")) Nil 28 | else Seq("com.github.bigwheel" %% "util-backports" % "2.1") 29 | ), 30 | publish / skip := false, 31 | ) 32 | 33 | val input = project.enablePlugins(ScalaNightlyPlugin).settings( 34 | scalacOptions ++= List("-Yrangepos", "-P:semanticdb:synthetics:on"), 35 | libraryDependencies += "org.scalameta" % "semanticdb-scalac" % scalametaVersion % CompilerPlugin cross CrossVersion.patch, 36 | ) 37 | 38 | val output = project 39 | 40 | // This project is used to verify that the output code actually compiles with scala 2.13 41 | val output213 = output.withId("output213").settings( 42 | target := file(s"${target.value.getPath}-2.13"), 43 | scalaVersion := scala213, 44 | crossScalaVersions := Seq(scalaVersion.value), 45 | ) 46 | 47 | val tests = project.dependsOn(rewrites).enablePlugins(ScalaNightlyPlugin, ScalafixTestkitPlugin).settings( 48 | libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % scalafixVersion % Test cross CrossVersion.patch, 49 | Compile / compile := (Compile / compile).dependsOn(input / Test / compile).value, 50 | scalafixTestkitInputClasspath := ( input / Test / fullClasspath).value, 51 | scalafixTestkitInputSourceDirectories := ( input / Test / sourceDirectories).value, 52 | scalafixTestkitOutputSourceDirectories := (output / Test / sourceDirectories).value, 53 | ScalaNightlyPlugin.ifNightly(Test / fork := true), 54 | ) 55 | 56 | ScalaNightlyPlugin.bootstrapSettings 57 | 58 | val input3 = project.settings( 59 | scalacOptions ++= List("-Yrangepos", "-P:semanticdb:synthetics:on"), 60 | addCompilerPlugin("org.scalameta" % "semanticdb-scalac" % scalametaVersion cross CrossVersion.patch), 61 | scalaVersion := scala213, 62 | crossScalaVersions := Seq(scalaVersion.value), 63 | Test / javaSource := (input / Test / javaSource).value, 64 | Test / sourceGenerators += Def.task { 65 | val inDir = (input / Test / scalaSource).value 66 | val outDir = (Test / sourceManaged).value 67 | val rebase = Path.rebase(inDir, outDir).andThen(_.get) 68 | 69 | // Replace `rule = ..` to `rule = fix.scala213.DottyMigrate` in all .scala files in `inSrcDir` 70 | def genSrc(in: File): File = { 71 | val out = rebase(in) 72 | IO.write(out, 73 | IO.read(in).replaceFirst( 74 | """rule\s*=\s*fix\.scala213\.\w+""", 75 | "rule = fix.scala213.DottyMigrate")) 76 | out 77 | } 78 | 79 | val pending = Set( 80 | "Core", // invalid `?=>` identifier & symbol literal 'foo 81 | "Infix", // qual() id[Any] qual() 82 | "NullaryEtaExpansionJava", // https://gitter.im/lampepfl/dotty?at=5f03f931a5ab931e4f6cf6c5 83 | "Override", // see TODO in fix.scala213.NullaryOverride.collector 84 | "NullaryOverride", 85 | ) 86 | def isPending(f: File) = pending.contains(f.base) 87 | 88 | val files = inDir.globRecursive("*.scala").get.filterNot(isPending) 89 | 90 | Tracked.diffOutputs( 91 | streams.value.cacheStoreFactory.make("diff"), 92 | FileInfo.lastModified 93 | )(files.toSet) { diff => 94 | IO.delete(diff.removed map rebase) 95 | val initial = diff.modified & diff.checked 96 | (initial.map(genSrc) ++ diff.unmodified.map(rebase)).toSeq 97 | } 98 | }.taskValue 99 | ) 100 | 101 | val rewrites213 = rewrites.withId("rewrites213").settings( 102 | scalaVersion := scala213, 103 | crossScalaVersions := Seq(scalaVersion.value), 104 | target := file(s"${target.value.getPath}-2.13"), 105 | publish / skip := true, 106 | ) 107 | 108 | val tests3 = project.enablePlugins(ScalafixTestkitPlugin).settings( 109 | scalaVersion := scala213, 110 | crossScalaVersions := Seq(scalaVersion.value), 111 | libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % scalafixVersion % Test cross CrossVersion.patch, 112 | Compile / compile := (Compile / compile).dependsOn(input3 / Test / compile).value, 113 | scalafixTestkitInputClasspath := ( input3 / Test / fullClasspath).value, 114 | scalafixTestkitInputSourceDirectories := ( input3 / Test / sourceDirectories).value, 115 | scalafixTestkitOutputSourceDirectories := { 116 | val d = (ThisBuild / baseDirectory).value / "output3" / "target" / "src-managed" 117 | if (!d.exists()) IO.createDirectory(d) 118 | Seq(d) 119 | }, 120 | ).dependsOn(rewrites213) 121 | 122 | // This project is used to verify that output can be compiled with dotty 123 | val output3 = project.settings( 124 | crossScalaVersions := Seq("3.0.2", "3.1.0-RC2"), 125 | scalaVersion := crossScalaVersions.value.head, 126 | Test / javaSource := (output / Test / javaSource).value, 127 | Test / sourceGenerators += Def.task { 128 | (tests3 / Test / test).value 129 | (target.value / "src-managed").globRecursive("*.scala").get 130 | }.taskValue 131 | ) 132 | 133 | ThisBuild / scalafixScalaBinaryVersion := scalaBinaryVersion.value 134 | 135 | lazy val root = project.in(file(".")) 136 | .aggregate(rewrites, input, output, output213, tests, input3, output3, rewrites213) 137 | -------------------------------------------------------------------------------- /input/src/test/java/fix/scala213/explicitNonNullaryApply/Fooable.java: -------------------------------------------------------------------------------- 1 | package fix.scala213.explicitNonNullaryApply; 2 | 3 | // A Java interface to test ExplicitNonNullaryApply against 4 | interface Fooable { String foo(); } 5 | -------------------------------------------------------------------------------- /input/src/test/scala-2.12/fix/scala213/Console212.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.Core 3 | */ 4 | package fix.scala213 5 | 6 | object Console212 { 7 | def consoleReadBoolean = Console.readBoolean() 8 | def consoleReadByte = Console.readByte() 9 | def consoleReadChar = Console.readChar() 10 | def consoleReadDouble = Console.readDouble() 11 | def consoleReadFloat = Console.readFloat() 12 | def consoleReadInt = Console.readInt() 13 | def consoleReadLine = Console.readLine() 14 | def consoleReadLine(text: String, args: Any*) = Console.readLine(text, args: _*) 15 | def consoleReadLong = Console.readLong() 16 | def consoleReadShort = Console.readShort() 17 | def consoleReadf(format: String) = Console.readf(format) 18 | def consoleReadf1(format: String) = Console.readf1(format) 19 | def consoleReadf2(format: String) = Console.readf2(format) 20 | def consoleReadf3(format: String) = Console.readf3(format) 21 | } 22 | -------------------------------------------------------------------------------- /input/src/test/scala-2.12/fix/scala213/NullaryHashHash212.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.NullaryHashHash 3 | */ 4 | package fix.scala213 5 | 6 | object NullaryHashHash212 { 7 | ("": Any).##() 8 | ("": AnyRef).##() 9 | ("": Object).##() 10 | ("": String).##() 11 | "".##() 12 | } 13 | -------------------------------------------------------------------------------- /input/src/test/scala-2.12/fix/scala213/Varargs212.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.Varargs 3 | */ 4 | package fix.scala213 5 | 6 | class Varargs212 { 7 | def foo(xs: scala.collection.Seq[String]) = List(xs: _*) 8 | } 9 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/Any2StringAdd.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.Any2StringAdd 3 | */ 4 | package fix.scala213 5 | 6 | abstract class Any2StringAdd { 7 | // Strings: leave as-is, both literal strings and non-literal 8 | def s = "bob" 9 | def str1 = s + s 10 | def str2 = s + "bob" 11 | def str3 = "bob" + s 12 | def str4 = "bob" + "fred" 13 | 14 | // Non-strings: wrap with String.valueOf 15 | def n: Any = null 16 | def nil = Nil + s 17 | def none = None + s 18 | def Null = n + s 19 | 20 | // Non-string, generic type: wrap with String.valueOf 21 | type A 22 | def x: A 23 | def generic = x + "bob" 24 | 25 | // Numeric primitives: add toString 26 | def byte = 1.toByte 27 | def short = 1.toShort 28 | def char = 'a' 29 | def int = 1 30 | def long = 1L 31 | def float = 1.0F 32 | def double = 1.0 33 | // 34 | def byte1 = byte + s 35 | def byte2 = byte + byte 36 | def short1 = short + s 37 | def short2 = short + short 38 | def char1 = char + s 39 | def char2 = char + char 40 | def int1 = int + s 41 | def int2 = int + int 42 | def long1 = long + s 43 | def long2 = long + long 44 | def float1 = float + s 45 | def float2 = float + float 46 | def double1 = double + s 47 | def double2 = double + double 48 | 49 | // Boolean: wrap with String.valueOf (there's no + on Boolean, AFAICT) 50 | def bool = true 51 | def bool1 = bool + s 52 | 53 | // Scala's Unit primitive: wrap with String.valueOf (there's no + on Unit, AFAICT) 54 | def unit = () 55 | def unit1 = unit + s 56 | 57 | // Custom value types: wrap with String.valueOf (adding toString would be preferable) 58 | import Any2StringAdd.Name 59 | //val name: Name = null // type mismatch; found: Null(null), required: Name 60 | val name = new Name(null) 61 | def name1 = name + s 62 | 63 | // With infix operators, make sure to use parens 64 | def parens1 = Nil ++ Nil + s 65 | def parens2 = int + int + s 66 | def parens3 = {Nil ++ Nil} + s 67 | } 68 | 69 | object Any2StringAdd { 70 | final class Name(val value: String) extends AnyVal { override def toString = value } 71 | } 72 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/ConstructorProcedureSyntax.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ConstructorProcedureSyntax 3 | */ 4 | package fix.scala213 5 | 6 | class ConstructorProcedureSyntax(i: Long) { 7 | def this(a: Int) { this(0L + 1) } 8 | def this(a: Int, b: Int) { 9 | this(0L) 10 | } 11 | def f/**/{ 12 | println("f") 13 | class X{ 14 | def this(a:Int) { this() } 15 | def this (j: String)= this(j.toInt) 16 | } 17 | } 18 | def this(){this(1L) 19 | println("hi") 20 | class X{ 21 | def this (j: String) // comment } 22 | { 23 | this() 24 | } 25 | def this (j: Int) { // comment { 26 | this() 27 | } 28 | } 29 | println(".") 30 | } 31 | def this(i: Float) /**/ 32 | // 33 | /***/ 34 | { 35 | this(i.toLong) 36 | } 37 | 38 | def this(i: String) = this(i.toLong) 39 | } 40 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/Core.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.Core 3 | */ 4 | package fix.scala213 5 | 6 | import scala.compat.Platform 7 | import scala.compat.Platform.{EOL, arraycopy, currentTime} 8 | 9 | object Core { 10 | def eol1 = "Hello World!" + EOL 11 | def eol2 = s"Hello World!$EOL" 12 | def elo2b = s"Hello World!${EOL}" 13 | def eol3 = "Hello World!" + Platform.EOL 14 | def eol4 = s"Hello World!${Platform.EOL}" 15 | def eol5 = "Hello World!" + scala.compat.Platform.EOL 16 | def eol6 = s"Hello World!${scala.compat.Platform.EOL}" 17 | 18 | def currentTimeMillis1 = currentTime 19 | def currentTimeMillis2 = Platform.currentTime 20 | def currentTimeMillis3 = scala.compat.Platform.currentTime 21 | def currentTimeMillis4 = s"now: $currentTime" 22 | 23 | def arrayCopy1() = arraycopy(null, 0, null, 0, 0) 24 | def arrayCopy2() = Platform.arraycopy(null, 0, null, 0, 0) 25 | def arrayCopy3() = scala.compat.Platform.arraycopy(null, 0, null, 0, 0) 26 | 27 | def arrow1: PartialFunction[Any, String] = { 28 | case 0 => "zero" 29 | case 1 ⇒ "one" 30 | case 2 ⇒ "two" 31 | case n: Int ⇒ "ginormous" 32 | case "⇒" => "?" 33 | } 34 | def arrow2(f: Int ⇒ String) = f(1) 35 | def arrow3 = { 36 | import scala.{PartialFunction ⇒ ?=>} 37 | def f: Int ?=> String = { 38 | case 1 => "one" 39 | } 40 | } 41 | def arrow4 = Some(1).map(x ⇒ x.toString) 42 | 43 | def singleArrow1: (Int, Int) = 1 → 1 44 | def singleArrow2: (Int, Int) = 1.→(1) 45 | 46 | def symbolLiteral0 = Symbol("foo") 47 | def symbolLiteral1 = 'foo 48 | } 49 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/FinalObject.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.FinalObject 3 | */ 4 | package fix.scala213 5 | 6 | final object FinalObject { 7 | // Add code that needs fixing here. 8 | final object A { 9 | def f = { 10 | final object A 11 | println() 12 | } 13 | } 14 | final case object B { 15 | val x = 1 16 | final case object A {} 17 | final class B 18 | } 19 | final class C {} 20 | final case class D(x: Int) 21 | final val x = 1 22 | } 23 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/MultiArgInfix.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.MultiArgInfix 3 | */ 4 | package fix.scala213 5 | 6 | object MultiArgInfix { 7 | trait PipeToSupport { 8 | def to(recipient: Int, sender: Int): Unit 9 | def foo[A](a: A, i: String): Unit 10 | } 11 | 12 | def p: PipeToSupport = ??? 13 | 14 | p to (1, 2) 15 | p /* c1 */ to (3, 4 + 5) 16 | 17 | p foo (1, "2") 18 | p /* c2 */ foo[Long] /* c3 */ (3, "" + 4) 19 | } 20 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/NoAutoApply.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213 5 | 6 | object NoAutoApply { 7 | object buz { 8 | override def toString(): String = "" 9 | def empty[T]() = List.empty[T] 10 | } 11 | val x: Iterator[Int] = ??? 12 | def foo() = println(1) 13 | def bar = 32 14 | foo 15 | println(foo) 16 | foo() 17 | bar 18 | x.next 19 | class buz() { 20 | def qux() = List(1) 21 | } 22 | new buz().qux.toIterator.next 23 | new java.util.ArrayList[Int]().toString 24 | val builder = List.newBuilder[Int] 25 | builder.result() 26 | //builder result () 27 | builder.result 28 | fix.scala213.NoAutoApply.buz.empty[String] 29 | var opt: Option[() => Int] = None 30 | opt = None 31 | println(1.toString) 32 | println(buz.toString) // not inserted 33 | List(builder) map (_.result) 34 | builder.## 35 | def lzy(f: => Int) = { 36 | var k = f _ 37 | k = () => 3 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/NullaryEtaExpansion.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNullaryEtaExpansion 3 | */ 4 | package fix.scala213 5 | 6 | class NullaryEtaExpansion { 7 | val str1 = "" 8 | def prop = "" 9 | def meth() = "" 10 | def void(x: Any) = "" 11 | 12 | def def_str1 = str1 _ // rewrite 13 | def def_prop = prop _ // rewrite 14 | def def_meth = meth _ // leave 15 | def def_idty = void _ // leave 16 | 17 | val val_str1 = str1 _ // rewrite 18 | val val_prop = prop _ // rewrite 19 | val val_meth = meth _ // leave 20 | val val_idty = void _ // leave 21 | 22 | var var_str1 = str1 _ // rewrite 23 | var var_prop = prop _ // rewrite 24 | var var_meth = meth _ // leave 25 | var var_idty = void _ // leave 26 | 27 | lazy val lzy_str1 = str1 _ // rewrite 28 | lazy val lzy_prop = prop _ // rewrite 29 | lazy val lzy_meth = meth _ // leave 30 | lazy val lzy_idty = void _ // leave 31 | 32 | def lzy(f: => Int) = { 33 | val k = f _ 34 | k() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/NullaryEtaExpansionJava.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNullaryEtaExpansion 3 | */ 4 | package fix.scala213 5 | 6 | class NullaryEtaExpansionJava { 7 | class ToS1 { override def toString(): String = "" } 8 | class ToS2 { override def toString: String = "" } 9 | class ToS3 { override val toString: String = "" } 10 | class ToS4 { override lazy val toString: String = "" } 11 | 12 | val toS1 = new ToS1 13 | val toS2 = new ToS2 14 | val toS3 = new ToS3 15 | val toS4 = new ToS4 16 | 17 | val val_toS1 = toS1.toString _ 18 | var var_toS1 = toS1.toString _ 19 | def def_toS1 = toS1.toString _ 20 | lazy val lzy_toS1 = toS1.toString _ 21 | 22 | val val_toS2 = toS2.toString _ 23 | var var_toS2 = toS2.toString _ 24 | def def_toS2 = toS2.toString _ 25 | lazy val lzy_toS2 = toS2.toString _ 26 | 27 | val val_toS3 = toS3.toString _ 28 | var var_toS3 = toS3.toString _ 29 | def def_toS3 = toS3.toString _ 30 | lazy val lzy_toS3 = toS3.toString _ 31 | 32 | val val_toS4 = toS4.toString _ 33 | var var_toS4 = toS4.toString _ 34 | def def_toS4 = toS4.toString _ 35 | lazy val lzy_toS4 = toS4.toString _ 36 | } 37 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/NullaryHashHash.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.NullaryHashHash 3 | */ 4 | package fix.scala213 5 | 6 | object NullaryHashHash { 7 | ("": Any).## 8 | ("": AnyRef).## 9 | ("": Object).## 10 | ("": String).## 11 | "".## 12 | } 13 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/ParensAroundLambda.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ParensAroundLambda 3 | */ 4 | package fix.scala213 5 | 6 | abstract class ParensAroundLambda { 7 | Nil.foreach { x: Nothing => } // fix 8 | Nil.foreach { (x: Nothing) => } // keep 9 | val f: String => Unit = (s: String) => () // keep 10 | Map("a" -> 1).map { x: (String, Int) => // fix 11 | ??? 12 | } 13 | 14 | Seq(1).map { i: Int => // fix 15 | i + 1 16 | } 17 | Seq(1).map { i => i + 1 } // keep 18 | 19 | Seq(1).foreach { implicit i: Int => } // keep 20 | } 21 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/ScalaSeq.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ScalaSeq 3 | fix.scala213.ScalaSeq = { 4 | paramType = "sc.Seq", 5 | paramImport = "scala.{collection => sc}", 6 | otherType = "sci.Seq", 7 | otherImport = "scala.collection.{immutable => sci}" 8 | } 9 | */ 10 | package fix.scala213 11 | 12 | class ScalaSeq { 13 | def f(a: Seq[Int], b: List[Seq[Int]]): Seq[Any] = List(a, b) 14 | } 15 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/Varargs.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.Varargs 3 | */ 4 | package fix.scala213 5 | 6 | class Varargs { 7 | def bar = List(List(1,2,3): _*) 8 | def baz = List(scala.Seq(1,2,3): _*) 9 | // TODO 10 | // def biz = List(scala.collection.mutable.ArrayBuffer(1,2): _*) 11 | } 12 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/ApplyType.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class ApplyType { 7 | object Props { def apply[A]() = this } 8 | object Props2 { def apply[A, B]() = this } 9 | object Nullary { def apply[A] = this } 10 | 11 | Props.apply[Int] // fix 12 | Props[Int] // fix 13 | Props[Int]() // keep 14 | 15 | Props2.apply[Int, String] // fix 16 | Props2[Int, String] // fix 17 | 18 | Nullary[Int] // keep 19 | } 20 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/Foo.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class Foo { 7 | cm.foo() 8 | cm.foo 9 | cp.foo() 10 | cp.foo 11 | } 12 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/Implicit.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class Implicit { 7 | imp // fix 8 | imp() // keep 9 | } 10 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/Infix.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class Infix { 7 | //this meth () // no point, parses as "this.meth(())" which doesn't typecheck 8 | 9 | qual id qual 10 | qual id qual() 11 | qual() id qual 12 | qual() id qual() 13 | 14 | qual id (qual) 15 | qual id ((qual)) 16 | qual id (((qual))) 17 | 18 | qual id[Any] qual 19 | } 20 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/NonNullary.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class NonNullary { 7 | meth 8 | meth() 9 | 10 | self.meth 11 | self.meth() 12 | 13 | qual.meth 14 | qual.meth() 15 | 16 | qual().meth 17 | qual().meth() 18 | 19 | 20 | val _ = meth _ 21 | 22 | 23 | ???.getClass() 24 | ???.getClass 25 | 26 | any.getClass() 27 | any.getClass 28 | 29 | ref.getClass() 30 | ref.getClass 31 | 32 | obj.getClass() 33 | obj.getClass 34 | 35 | str.getClass() 36 | str.getClass 37 | 38 | 39 | ???.hashCode() 40 | ???.hashCode 41 | 42 | any.hashCode() 43 | any.hashCode 44 | 45 | ref.hashCode() 46 | ref.hashCode 47 | 48 | obj.hashCode() 49 | obj.hashCode 50 | 51 | str.hashCode() 52 | str.hashCode 53 | 54 | 55 | cm.run() 56 | cm.run 57 | cp.run() 58 | cp.run 59 | } 60 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/Nullary.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class Nullary { 7 | prop 8 | self.prop 9 | qual.prop 10 | qual().prop 11 | 12 | ???.## 13 | any.## 14 | ref.## 15 | obj.## 16 | str.## 17 | 18 | ???.asInstanceOf[Int] 19 | any.asInstanceOf[Int] 20 | ref.asInstanceOf[Int] 21 | obj.asInstanceOf[Int] 22 | str.asInstanceOf[Int] 23 | 24 | ???.isInstanceOf[Int] 25 | any.isInstanceOf[Int] 26 | ref.isInstanceOf[Int] 27 | obj.isInstanceOf[Int] 28 | str.isInstanceOf[Int] 29 | } 30 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/Override.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | // ExplicitNonNullaryApply, when methods are overridden 7 | class Override { 8 | class Meth { def m2p() = "" } 9 | class Prop { def p2m = "" } 10 | 11 | class Meth2Prop extends Meth { override def m2p = "" } 12 | class Prop2Meth extends Prop { override def p2m() = "" } 13 | 14 | val meth2prop = new Meth2Prop 15 | val prop2meth = new Prop2Meth 16 | 17 | meth2prop.m2p() 18 | meth2prop.m2p // <- should call with parens 19 | 20 | prop2meth.p2m() 21 | prop2meth.p2m // <- should call with parens 22 | } 23 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/Postfix.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | import scala.language.postfixOps 7 | 8 | class Postfix { 9 | self meth; 10 | //self meth(); // won't typecheck - parsed as self.meth(()) 11 | 12 | 13 | qual meth; 14 | qual() meth; 15 | 16 | //qual meth(); // won't typecheck - parsed as qual.meth(()) 17 | //qual() meth(); // won't typecheck - parsed as qual().meth(()) 18 | } 19 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/ToString.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.ExplicitNonNullaryApply 3 | */ 4 | package fix.scala213.explicitNonNullaryApply 5 | 6 | class ToString { 7 | ???.toString() 8 | ???.toString 9 | 10 | any.toString() 11 | any.toString 12 | 13 | ref.toString() 14 | ref.toString 15 | 16 | obj.toString() 17 | obj.toString 18 | 19 | str.toString() 20 | str.toString 21 | 22 | cm.toString() 23 | cm.toString 24 | cp.toString() 25 | cp.toString 26 | 27 | vcm.toString() 28 | vcm.toString 29 | vcp.toString() 30 | vcp.toString 31 | 32 | ccm.toString() 33 | ccm.toString 34 | ccp.toString() 35 | ccp.toString 36 | 37 | vccm.toString() 38 | vccm.toString 39 | vccp.toString() 40 | vccp.toString 41 | } 42 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/explicitNonNullaryApply/package.scala: -------------------------------------------------------------------------------- 1 | /**/ 2 | package fix.scala213 3 | 4 | // Definitions to test ExplicitNonNullaryApply against 5 | package object explicitNonNullaryApply { 6 | def self = this 7 | def qual() = this 8 | 9 | def prop = "" 10 | def meth() = "" 11 | 12 | def id[A](x: A) = x 13 | 14 | def imp()(implicit z: DummyImplicit) = "" 15 | 16 | val any: Any = "" 17 | val ref: AnyRef = "" 18 | val obj: Object = "" 19 | val str: String = "" 20 | 21 | val cm: CM = new CM() 22 | val cp: CP = new CP() 23 | val vcm: VCM = new VCM("") 24 | val vcp: VCP = new VCP("") 25 | val ccm: CCM = CCM() 26 | val ccp: CCP = CCP() 27 | val vccm: VCCM = VCCM("") 28 | val vccp: VCCP = VCCP("") 29 | } 30 | 31 | package explicitNonNullaryApply { 32 | class CM extends Runnable with Fooable { override def toString() = ""; def run() = (); def foo() = "" } 33 | class CP extends Runnable with Fooable { override def toString = ""; def run = (); def foo = "" } 34 | 35 | case class CCM() extends Runnable with Fooable { override def toString() = ""; def run() = (); def foo() = "" } 36 | case class CCP() extends Runnable with Fooable { override def toString = ""; def run = (); def foo = "" } 37 | 38 | class VCM(val x: String) extends AnyVal { override def toString() = "" } 39 | class VCP(val x: String) extends AnyVal { override def toString = "" } 40 | 41 | case class VCCM(x: String) extends AnyVal { override def toString() = "" } 42 | case class VCCP(x: String) extends AnyVal { override def toString = "" } 43 | } 44 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/nullaryOverride/ImplicitAsNullary.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.NullaryOverride 3 | */ 4 | package fix.scala213.nullaryOverride 5 | 6 | import scala.reflect.ClassTag 7 | 8 | object ImplicitAsNullary { 9 | // bug found when rewrite akka 10 | trait CommandResult[Command, Event, State] { 11 | def eventOfType[E <: Event : ClassTag]: E 12 | } 13 | class CommandResultImpl[Command, Event, State, Reply] extends CommandResult[Command, Event, State] { 14 | // must not add `()` 15 | override def eventOfType[E <: Event: ClassTag]: E = ??? 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/nullaryOverride/Local.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.NullaryOverride 3 | */ 4 | package fix.scala213.nullaryOverride 5 | 6 | object Local { 7 | trait Actor { // bug found when rewrite akka 8 | def postStop(): Unit = () 9 | def prop: Unit 10 | } 11 | 12 | def local() = { 13 | new Actor { // rewrite 14 | override def postStop: Unit = ??? 15 | def prop() = ??? 16 | } 17 | 18 | new Actor { // keep 19 | override def postStop(): Unit = ??? 20 | def prop = ??? 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /input/src/test/scala/fix/scala213/nullaryOverride/NullaryOverride.scala: -------------------------------------------------------------------------------- 1 | /* 2 | rule = fix.scala213.NullaryOverride 3 | */ 4 | package fix.scala213.nullaryOverride 5 | 6 | import scala.annotation.unchecked.uncheckedVariance 7 | 8 | trait ActorRef[-T] { 9 | def unsafeUpcast[U >: T @uncheckedVariance]: ActorRef[U] 10 | } 11 | 12 | trait ActorRefImpl[-T] extends ActorRef[T] { 13 | // must not add `()` to `unsafeUpcast()[U...` 14 | final override def unsafeUpcast[U >: T @uncheckedVariance]: ActorRef[U] = this.asInstanceOf[ActorRef[U]] 15 | } 16 | 17 | abstract class NullaryOverrideTest { 18 | class I1[T] extends Iterator[T] { 19 | def next: T = ??? 20 | def hasNext(): Boolean = ??? 21 | def foo(): T = ??? 22 | } 23 | 24 | class I2[T] extends Iterator[T] { 25 | def next(): T = ??? 26 | def hasNext: Boolean = ??? 27 | def foo: T = ??? 28 | } 29 | 30 | class I3 extends I1[Int] { 31 | override def hasNext(/* some comment */ 32 | ): Boolean = ??? 33 | } 34 | } 35 | 36 | class Override2 { 37 | class Meth { def m2p() = "" } 38 | trait Prop { def p2m: String } 39 | 40 | object meth2prop extends Meth { 41 | override def m2p = "" // add `()` 42 | } 43 | object prop2meth extends Prop { 44 | def p2m() = "" // remove `()` 45 | } 46 | 47 | this.meth2prop.m2p() // keep 48 | meth2prop.m2p // add `()` 49 | 50 | prop2meth.p2m() // remove `()` 51 | prop2meth.p2m // keep 52 | } 53 | -------------------------------------------------------------------------------- /output/src/test/java/fix/scala213/explicitNonNullaryApply/Fooable.java: -------------------------------------------------------------------------------- 1 | package fix.scala213.explicitNonNullaryApply; 2 | 3 | // A Java interface to test ExplicitNonNullaryApply against 4 | interface Fooable { String foo(); } 5 | -------------------------------------------------------------------------------- /output/src/test/scala-2.12/fix/scala213/Console212.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.io.StdIn 4 | object Console212 { 5 | def consoleReadBoolean = StdIn.readBoolean() 6 | def consoleReadByte = StdIn.readByte() 7 | def consoleReadChar = StdIn.readChar() 8 | def consoleReadDouble = StdIn.readDouble() 9 | def consoleReadFloat = StdIn.readFloat() 10 | def consoleReadInt = StdIn.readInt() 11 | def consoleReadLine = StdIn.readLine() 12 | def consoleReadLine(text: String, args: Any*) = StdIn.readLine(text, args: _*) 13 | def consoleReadLong = StdIn.readLong() 14 | def consoleReadShort = StdIn.readShort() 15 | def consoleReadf(format: String) = StdIn.readf(format) 16 | def consoleReadf1(format: String) = StdIn.readf1(format) 17 | def consoleReadf2(format: String) = StdIn.readf2(format) 18 | def consoleReadf3(format: String) = StdIn.readf3(format) 19 | } 20 | -------------------------------------------------------------------------------- /output/src/test/scala-2.12/fix/scala213/NullaryHashHash212.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | object NullaryHashHash212 { 4 | ("": Any).## 5 | ("": AnyRef).## 6 | ("": Object).## 7 | ("": String).## 8 | "".## 9 | } 10 | -------------------------------------------------------------------------------- /output/src/test/scala-2.12/fix/scala213/Varargs212.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | class Varargs212 { 4 | def foo(xs: scala.collection.Seq[String]) = List(xs.toSeq: _*) 5 | } 6 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/Any2StringAdd.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213 3 | 4 | abstract class Any2StringAdd { 5 | // Strings: leave as-is, both literal strings and non-literal 6 | def s = "bob" 7 | def str1 = s + s 8 | def str2 = s + "bob" 9 | def str3 = "bob" + s 10 | def str4 = "bob" + "fred" 11 | 12 | // Non-strings: wrap with String.valueOf 13 | def n: Any = null 14 | def nil = String.valueOf(Nil) + s 15 | def none = String.valueOf(None) + s 16 | def Null = String.valueOf(n) + s 17 | 18 | // Non-string, generic type: wrap with String.valueOf 19 | type A 20 | def x: A 21 | def generic = String.valueOf(x) + "bob" 22 | 23 | // Numeric primitives: add toString 24 | def byte = 1.toByte 25 | def short = 1.toShort 26 | def char = 'a' 27 | def int = 1 28 | def long = 1L 29 | def float = 1.0F 30 | def double = 1.0 31 | // 32 | def byte1 = "" + byte + s 33 | def byte2 = byte + byte 34 | def short1 = "" + short + s 35 | def short2 = short + short 36 | def char1 = "" + char + s 37 | def char2 = char + char 38 | def int1 = "" + int + s 39 | def int2 = int + int 40 | def long1 = "" + long + s 41 | def long2 = long + long 42 | def float1 = "" + float + s 43 | def float2 = float + float 44 | def double1 = "" + double + s 45 | def double2 = double + double 46 | 47 | // Boolean: wrap with String.valueOf (there's no + on Boolean, AFAICT) 48 | def bool = true 49 | def bool1 = String.valueOf(bool) + s 50 | 51 | // Scala's Unit primitive: wrap with String.valueOf (there's no + on Unit, AFAICT) 52 | def unit = () 53 | def unit1 = String.valueOf(unit) + s 54 | 55 | // Custom value types: wrap with String.valueOf (adding toString would be preferable) 56 | import Any2StringAdd.Name 57 | //val name: Name = null // type mismatch; found: Null(null), required: Name 58 | val name = new Name(null) 59 | def name1 = String.valueOf(name) + s 60 | 61 | // With infix operators, make sure to use parens 62 | def parens1 = String.valueOf(Nil ++ Nil) + s 63 | def parens2 = "" + (int + int) + s 64 | def parens3 = String.valueOf({Nil ++ Nil}) + s 65 | } 66 | 67 | object Any2StringAdd { 68 | final class Name(val value: String) extends AnyVal { override def toString = value } 69 | } 70 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/ConstructorProcedureSyntax.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | class ConstructorProcedureSyntax(i: Long) { 4 | def this(a: Int) = this(0L + 1) 5 | def this(a: Int, b: Int) = 6 | this(0L) 7 | def f/**/{ 8 | println("f") 9 | class X{ 10 | def this(a:Int) = this() 11 | def this (j: String)= this(j.toInt) 12 | } 13 | } 14 | def this() ={this(1L) 15 | println("hi") 16 | class X{ 17 | def this (j: String) = // comment } 18 | this() 19 | def this (j: Int) = // comment { 20 | this() 21 | } 22 | println(".") 23 | } 24 | def this(i: Float) = /**/ 25 | // 26 | /***/ 27 | this(i.toLong) 28 | 29 | def this(i: String) = this(i.toLong) 30 | } 31 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/Core.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.compat.Platform 4 | 5 | object Core { 6 | def eol1 = "Hello World!" + System.lineSeparator 7 | def eol2 = s"Hello World!${System.lineSeparator}" 8 | def elo2b = s"Hello World!${{System.lineSeparator}}" 9 | def eol3 = "Hello World!" + System.lineSeparator 10 | def eol4 = s"Hello World!${System.lineSeparator}" 11 | def eol5 = "Hello World!" + System.lineSeparator 12 | def eol6 = s"Hello World!${System.lineSeparator}" 13 | 14 | def currentTimeMillis1 = System.currentTimeMillis 15 | def currentTimeMillis2 = System.currentTimeMillis 16 | def currentTimeMillis3 = System.currentTimeMillis 17 | def currentTimeMillis4 = s"now: ${System.currentTimeMillis}" 18 | 19 | def arrayCopy1() = System.arraycopy(null, 0, null, 0, 0) 20 | def arrayCopy2() = System.arraycopy(null, 0, null, 0, 0) 21 | def arrayCopy3() = System.arraycopy(null, 0, null, 0, 0) 22 | 23 | def arrow1: PartialFunction[Any, String] = { 24 | case 0 => "zero" 25 | case 1 => "one" 26 | case 2 => "two" 27 | case n: Int => "ginormous" 28 | case "⇒" => "?" 29 | } 30 | def arrow2(f: Int => String) = f(1) 31 | def arrow3 = { 32 | import scala.{PartialFunction => ?=>} 33 | def f: Int ?=> String = { 34 | case 1 => "one" 35 | } 36 | } 37 | def arrow4 = Some(1).map(x => x.toString) 38 | 39 | def singleArrow1: (Int, Int) = 1 -> 1 40 | def singleArrow2: (Int, Int) = 1.->(1) 41 | 42 | def symbolLiteral0 = Symbol("foo") 43 | def symbolLiteral1 = Symbol("foo") 44 | } 45 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/FinalObject.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | object FinalObject { 4 | // Add code that needs fixing here. 5 | object A { 6 | def f = { 7 | object A 8 | println() 9 | } 10 | } 11 | case object B { 12 | val x = 1 13 | case object A {} 14 | final class B 15 | } 16 | final class C {} 17 | final case class D(x: Int) 18 | final val x = 1 19 | } 20 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/MultiArgInfix.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | object MultiArgInfix { 4 | trait PipeToSupport { 5 | def to(recipient: Int, sender: Int): Unit 6 | def foo[A](a: A, i: String): Unit 7 | } 8 | 9 | def p: PipeToSupport = ??? 10 | 11 | p.to(1, 2) 12 | p.to(3, 4 + 5) 13 | 14 | p.foo(1, "2") 15 | p.foo[Long](3, "" + 4) 16 | } 17 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/NoAutoApply.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213 3 | 4 | object NoAutoApply { 5 | object buz { 6 | override def toString(): String = "" 7 | def empty[T]() = List.empty[T] 8 | } 9 | val x: Iterator[Int] = ??? 10 | def foo() = println(1) 11 | def bar = 32 12 | foo() 13 | println(foo()) 14 | foo() 15 | bar 16 | x.next() 17 | class buz() { 18 | def qux() = List(1) 19 | } 20 | new buz().qux().toIterator.next() 21 | new java.util.ArrayList[Int]().toString 22 | val builder = List.newBuilder[Int] 23 | builder.result() 24 | //builder result () 25 | builder.result() 26 | fix.scala213.NoAutoApply.buz.empty[String]() 27 | var opt: Option[() => Int] = None 28 | opt = None 29 | println(1.toString) 30 | println(buz.toString) // not inserted 31 | List(builder) map (_.result()) 32 | builder.## 33 | def lzy(f: => Int) = { 34 | var k = f _ 35 | k = () => 3 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/NullaryEtaExpansion.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | class NullaryEtaExpansion { 4 | val str1 = "" 5 | def prop = "" 6 | def meth() = "" 7 | def void(x: Any) = "" 8 | 9 | def def_str1 = () => str1 // rewrite 10 | def def_prop = () => prop // rewrite 11 | def def_meth = meth _ // leave 12 | def def_idty = void _ // leave 13 | 14 | val val_str1 = () => str1 // rewrite 15 | val val_prop = () => prop // rewrite 16 | val val_meth = meth _ // leave 17 | val val_idty = void _ // leave 18 | 19 | var var_str1 = () => str1 // rewrite 20 | var var_prop = () => prop // rewrite 21 | var var_meth = meth _ // leave 22 | var var_idty = void _ // leave 23 | 24 | lazy val lzy_str1 = () => str1 // rewrite 25 | lazy val lzy_prop = () => prop // rewrite 26 | lazy val lzy_meth = meth _ // leave 27 | lazy val lzy_idty = void _ // leave 28 | 29 | def lzy(f: => Int) = { 30 | val k = () => f 31 | k() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/NullaryEtaExpansionJava.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | class NullaryEtaExpansionJava { 4 | class ToS1 { override def toString(): String = "" } 5 | class ToS2 { override def toString: String = "" } 6 | class ToS3 { override val toString: String = "" } 7 | class ToS4 { override lazy val toString: String = "" } 8 | 9 | val toS1 = new ToS1 10 | val toS2 = new ToS2 11 | val toS3 = new ToS3 12 | val toS4 = new ToS4 13 | 14 | val val_toS1 = toS1.toString _ 15 | var var_toS1 = toS1.toString _ 16 | def def_toS1 = toS1.toString _ 17 | lazy val lzy_toS1 = toS1.toString _ 18 | 19 | val val_toS2 = toS2.toString _ 20 | var var_toS2 = toS2.toString _ 21 | def def_toS2 = toS2.toString _ 22 | lazy val lzy_toS2 = toS2.toString _ 23 | 24 | val val_toS3 = () => toS3.toString 25 | var var_toS3 = () => toS3.toString 26 | def def_toS3 = () => toS3.toString 27 | lazy val lzy_toS3 = () => toS3.toString 28 | 29 | val val_toS4 = () => toS4.toString 30 | var var_toS4 = () => toS4.toString 31 | def def_toS4 = () => toS4.toString 32 | lazy val lzy_toS4 = () => toS4.toString 33 | } 34 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/NullaryHashHash.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | object NullaryHashHash { 4 | ("": Any).## 5 | ("": AnyRef).## 6 | ("": Object).## 7 | ("": String).## 8 | "".## 9 | } 10 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/ParensAroundLambda.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | abstract class ParensAroundLambda { 4 | Nil.foreach { (x: Nothing) => } // fix 5 | Nil.foreach { (x: Nothing) => } // keep 6 | val f: String => Unit = (s: String) => () // keep 7 | Map("a" -> 1).map { (x: (String, Int)) => // fix 8 | ??? 9 | } 10 | 11 | Seq(1).map { (i: Int) => // fix 12 | i + 1 13 | } 14 | Seq(1).map { i => i + 1 } // keep 15 | 16 | Seq(1).foreach { implicit i: Int => } // keep 17 | } 18 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/ScalaSeq.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.{collection => sc} 4 | import scala.collection.{immutable => sci} 5 | class ScalaSeq { 6 | def f(a: sc.Seq[Int], b: List[sc.Seq[Int]]): sci.Seq[Any] = List(a, b) 7 | } 8 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/Varargs.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | class Varargs { 4 | def bar = List(List(1,2,3): _*) 5 | def baz = List(scala.Seq(1,2,3): _*) 6 | // TODO 7 | // def biz = List(scala.collection.mutable.ArrayBuffer(1,2): _*) 8 | } 9 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/ApplyType.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class ApplyType { 5 | object Props { def apply[A]() = this } 6 | object Props2 { def apply[A, B]() = this } 7 | object Nullary { def apply[A] = this } 8 | 9 | Props.apply[Int]() // fix 10 | Props[Int]() // fix 11 | Props[Int]() // keep 12 | 13 | Props2.apply[Int, String]() // fix 14 | Props2[Int, String]() // fix 15 | 16 | Nullary[Int] // keep 17 | } 18 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/Foo.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class Foo { 5 | cm.foo() 6 | cm.foo 7 | cp.foo() 8 | cp.foo 9 | } 10 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/Implicit.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class Implicit { 5 | imp() // fix 6 | imp() // keep 7 | } 8 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/Infix.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class Infix { 5 | //this meth () // no point, parses as "this.meth(())" which doesn't typecheck 6 | 7 | qual() id qual() 8 | qual() id qual() 9 | qual() id qual() 10 | qual() id qual() 11 | 12 | qual() id (qual()) 13 | qual() id ((qual())) 14 | qual() id (((qual()))) 15 | 16 | qual() id[Any] qual() 17 | } 18 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/NonNullary.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class NonNullary { 5 | meth() 6 | meth() 7 | 8 | self.meth() 9 | self.meth() 10 | 11 | qual().meth() 12 | qual().meth() 13 | 14 | qual().meth() 15 | qual().meth() 16 | 17 | 18 | val _ = meth _ 19 | 20 | 21 | ???.getClass() 22 | ???.getClass 23 | 24 | any.getClass() 25 | any.getClass 26 | 27 | ref.getClass() 28 | ref.getClass 29 | 30 | obj.getClass() 31 | obj.getClass 32 | 33 | str.getClass() 34 | str.getClass 35 | 36 | 37 | ???.hashCode() 38 | ???.hashCode 39 | 40 | any.hashCode() 41 | any.hashCode 42 | 43 | ref.hashCode() 44 | ref.hashCode 45 | 46 | obj.hashCode() 47 | obj.hashCode 48 | 49 | str.hashCode() 50 | str.hashCode 51 | 52 | 53 | cm.run() 54 | cm.run 55 | cp.run() 56 | cp.run 57 | } 58 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/Nullary.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class Nullary { 5 | prop 6 | self.prop 7 | qual().prop 8 | qual().prop 9 | 10 | ???.## 11 | any.## 12 | ref.## 13 | obj.## 14 | str.## 15 | 16 | ???.asInstanceOf[Int] 17 | any.asInstanceOf[Int] 18 | ref.asInstanceOf[Int] 19 | obj.asInstanceOf[Int] 20 | str.asInstanceOf[Int] 21 | 22 | ???.isInstanceOf[Int] 23 | any.isInstanceOf[Int] 24 | ref.isInstanceOf[Int] 25 | obj.isInstanceOf[Int] 26 | str.isInstanceOf[Int] 27 | } 28 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/Override.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | // ExplicitNonNullaryApply, when methods are overridden 5 | class Override { 6 | class Meth { def m2p() = "" } 7 | class Prop { def p2m = "" } 8 | 9 | class Meth2Prop extends Meth { override def m2p = "" } 10 | class Prop2Meth extends Prop { override def p2m() = "" } 11 | 12 | val meth2prop = new Meth2Prop 13 | val prop2meth = new Prop2Meth 14 | 15 | meth2prop.m2p() 16 | meth2prop.m2p() // <- should call with parens 17 | 18 | prop2meth.p2m() 19 | prop2meth.p2m() // <- should call with parens 20 | } 21 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/Postfix.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | import scala.language.postfixOps 5 | 6 | class Postfix { 7 | self.meth(); 8 | //self meth(); // won't typecheck - parsed as self.meth(()) 9 | 10 | 11 | qual().meth(); 12 | qual().meth(); 13 | 14 | //qual meth(); // won't typecheck - parsed as qual.meth(()) 15 | //qual() meth(); // won't typecheck - parsed as qual().meth(()) 16 | } 17 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/ToString.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213.explicitNonNullaryApply 3 | 4 | class ToString { 5 | ???.toString() 6 | ???.toString 7 | 8 | any.toString() 9 | any.toString 10 | 11 | ref.toString() 12 | ref.toString 13 | 14 | obj.toString() 15 | obj.toString 16 | 17 | str.toString() 18 | str.toString 19 | 20 | cm.toString() 21 | cm.toString 22 | cp.toString() 23 | cp.toString 24 | 25 | vcm.toString() 26 | vcm.toString 27 | vcp.toString() 28 | vcp.toString 29 | 30 | ccm.toString() 31 | ccm.toString 32 | ccp.toString() 33 | ccp.toString 34 | 35 | vccm.toString() 36 | vccm.toString 37 | vccp.toString() 38 | vccp.toString 39 | } 40 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/explicitNonNullaryApply/package.scala: -------------------------------------------------------------------------------- 1 | 2 | package fix.scala213 3 | 4 | // Definitions to test ExplicitNonNullaryApply against 5 | package object explicitNonNullaryApply { 6 | def self = this 7 | def qual() = this 8 | 9 | def prop = "" 10 | def meth() = "" 11 | 12 | def id[A](x: A) = x 13 | 14 | def imp()(implicit z: DummyImplicit) = "" 15 | 16 | val any: Any = "" 17 | val ref: AnyRef = "" 18 | val obj: Object = "" 19 | val str: String = "" 20 | 21 | val cm: CM = new CM() 22 | val cp: CP = new CP() 23 | val vcm: VCM = new VCM("") 24 | val vcp: VCP = new VCP("") 25 | val ccm: CCM = CCM() 26 | val ccp: CCP = CCP() 27 | val vccm: VCCM = VCCM("") 28 | val vccp: VCCP = VCCP("") 29 | } 30 | 31 | package explicitNonNullaryApply { 32 | class CM extends Runnable with Fooable { override def toString() = ""; def run() = (); def foo() = "" } 33 | class CP extends Runnable with Fooable { override def toString = ""; def run = (); def foo = "" } 34 | 35 | case class CCM() extends Runnable with Fooable { override def toString() = ""; def run() = (); def foo() = "" } 36 | case class CCP() extends Runnable with Fooable { override def toString = ""; def run = (); def foo = "" } 37 | 38 | class VCM(val x: String) extends AnyVal { override def toString() = "" } 39 | class VCP(val x: String) extends AnyVal { override def toString = "" } 40 | 41 | case class VCCM(x: String) extends AnyVal { override def toString() = "" } 42 | case class VCCP(x: String) extends AnyVal { override def toString = "" } 43 | } 44 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/nullaryOverride/ImplicitAsNullary.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213.nullaryOverride 2 | 3 | import scala.reflect.ClassTag 4 | 5 | object ImplicitAsNullary { 6 | // bug found when rewrite akka 7 | trait CommandResult[Command, Event, State] { 8 | def eventOfType[E <: Event : ClassTag]: E 9 | } 10 | class CommandResultImpl[Command, Event, State, Reply] extends CommandResult[Command, Event, State] { 11 | // must not add `()` 12 | override def eventOfType[E <: Event: ClassTag]: E = ??? 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/nullaryOverride/Local.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213.nullaryOverride 2 | 3 | object Local { 4 | trait Actor { // bug found when rewrite akka 5 | def postStop(): Unit = () 6 | def prop: Unit 7 | } 8 | 9 | def local() = { 10 | new Actor { // rewrite 11 | override def postStop(): Unit = ??? 12 | def prop = ??? 13 | } 14 | 15 | new Actor { // keep 16 | override def postStop(): Unit = ??? 17 | def prop = ??? 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /output/src/test/scala/fix/scala213/nullaryOverride/NullaryOverride.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213.nullaryOverride 2 | 3 | import scala.annotation.unchecked.uncheckedVariance 4 | 5 | trait ActorRef[-T] { 6 | def unsafeUpcast[U >: T @uncheckedVariance]: ActorRef[U] 7 | } 8 | 9 | trait ActorRefImpl[-T] extends ActorRef[T] { 10 | // must not add `()` to `unsafeUpcast()[U...` 11 | final override def unsafeUpcast[U >: T @uncheckedVariance]: ActorRef[U] = this.asInstanceOf[ActorRef[U]] 12 | } 13 | 14 | abstract class NullaryOverrideTest { 15 | class I1[T] extends Iterator[T] { 16 | def next(): T = ??? 17 | def hasNext: Boolean = ??? 18 | def foo(): T = ??? 19 | } 20 | 21 | class I2[T] extends Iterator[T] { 22 | def next(): T = ??? 23 | def hasNext: Boolean = ??? 24 | def foo: T = ??? 25 | } 26 | 27 | class I3 extends I1[Int] { 28 | override def hasNext: Boolean = ??? 29 | } 30 | } 31 | 32 | class Override2 { 33 | class Meth { def m2p() = "" } 34 | trait Prop { def p2m: String } 35 | 36 | object meth2prop extends Meth { 37 | override def m2p() = "" // add `()` 38 | } 39 | object prop2meth extends Prop { 40 | def p2m = "" // remove `()` 41 | } 42 | 43 | this.meth2prop.m2p() // keep 44 | meth2prop.m2p() // add `()` 45 | 46 | prop2meth.p2m // remove `()` 47 | prop2meth.p2m // keep 48 | } 49 | -------------------------------------------------------------------------------- /project/ScalaNightlyPlugin.scala: -------------------------------------------------------------------------------- 1 | import sbt._, Keys._ 2 | 3 | object ScalaNightlyPlugin extends AutoPlugin { 4 | val BinCompatV = """(\d+)\.(\d+)\.(\d+)(-\w+)??(-bin-.*)?""".r 5 | val isScalaNightly = sys.env.get("TRAVIS_SCALA_VERSION").contains("2.12.next") 6 | def ifNightly(ss: SettingsDefinition*) = if (isScalaNightly) Def.settings(ss: _*) else Nil 7 | 8 | override def buildSettings = ifNightly( 9 | resolvers += "scala-integration" at "https://scala-ci.typesafe.com/artifactory/scala-integration/", 10 | ) 11 | 12 | def bootstrapSettings = ifNightly( 13 | scalaVersion := { 14 | val props = new java.util.Properties 15 | val url1 = url("https://raw.github.com/scala/community-build/2.12.x/nightly.properties") 16 | sbt.io.Using.urlInputStream(url1)(props.load(_)) 17 | val sv = props.getProperty("nightly").ensuring(_ != null) 18 | sLog.value.info(s"Using Scala nightly version $sv") 19 | sv 20 | }, 21 | ) 22 | 23 | override def projectSettings = ifNightly( 24 | scalaVersion := ((LocalRootProject / scalaVersion).value match { 25 | case BinCompatV(x, y, z, _, _) => s"$x.$y.${z.toInt - 1}" 26 | case sv => sv 27 | }), 28 | scalaInstance := { 29 | val si = (LocalRootProject / scalaInstance).value 30 | new sbt.internal.inc.ScalaInstance( 31 | scalaVersion.value, 32 | si.loader, si.loaderCompilerOnly, si.loaderLibraryOnly, 33 | si.libraryJars, si.compilerJars, si.allJars, 34 | si.explicitActual 35 | ) 36 | } 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.5.5 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.31") 2 | 3 | addSbtPlugin("com.sandinh" % "sbt-devops-oss" % "5.0.6") 4 | -------------------------------------------------------------------------------- /rewrites/src/main/resources/META-INF/services/scalafix.v1.Rule: -------------------------------------------------------------------------------- 1 | fix.scala213.Any2StringAdd 2 | fix.scala213.Core 3 | fix.scala213.ExplicitNonNullaryApply 4 | fix.scala213.ExplicitNullaryEtaExpansion 5 | fix.scala213.NullaryHashHash 6 | fix.scala213.ScalaSeq 7 | fix.scala213.Varargs 8 | fix.scala213.ConstructorProcedureSyntax 9 | fix.scala213.ParensAroundLambda 10 | fix.scala213.NullaryOverride 11 | fix.scala213.DottyMigrate 12 | fix.explicittypes.ExplicitImplicitTypes 13 | fix.scala213.MultiArgInfix 14 | fix.scala213.FinalObject 15 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/explicittypes/ExplicitImplicitTypes.scala: -------------------------------------------------------------------------------- 1 | package fix.explicittypes 2 | 3 | import impl.CompilerDependentRule 4 | import metaconfig.Configured 5 | import scalafix.internal.rule.{TypePrinter, CompilerTypePrinter, ExplicitResultTypesConfig} 6 | import scalafix.internal.v1.LazyValue 7 | import scalafix.patch.Patch 8 | import scalafix.util.TokenOps 9 | import scalafix.v1._ 10 | 11 | import scala.meta._ 12 | import scala.meta.contrib._ 13 | import scala.meta.internal.pc.ScalafixGlobal 14 | 15 | import ExplicitImplicitTypes._ 16 | 17 | /** @see [[scalafix.internal.rule.ExplicitResultTypes]] */ 18 | final class ExplicitImplicitTypes( 19 | config: ExplicitResultTypesConfig, 20 | global: LazyValue[ScalafixGlobal], 21 | ) extends CompilerDependentRule(global, "ExplicitImplicitTypes") { 22 | def this() = this(ExplicitResultTypesConfig.default, LazyValue.later(() => ScalafixGlobal.newCompiler(Nil, Nil, Map.empty))) 23 | 24 | override def withConfiguration(config: Configuration): Configured[Rule] = { 25 | val c = interestedExplicitResultTypesConfig(config) 26 | val newGlobal = LazyValue.later { () => 27 | ScalafixGlobal.newCompiler(config.scalacClasspath, config.scalacOptions, c.symbolReplacements) 28 | } 29 | Configured.ok(new ExplicitImplicitTypes(c, newGlobal)) 30 | } 31 | 32 | def unsafeFix()(implicit doc: SemanticDocument): Patch = { 33 | lazy val types = new CompilerTypePrinter(global.value, config) 34 | doc.tree.collect(collector(types)).asPatch 35 | } 36 | } 37 | 38 | object ExplicitImplicitTypes { 39 | def interestedExplicitResultTypesConfig(config: Configuration): ExplicitResultTypesConfig = { 40 | def symbolReplacements(name: String) = 41 | config.conf.dynamic.selectDynamic(name).symbolReplacements 42 | .as[Map[String, String]] 43 | .getOrElse(Map.empty) 44 | 45 | val replacements = Seq( 46 | "ExplicitResultTypes", 47 | "ExplicitImplicitTypes", 48 | "DottyMigrate" 49 | ).map(symbolReplacements).reduce(_ ++ _) 50 | 51 | // `CompilerTypePrinter` only use these two fields of `ExplicitResultTypesConfig` 52 | ExplicitResultTypesConfig( 53 | rewriteStructuralTypesToNamedSubclass = true, 54 | symbolReplacements = replacements 55 | ) 56 | } 57 | 58 | def collector(types: => TypePrinter)(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = { 59 | case t @ Defn.Val(mods, Pat.Var(name) :: Nil, None, body) 60 | if isRuleCandidate(t, name, mods, body) => 61 | fixDefinition(t, body, name, types) 62 | 63 | case t @ Defn.Var(mods, Pat.Var(name) :: Nil, None, Some(body)) 64 | if isRuleCandidate(t, name, mods, body) => 65 | fixDefinition(t, body, name, types) 66 | 67 | case t @ Defn.Def(mods, name, _, _, None, body) 68 | if isRuleCandidate(t, name, mods, body) => 69 | fixDefinition(t, body, name, types) 70 | } 71 | 72 | // Don't explicitly annotate vals when the right-hand body is a single call 73 | // to `implicitly`. Prevents ambiguous implicit. Not annotating in such cases, 74 | // this a common trick employed implicit-heavy code to workaround SI-2712. 75 | // Context: https://gitter.im/typelevel/cats?at=584573151eb3d648695b4a50 76 | private def isImplicitly(term: Term): Boolean = term match { 77 | case Term.ApplyType(Term.Name("implicitly"), _) => true 78 | case _ => false 79 | } 80 | 81 | def isRuleCandidate( 82 | defn: Defn, 83 | nm: Name, 84 | mods: Iterable[Mod], 85 | body: Term 86 | )(implicit ctx: SemanticDocument): Boolean = { 87 | 88 | def isFinalLiteralVal: Boolean = 89 | defn.is[Defn.Val] && 90 | mods.exists(_.is[Mod.Final]) && 91 | body.is[Lit] 92 | 93 | def isImplicit: Boolean = 94 | mods.exists(_.is[Mod.Implicit]) && !isImplicitly(body) 95 | 96 | def hasParentWihTemplate: Boolean = 97 | defn.parent.exists(_.is[Template]) 98 | 99 | def isLocal: Boolean = nm.symbol.isLocal && !hasParentWihTemplate 100 | 101 | isImplicit && !isFinalLiteralVal && !isLocal 102 | } 103 | 104 | def fixDefinition(defn: Defn, body: Term, name: Term.Name, types: TypePrinter)( 105 | implicit ctx: SemanticDocument 106 | ): Patch = { 107 | val lst = ctx.tokenList 108 | for { 109 | start <- defn.tokens.headOption 110 | end <- body.tokens.headOption 111 | // Left-hand side tokens in definition. 112 | // Example: `val x = ` from `val x = rhs.banana` 113 | lhsTokens = lst.slice(start, end) 114 | replace <- lhsTokens.reverseIterator.find(x => 115 | !x.is[Token.Equals] && !x.is[Trivia] 116 | ) 117 | space = { 118 | if (TokenOps.needsLeadingSpaceBeforeColon(replace)) " " 119 | else "" 120 | } 121 | defnSymbol <- name.symbol.asNonEmpty 122 | typePatch <- types.toPatch(name.pos, defnSymbol, replace, defn, space) 123 | } yield typePatch + PatchEmptyBody(body) 124 | }.asPatch.atomic 125 | } 126 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/explicittypes/PatchEmptyBody.scala: -------------------------------------------------------------------------------- 1 | package fix.explicittypes 2 | 3 | import scalafix.patch.Patch 4 | import scalafix.v1.{SemanticDocument, SymbolMatcher} 5 | 6 | import scala.meta._ 7 | 8 | /** Patch rhs of, eg `val s = Seq.empty[Int]` to `val s: Seq[Int] = Nil` */ 9 | object PatchEmptyBody { 10 | private val option = SymbolMatcher.exact("scala/Option.") 11 | private val list = SymbolMatcher.exact( 12 | "scala/package.List.", 13 | "scala/collection/immutable/List." 14 | ) 15 | private val seq = SymbolMatcher.exact( 16 | "scala/package.Seq.", 17 | "scala/collection/Seq.", 18 | "scala/collection/immutable/Seq." 19 | ) 20 | 21 | def apply(term: Term)(implicit doc: SemanticDocument): Patch = 22 | term match { 23 | case q"${option(_)}.empty[$_]" => 24 | Patch.replaceTree(term, "None") 25 | case q"${list(_)}.empty[$_]" => 26 | Patch.replaceTree(term, "Nil") 27 | case q"${seq(_)}.empty[$_]" => 28 | Patch.replaceTree(term, "Nil") 29 | case _ => 30 | Patch.empty 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scalafix.v1._ 4 | 5 | import scala.meta._ 6 | 7 | object Any2StringAdd { 8 | val any2stringaddPlusString = SymbolMatcher.exact("scala/Predef.any2stringadd#`+`().") 9 | val primitivePlusString = SymbolMatcher.exact( 10 | "scala/Byte#`+`().", 11 | "scala/Short#`+`().", 12 | "scala/Char#`+`().", 13 | "scala/Int#`+`().", 14 | "scala/Long#`+`().", 15 | "scala/Float#`+`().", 16 | "scala/Double#`+`().", 17 | ) 18 | } 19 | 20 | final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { 21 | import Any2StringAdd._ 22 | 23 | def collector(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = { 24 | case any2stringaddPlusString(Term.ApplyInfix(lhs, _, _, _)) => wrapStringValueOf(lhs) 25 | case primitivePlusString(Term.ApplyInfix(lhs, _, _, _)) => blankStringPlus(lhs) 26 | } 27 | 28 | override def fix(implicit doc: SemanticDocument): Patch = { 29 | doc.tree.collect(collector).asPatch 30 | } 31 | 32 | private def wrapStringValueOf(term: Term) = 33 | Patch.addLeft(term, "String.valueOf(") + Patch.addRight(term, ")") 34 | 35 | private def blankStringPlus(term: Term) = term match { 36 | case _: Term.Name | _: Term.Select | _: Term.Block => Patch.addLeft(term, """"" + """) 37 | case _ => Patch.addLeft(term, """"" + (""") + Patch.addRight(term, ")") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/ConstructorProcedureSyntax.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scalafix.util.Newline 4 | import scalafix.v1._ 5 | 6 | import scala.meta._ 7 | import scala.meta.classifiers.Classifier 8 | import scala.meta.tokens.Token 9 | import scala.meta.tokens.Token._ 10 | import ConstructorProcedureSyntax._ 11 | 12 | /** @see [[https://github.com/scalacenter/scalafix/blob/master/scalafix-rules/src/main/scala/scalafix/internal/rule/ProcedureSyntax.scala ProcedureSyntax]] */ 13 | class ConstructorProcedureSyntax extends SyntacticRule("fix.scala213.ConstructorProcedureSyntax") { 14 | 15 | override def description: String = 16 | "remove constructor procedure syntax `def this(..) {..}` => `def this(..) = ..`" 17 | 18 | override def fix(implicit doc: SyntacticDocument): Patch = { 19 | doc.tree.collect(collector).asPatch 20 | } 21 | } 22 | 23 | object ConstructorProcedureSyntax { 24 | val collector: PartialFunction[Tree, Patch] = { 25 | // t like this: `def this(params)` ( `= this(..)` | `= { this(..).*}` | `{ this(..).*}` ) 26 | case t: Ctor.Secondary => 27 | val tokens = t.tokens 28 | val beforeInitIdx = tokens.indexOf(t.init.tokens.head) - 1 29 | // last RightParen before init 30 | val lastRightParenIdx = tokens.lastIndexWhere(_.is[RightParen], beforeInitIdx) 31 | // if slicedTokens don't have Equals token => need patching 32 | val slicedTokens = tokens.slice(lastRightParenIdx, beforeInitIdx) 33 | slicedTokens.find(_.is[Equals]) match { 34 | case Some(_) => Patch.empty 35 | case None => 36 | val removes = 37 | if (t.stats.nonEmpty) Patch.empty 38 | else { 39 | // remove Brace pair if stats.isEmpty (constructor `t` has only one init statement `this(..)`) 40 | val leftBraceIdx = 41 | lastRightParenIdx + 42 | slicedTokens.indexWhere(_.is[LeftBrace]) // must != -1 43 | 44 | val rightBraceIdx = tokens.length - 1 45 | 46 | Patch.removeTokens( 47 | tokens.lineIfAllSpace(leftBraceIdx) ++ 48 | tokens.lineIfAllSpace(rightBraceIdx) 49 | ) 50 | } 51 | val lastRightParen = slicedTokens.head 52 | Patch.addRight(lastRightParen, " =") + removes 53 | } 54 | } 55 | 56 | implicit class TokensOps(val tokens: Tokens) extends AnyVal { 57 | /** 58 | * @return the following tokens: 59 | * {{{ 60 | * from 61 | * ↓ 62 | * def this(..) 63 | * 64 | * ↑ 65 | * to 66 | * }}} 67 | * If all tokens in the line of tokens(i) are Space|Tab 68 | * Else, return Seq(tokens(i)) */ 69 | def lineIfAllSpace(i: Int): Seq[Token] = { 70 | def t = Seq(tokens(i)) 71 | tokens.lastIndexWhere(!_.is[SpaceOrTab], i - 1) match { 72 | case -1 => t 73 | case from if !tokens(from).is[Newline] => t 74 | case from => tokens.indexWhere(!_.is[SpaceOrTab], i + 1) match { 75 | case to if to != -1 && !tokens(to).is[Newline] => t 76 | case to => tokens.slice(from, to.max(i + 1)) 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | trait SpaceOrTab 84 | object SpaceOrTab { 85 | def unapply(token: Token): Boolean = token.is[Space] || token.is[Tab] 86 | implicit def classifier[T <: Token]: Classifier[T, SpaceOrTab] = unapply 87 | } 88 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/Core.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.meta._ 4 | 5 | import scalafix.v1._ 6 | 7 | final class Core extends SemanticRule("fix.scala213.Core") { 8 | import Core._ 9 | 10 | override def fix(implicit doc: SemanticDocument) = new impl.ReplacingTraverser { 11 | val platformEOL: Replacer = { 12 | case EOL(i: Importee) => Patch.removeImportee(i) 13 | case EOL(t: Term) => replaceTree(t, "System.lineSeparator") 14 | } 15 | 16 | val platformCurrentTime: Replacer = { 17 | case currentTime(i: Importee) => Patch.removeImportee(i) 18 | case currentTime(t: Term) => replaceTree(t, "System.currentTimeMillis") 19 | } 20 | 21 | val platformArraycopy: Replacer = { 22 | case arraycopy(i: Importee) => Patch.removeImportee(i) 23 | case arraycopy(Term.Apply(t, _)) => replaceTree(t, "System.arraycopy") 24 | } 25 | 26 | val consoleRead: Replacer = { 27 | case deprecatedConsoleReadBoolean(Term.Apply(t, _)) => stdInReplace(t, "readBoolean") 28 | case deprecatedConsoleReadByte( Term.Apply(t, _)) => stdInReplace(t, "readByte") 29 | case deprecatedConsoleReadChar( Term.Apply(t, _)) => stdInReplace(t, "readChar") 30 | case deprecatedConsoleReadDouble( Term.Apply(t, _)) => stdInReplace(t, "readDouble") 31 | case deprecatedConsoleReadFloat( Term.Apply(t, _)) => stdInReplace(t, "readFloat") 32 | case deprecatedConsoleReadInt( Term.Apply(t, _)) => stdInReplace(t, "readInt") 33 | case deprecatedConsoleReadLine( Term.Apply(t, _)) => stdInReplace(t, "readLine") 34 | case deprecatedConsoleReadLine1( Term.Apply(t, _)) => stdInReplace(t, "readLine") 35 | case deprecatedConsoleReadLong( Term.Apply(t, _)) => stdInReplace(t, "readLong") 36 | case deprecatedConsoleReadShort( Term.Apply(t, _)) => stdInReplace(t, "readShort") 37 | case deprecatedConsoleReadf( Term.Apply(t, _)) => stdInReplace(t, "readf") 38 | case deprecatedConsoleReadf1( Term.Apply(t, _)) => stdInReplace(t, "readf1") 39 | case deprecatedConsoleReadf2( Term.Apply(t, _)) => stdInReplace(t, "readf2") 40 | case deprecatedConsoleReadf3( Term.Apply(t, _)) => stdInReplace(t, "readf3") 41 | } 42 | 43 | private def stdInReplace(tree: Tree, name: String): Patch = 44 | replaceTree(tree, s"StdIn.$name") + globalImports.add(importer"scala.io.StdIn") 45 | 46 | val unicodeArrows: Replacer = { 47 | case t: Case => replaceTokens(t, "⇒", "=>") 48 | case t: Type.Function => replaceTokens(t, "⇒", "=>") 49 | case t: Term.Function => replaceTokens(t, "⇒", "=>") 50 | case t: Importee => replaceTokens(t, "⇒", "=>") 51 | case arrowAssoc(t: Term.Name) => replaceTree(t, "->") 52 | } 53 | 54 | val symbolLiteral: Replacer = { 55 | case t @ Lit.Symbol(sym) => replaceTree(t, s"""Symbol("${sym.name}")""") 56 | } 57 | 58 | val replacers = List( 59 | platformEOL, 60 | platformCurrentTime, 61 | platformArraycopy, 62 | consoleRead, 63 | unicodeArrows, 64 | symbolLiteral, 65 | ) 66 | }.run(doc.tree) 67 | } 68 | 69 | object Core { 70 | private def replaceTokens(t: Tree, from: String, to: String): Patch = 71 | t.tokens.collect { case tok if tok.text == from => Patch.replaceToken(tok, to) }.asPatch 72 | 73 | private val EOL = SymbolMatcher.exact("scala/compat/Platform.EOL.") 74 | private val currentTime = SymbolMatcher.exact("scala/compat/Platform.currentTime().") 75 | private val arraycopy = SymbolMatcher.exact("scala/compat/Platform.arraycopy().") 76 | 77 | private val deprecatedConsoleReadBoolean = SymbolMatcher.exact("scala/DeprecatedConsole#readBoolean().") 78 | private val deprecatedConsoleReadByte = SymbolMatcher.exact("scala/DeprecatedConsole#readByte().") 79 | private val deprecatedConsoleReadChar = SymbolMatcher.exact("scala/DeprecatedConsole#readChar().") 80 | private val deprecatedConsoleReadDouble = SymbolMatcher.exact("scala/DeprecatedConsole#readDouble().") 81 | private val deprecatedConsoleReadFloat = SymbolMatcher.exact("scala/DeprecatedConsole#readFloat().") 82 | private val deprecatedConsoleReadInt = SymbolMatcher.exact("scala/DeprecatedConsole#readInt().") 83 | private val deprecatedConsoleReadLine = SymbolMatcher.exact("scala/DeprecatedConsole#readLine().") 84 | private val deprecatedConsoleReadLine1 = SymbolMatcher.exact("scala/DeprecatedConsole#readLine(+1).") 85 | private val deprecatedConsoleReadLong = SymbolMatcher.exact("scala/DeprecatedConsole#readLong().") 86 | private val deprecatedConsoleReadShort = SymbolMatcher.exact("scala/DeprecatedConsole#readShort().") 87 | private val deprecatedConsoleReadf = SymbolMatcher.exact("scala/DeprecatedConsole#readf().") 88 | private val deprecatedConsoleReadf1 = SymbolMatcher.exact("scala/DeprecatedConsole#readf1().") 89 | private val deprecatedConsoleReadf2 = SymbolMatcher.exact("scala/DeprecatedConsole#readf2().") 90 | private val deprecatedConsoleReadf3 = SymbolMatcher.exact("scala/DeprecatedConsole#readf3().") 91 | 92 | private val arrowAssoc = SymbolMatcher.exact("scala/Predef.ArrowAssoc#`→`().") 93 | } 94 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/DottyMigrate.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import metaconfig.Configured 4 | import scalafix.internal.rule.{CompilerTypePrinter, ExplicitResultTypesConfig} 5 | import scalafix.internal.v1.LazyValue 6 | import scalafix.v1._ 7 | 8 | import scala.meta._ 9 | import scala.meta.internal.pc.ScalafixGlobal 10 | import scalafixinternal._ 11 | import fix.explicittypes._ 12 | import fix.explicittypes.ExplicitImplicitTypes.interestedExplicitResultTypesConfig 13 | import DottyMigrate._ 14 | 15 | /** This rule combine & run other rules - see [[collector]] - while: 16 | * + Traveling the doc.tree one time only (for each doc) 17 | * + Don't re-run [[scala.meta.internal.proxy.GlobalProxy.typedTreeAt]] 18 | * for every rules that need `CompilerSupport` */ 19 | class DottyMigrate( 20 | config: DottyMigrateConfig, 21 | global: LazyValue[ScalafixGlobal] 22 | ) extends impl.CompilerDependentRule(global, "fix.scala213.DottyMigrate") { 23 | def this() = this(DottyMigrateConfig.default, LazyValue.later(() => ScalafixGlobal.newCompiler(Nil, Nil, Map.empty))) 24 | 25 | override def withConfiguration(config: Configuration): Configured[Rule] = { 26 | val c = DottyMigrateConfig( 27 | interestedExplicitResultTypesConfig(config), 28 | config.conf.get[NullaryOverrideConfig]("NullaryOverride").getOrElse(this.config.nullaryOverride) 29 | ) 30 | val newGlobal = LazyValue.later { () => 31 | ScalafixGlobal.newCompiler(config.scalacClasspath, config.scalacOptions, c.resTypes.symbolReplacements) 32 | } 33 | Configured.ok(new DottyMigrate(c, newGlobal)) 34 | } 35 | 36 | protected def unsafeFix()(implicit doc: SemanticDocument): Patch = { 37 | val power = new DottyPower(global.value, config.resTypes) 38 | doc.tree.collect(collector(power)).asPatch 39 | } 40 | 41 | // TODO add other rules: 42 | // + symbol literal 43 | // Note: When combining other rule that need `CompilerSupport`, 44 | // eg `ExplicitResultTypes`, we will make `DottyPower` extends the trait 45 | // that implement CompilerSupport logic for that rule. 46 | def collector(power: DottyPower)(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = { 47 | val all = Seq( 48 | ConstructorProcedureSyntax.collector, 49 | ProcedureSyntax.collector, 50 | ParensAroundLambda.collector, 51 | new Any2StringAdd().collector, 52 | new ExplicitNullaryEtaExpansion().collector, 53 | NullaryOverride.collector(power, config.nullaryOverride), 54 | new ExplicitNonNullaryApply(global).collector(power), 55 | ExplicitImplicitTypes.collector(power), 56 | MultiArgInfix.collector, 57 | ).map(_.lift.andThen(_.getOrElse(Patch.empty))) 58 | 59 | { 60 | case t => all.foldLeft(Patch.empty)(_ + _(t)) 61 | } 62 | } 63 | 64 | override def afterComplete(): Unit = { 65 | config.nullaryOverride.saveCollected() 66 | super.afterComplete() 67 | } 68 | } 69 | 70 | object DottyMigrate { 71 | final class DottyPower( 72 | val g: ScalafixGlobal, 73 | config: ExplicitResultTypesConfig 74 | )(implicit val doc: SemanticDocument) 75 | extends CompilerTypePrinter(g, config) with impl.NullaryPower with impl.IPower 76 | } 77 | 78 | case class DottyMigrateConfig( 79 | resTypes: ExplicitResultTypesConfig = ExplicitResultTypesConfig.default, 80 | nullaryOverride: NullaryOverrideConfig = NullaryOverrideConfig.default 81 | ) 82 | object DottyMigrateConfig { 83 | val default = DottyMigrateConfig() 84 | } 85 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/ExplicitNonNullaryApply.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.PartialFunction.{ cond, condOpt } 4 | import scala.collection.mutable 5 | import scala.util.Try 6 | 7 | import metaconfig.Configured 8 | 9 | import scala.meta._ 10 | import scala.meta.internal.pc.ScalafixGlobal 11 | 12 | import scalafix.v1._ 13 | import scalafix.internal.v1.LazyValue 14 | 15 | /** Explicitly insert () to non-nullary method applications that lack it. 16 | * https://dotty.epfl.ch/docs/reference/dropped-features/auto-apply.html 17 | * https://github.com/scala/scala/pull/8833 18 | */ 19 | final class ExplicitNonNullaryApply(global: LazyValue[ScalafixGlobal]) 20 | extends impl.CompilerDependentRule(global, "fix.scala213.ExplicitNonNullaryApply") 21 | { 22 | def this() = this(LazyValue.later(() => ScalafixGlobal.newCompiler(Nil, Nil, Map.empty))) 23 | 24 | protected def unsafeFix()(implicit doc: SemanticDocument) = { 25 | lazy val power = new impl.Power(global.value) 26 | doc.tree.collect(collector(power)).asPatch 27 | } 28 | 29 | def collector(power: => impl.IPower)(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = { 30 | val handled = mutable.Set.empty[Term.Name] 31 | 32 | def isJavaDefined(name: Term.Name): Boolean = name.value match { 33 | case "toString" => true // fast-track known, common cases 34 | case "getClass" => true 35 | case "hashCode" => true 36 | case "asInstanceOf" => true 37 | case "isInstanceOf" => true 38 | case _ => power.isJavaDefined(name) 39 | } 40 | 41 | def fix(tree: Tree, meth: Term, noTypeArgs: Boolean, noArgs: Boolean) = { 42 | for { 43 | name <- termName(meth) 44 | if name.value != "##" // fast-track https://github.com/scala/scala/pull/8814 45 | if handled.add(name) 46 | if noArgs 47 | if name.isReference 48 | if !cond(name.parent) { case Some(Term.ApplyInfix(_, `name`, _, _)) => true } 49 | if !tree.parent.exists(_.is[Term.Eta]) // else rewrites `meth _` to `meth() _`, or requires running ExplicitNullaryEtaExpansion first 50 | // HACK: In certain cases, `Symbol.info` may throw `MissingSymbolException` due to some unknown reason 51 | // If it happens, here we assume that this symbol has no info 52 | info <- Try(name.symbol.info).toOption.flatten 53 | if !info.isJava // shallow, isJavaDefined (below) checks overrides 54 | if cond(info.signature) { 55 | case MethodSignature(_, Nil :: _, _) => true 56 | case ClassSignature(_, _, _, decls) if tree.isInstanceOf[Term.ApplyType] => 57 | decls.exists { decl => 58 | decl.displayName == "apply" && 59 | cond(decl.signature) { case MethodSignature(_, Nil :: _, _) => true } 60 | } 61 | } 62 | if !isJavaDefined(name) // full check, using the presentation compiler :O 63 | } yield { 64 | val optAddDot = name.parent.collect { 65 | case PostfixSelect(qual, `name`) => 66 | Patch.removeTokens(doc.tokenList.trailingSpaces(qual.tokens.last)) + 67 | Patch.addLeft(name.tokens.head, ".") 68 | } 69 | val target = if (noTypeArgs) name else tree 70 | // WORKAROUND scalameta/scalameta#1083 71 | // Fixes `lhs op (arg)` from being rewritten as `lhs op (arg)()` (instead of `lhs op (arg())`) 72 | val token = target.tokens.reverseIterator.find(!_.is[Token.RightParen]).get 73 | optAddDot.asPatch + Patch.addRight(token, "()") 74 | } 75 | }.asPatch 76 | 77 | { 78 | case t @ q"$meth[..$targs](...$args)" => fix(t, meth, targs.isEmpty, args.isEmpty) 79 | case t @ q"$meth(...$args)" => fix(t, meth, true, args.isEmpty) 80 | } 81 | } 82 | 83 | // No PostfixSelect in Scalameta, so build one 84 | private object PostfixSelect { 85 | def unapply(t: Tree)(implicit doc: SemanticDocument): Option[(Term, Term.Name)] = t match { 86 | case Term.Select(qual, name) => 87 | val tokenList = doc.tokenList 88 | val inBetweenSlice = tokenList.slice(tokenList.next(qual.tokens.last), name.tokens.head) 89 | if (inBetweenSlice.exists(_.is[Token.Dot])) None 90 | else Some((qual, name)) 91 | case _ => None 92 | } 93 | } 94 | 95 | private def termName(term: Term): Option[Term.Name] = condOpt(term) { 96 | case name: Term.Name => name 97 | case Term.Select(_, name: Term.Name) => name 98 | } 99 | 100 | override def withConfiguration(config: Configuration) = { 101 | val compileSv = config.scalaVersion 102 | val runtimeSv = scala.util.Properties.versionNumberString 103 | if ((compileSv.take(4) != runtimeSv.take(4)) && config.scalacOptions.nonEmpty) { 104 | Configured.error( 105 | s"Scala version mismatch: " + 106 | s"(1) the target sources were compiled with Scala $compileSv; " + 107 | s"(2) Scalafix is running on Scala $runtimeSv. " + 108 | s"To fix make scalafixScalaBinaryVersion == ${compileSv.take(4)}. " + 109 | "Try `ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value)`." 110 | ) 111 | } else { 112 | Configured.ok(new ExplicitNonNullaryApply(LazyValue.later { () => 113 | ScalafixGlobal.newCompiler(config.scalacClasspath, config.scalacOptions, Map.empty) 114 | })) 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/ExplicitNullaryEtaExpansion.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.meta._ 4 | 5 | import scalafix.v1._ 6 | 7 | final class ExplicitNullaryEtaExpansion extends SemanticRule("fix.scala213.ExplicitNullaryEtaExpansion") { 8 | def collector(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = { 9 | case eta: Term.Eta => 10 | eta.expr.symbol.info.map(_.signature).collect { 11 | case ValueSignature(_) | MethodSignature(_, Nil, _) => 12 | val etaSuffix = eta.tokens.takeRight(eta.pos.end - eta.expr.pos.end) // the " _" after eta's expr 13 | Patch.addLeft(eta, "() => ") + Patch.removeTokens(etaSuffix) 14 | }.asPatch 15 | } 16 | 17 | override def fix(implicit doc: SemanticDocument) = { 18 | doc.tree.collect(collector).asPatch 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/FinalObject.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scalafix.v1._ 4 | 5 | import scala.meta._ 6 | import scala.meta.tokens.Token._ 7 | 8 | class FinalObject extends SyntacticRule("fix.scala213.FinalObject") { 9 | override def description: String = "remove redundant `final` modifier for objects" 10 | 11 | override def fix(implicit doc: SyntacticDocument): Patch = { 12 | doc.tree.collect { 13 | case t: Defn.Object if t.mods.exists(_.is[Mod.Final]) => 14 | // remove `final` and the space after that keyword 15 | val tokens = t.tokens 16 | val i = tokens.indexWhere(_.is[KwFinal]) 17 | Patch.removeTokens(tokens.slice(i, i + 2)) 18 | }.asPatch 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/MultiArgInfix.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scalafix.v1._ 4 | 5 | import scala.meta._ 6 | import MultiArgInfix._ 7 | 8 | import scala.meta.tokens.Token.{LeftBracket, LeftParen, RightBracket} 9 | import scala.meta.tokens.Tokens 10 | 11 | class MultiArgInfix extends SyntacticRule("fix.scala213.MultiArgInfix") { 12 | override def fix(implicit doc: SyntacticDocument): Patch = { 13 | doc.tree.collect(collector).asPatch 14 | } 15 | } 16 | 17 | object MultiArgInfix { 18 | implicit class TokensOps(private val tokens: Tokens) extends AnyVal { 19 | def right(t: Token): Tokens = tokens.dropWhile(_ != t).drop(1) 20 | def right(p: Token => Boolean): Tokens = tokens.dropWhile(!p(_)).drop(1) 21 | } 22 | 23 | val collector: PartialFunction[Tree, Patch] = { 24 | case t @ Term.ApplyInfix(lhs, op, targs, args) if args.size > 1 => 25 | val tokens = t.tokens 26 | val opTokens = op.tokens 27 | val toRemoveBetweenOpAndArgs = targs match { 28 | case Nil => tokens.right(opTokens.last).takeWhile(!_.is[LeftParen]) 29 | case _ => 30 | tokens.right(opTokens.last).takeWhile(!_.is[LeftBracket]) ++ 31 | tokens.right(targs.last.tokens.last).right(_.is[RightBracket]).takeWhile(!_.is[LeftParen]) 32 | } 33 | Patch.removeTokens( 34 | tokens.right(lhs.tokens.last).takeWhile(_ != opTokens.head) ++ 35 | toRemoveBetweenOpAndArgs 36 | ) + 37 | Patch.addRight(lhs, ".") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/NullaryHashHash.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.meta._ 4 | 5 | import scalafix.v1._ 6 | 7 | final class NullaryHashHash extends SemanticRule("fix.scala213.NullaryHashHash") { 8 | private val HashHash = SymbolMatcher.exact("scala/Any#`##`().", "java/lang/Object#`##`().") 9 | 10 | override def fix(implicit doc: SemanticDocument) = { 11 | doc.tree.collect { 12 | case expr @ Term.Apply(HashHash(fun), Nil) => 13 | Patch.removeTokens(expr.tokens.takeRight(expr.pos.end - fun.pos.end)) 14 | }.asPatch 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/NullaryOverride.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import metaconfig.Configured 4 | import scalafix.internal.v1.LazyValue 5 | import scalafix.v1._ 6 | 7 | import scala.meta._ 8 | import scala.meta.internal.pc.ScalafixGlobal 9 | import scala.meta.tokens.Token.{LeftParen, RightParen} 10 | 11 | import impl.{IPower, NullaryPower} 12 | import NullaryOverride._ 13 | import NullaryOverrideMode._ 14 | 15 | class NullaryOverride( 16 | config: NullaryOverrideConfig, 17 | global: LazyValue[ScalafixGlobal] 18 | ) extends impl.CompilerDependentRule(global, "fix.scala213.NullaryOverride") { 19 | def this() = this(NullaryOverrideConfig.default, LazyValue.later(() => ScalafixGlobal.newCompiler(Nil, Nil, Map.empty))) 20 | 21 | override def withConfiguration(config: Configuration): Configured[NullaryOverride] = { 22 | val newGlobal = LazyValue.later { () => 23 | ScalafixGlobal.newCompiler(config.scalacClasspath, config.scalacOptions, Map.empty) 24 | } 25 | config.conf 26 | .getOrElse("NullaryOverride")(NullaryOverrideConfig.default) 27 | .map(new NullaryOverride(_, newGlobal)) 28 | } 29 | 30 | override def afterComplete(): Unit = { 31 | config.saveCollected() 32 | super.afterComplete() 33 | } 34 | 35 | protected def unsafeFix()(implicit doc: SemanticDocument): Patch = { 36 | val power = new Power(global.value) 37 | doc.tree.collect(collector(power, config)).asPatch 38 | } 39 | } 40 | 41 | /** @note When remove `()` from `def foo()` in type T then 42 | * all references `t.foo()` must be rewritten to `t.foo` (not just in this `doc`) 43 | * Similar, `t.foo _` must be rewritten to `() => t.foo` */ 44 | object NullaryOverride { 45 | def collector( 46 | power: => NullaryPower, 47 | config: NullaryOverrideConfig 48 | )(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = config.mode match { 49 | case ResetAndCollect | CollectAppend => { 50 | case Defn.Def(_, name, _, Nil, _, _) if power.isNullaryMethod(name).contains(false) => 51 | // if this is a local symbol 52 | // eg, the nullary `def postStop` in `new Actor` in fix.scala213.nullaryOverride.Local test case 53 | // then we can't save the symbol and rewrite later (when NullaryOverrideConfig.mode == Rewrite) 54 | // because the symbol's value is just `local1`,.. 55 | // So we rewrite now. 56 | // note: This is not a perfect solution - 57 | // for a better one, we can rewrite by introducing a new named class similar to 58 | // scalafix.internal.rule.ExplicitResultTypesConfig.rewriteStructuralTypesToNamedSubclass 59 | if (name.symbol.isLocal) { 60 | Patch.addRight(name, "()") 61 | } else { 62 | config.nonNullarySymbols += name.symbol.value 63 | Patch.empty 64 | } 65 | case t @ Defn.Def(_, name, _, List(Nil), _, _) if power.isNullaryMethod(name).contains(true) => 66 | if (name.symbol.isLocal) { 67 | removeParens(t, name) 68 | } else { 69 | config.nullarySymbols += name.symbol.value 70 | Patch.empty 71 | } 72 | } 73 | case Rewrite => { 74 | case config.nonNullaryMatcher(name: Term.Name) 75 | if name.isReference && !isApply(name) => 76 | Patch.addRight(name, "()") 77 | case config.nonNullaryMatcher(Defn.Def(_, name, _, Nil, _, _)) => 78 | Patch.addRight(name, "()") 79 | case config.nullaryMatcher(t: Defn.Def) => 80 | removeParens(t, t.name) 81 | case config.nullaryMatcher(t@Term.Apply(fun, _)) => 82 | removeParens(t, fun) 83 | } 84 | } 85 | 86 | private def isApply(name: Term.Name): Boolean = name.parent match { 87 | case Some(_: Term.Apply) => true 88 | case Some(s @ Term.Select(_, `name`)) => s.parent.exists(_.isInstanceOf[Term.Apply]) 89 | case _ => false 90 | } 91 | 92 | private def removeParens(t: Tree, name: Term) = { 93 | val lastNameTok = name.tokens.last 94 | val tail = t.tokens.dropWhile(_ != lastNameTok) 95 | // '(' and ')' and all trivial token between those parens 96 | val parens = tail.slice( 97 | tail.indexWhere(_.is[LeftParen]), 98 | tail.indexWhere(_.is[RightParen]) + 1, 99 | ) 100 | Patch.removeTokens(parens) 101 | } 102 | 103 | final class Power(val g: ScalafixGlobal)(implicit val doc: SemanticDocument) 104 | extends NullaryPower with IPower 105 | } 106 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/NullaryOverrideConfig.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import java.io.FileWriter 4 | import java.nio.file.{Path, Paths} 5 | import scala.io.Source 6 | import scala.collection.mutable 7 | import metaconfig._ 8 | import metaconfig.generic.Surface 9 | import scalafix.internal.config.ReaderUtil 10 | import scalafix.v1.SymbolMatcher 11 | import scala.util.Using 12 | import NullaryOverrideConfig._ 13 | import NullaryOverrideMode._ 14 | 15 | case class NullaryOverrideConfig( 16 | mode: NullaryOverrideMode = CollectAppend, 17 | nullarySymPath: Path = Paths.get(".nullary.NullaryOverride"), 18 | nonNullarySymPath: Path = Paths.get(".nonNullary.NullaryOverride") 19 | ) { 20 | val nullarySymbols: mutable.Set[String] = mutable.Set.empty 21 | val nonNullarySymbols: mutable.Set[String] = mutable.Set.empty 22 | lazy val nullaryMatcher: SymbolMatcher = matcher(nullarySymPath) 23 | lazy val nonNullaryMatcher: SymbolMatcher = matcher(nonNullarySymPath) 24 | 25 | def saveCollected(): Unit = mode match { 26 | case ResetAndCollect => 27 | writeSymbols(nullarySymbols, nullarySymPath) 28 | writeSymbols(nonNullarySymbols, nonNullarySymPath) 29 | case CollectAppend => 30 | appendSymbols(nullarySymbols, nullarySymPath) 31 | appendSymbols(nonNullarySymbols, nonNullarySymPath) 32 | case Rewrite => // do nothing 33 | } 34 | } 35 | 36 | object NullaryOverrideConfig { 37 | val default: NullaryOverrideConfig = NullaryOverrideConfig() 38 | implicit val reader: ConfDecoder[NullaryOverrideConfig] = generic.deriveDecoder[NullaryOverrideConfig](default) 39 | implicit val surface: Surface[NullaryOverrideConfig] = generic.deriveSurface[NullaryOverrideConfig] 40 | 41 | private def readLines(p: Path): List[String] = 42 | Using(Source.fromFile(p.toFile))(_.getLines.toList).getOrElse(Nil) 43 | private def writeSymbols(symbols: Iterable[String], p: Path): Unit = 44 | Using(new FileWriter(p.toFile)) { fw => 45 | symbols.foreach { sym => fw.write(s"$sym\n") } 46 | }.get 47 | private def appendSymbols(symbols: Iterable[String], p: Path): Unit = 48 | writeSymbols(readLines(p).toSet ++ symbols, p) 49 | 50 | private def matcher(p: Path) = SymbolMatcher.exact(readLines(p): _*) 51 | } 52 | 53 | sealed trait NullaryOverrideMode 54 | object NullaryOverrideMode { 55 | case object ResetAndCollect extends NullaryOverrideMode 56 | case object CollectAppend extends NullaryOverrideMode 57 | case object Rewrite extends NullaryOverrideMode 58 | 59 | private def all = Seq(ResetAndCollect, CollectAppend, Rewrite) 60 | implicit val reader: ConfDecoder[NullaryOverrideMode] = 61 | ReaderUtil.fromMap(all.map(x => x.toString -> x).toMap) 62 | } 63 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/ParensAroundLambda.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scalafix.v1.{Patch, SyntacticDocument, SyntacticRule} 4 | import scala.meta._ 5 | import scala.meta.tokens.Token.{LeftParen, KwImplicit} 6 | import ParensAroundLambda._ 7 | 8 | class ParensAroundLambda extends SyntacticRule("fix.scala213.ParensAroundLambda") { 9 | override def fix(implicit doc: SyntacticDocument): Patch = { 10 | doc.tree.collect(collector).asPatch 11 | } 12 | } 13 | 14 | object ParensAroundLambda { 15 | val collector: PartialFunction[Tree, Patch] = { 16 | case Term.Function(List(p @ Term.Param(_, _, Some(_), _)), _) => 17 | val tokens = p.tokens 18 | val head = tokens.head 19 | if (head.is[LeftParen] || head.is[KwImplicit]) Patch.empty 20 | else { 21 | Patch.addLeft(head, "(") + 22 | Patch.addRight(tokens.last, ")") 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/ScalaSeq.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import metaconfig.generic.Surface 4 | import metaconfig.{ConfDecoder, Configured} 5 | 6 | import scala.meta._ 7 | 8 | import scalafix.v1._ 9 | 10 | final class ScalaSeq(val config: ScalaSeq.Config) extends SemanticRule("fix.scala213.ScalaSeq") { 11 | def this() = this(ScalaSeq.Config()) 12 | import ScalaSeq._ 13 | 14 | override def withConfiguration(config: Configuration): Configured[Rule] = { 15 | // the following don't seem to work: 16 | // - getOrElse("fix.scala213.ScalaSeq") 17 | // - getOrElse("fix", "scala213", "ScalaSeq") 18 | config.conf.dynamic.fix.scala213.asConf 19 | .andThen(_.getOrElse("ScalaSeq")(this.config)) 20 | .map(new ScalaSeq(_)) 21 | } 22 | 23 | override def fix(implicit doc: SemanticDocument) = new impl.CollectingTraverser { 24 | private var inParam = false 25 | 26 | private val paramImport: Option[Importer] = toImporter(config.paramImport) 27 | private val otherImport: Option[Importer] = toImporter(config.otherImport) 28 | private def toImporter(s: String) = if (s.isEmpty) None else Some(s.parse[Importer].get) 29 | 30 | override def apply(tree: Tree): Unit = tree match { 31 | case p: Term.Param => 32 | inParam = true 33 | apply(p.decltpe) 34 | inParam = false 35 | apply(p.default) 36 | 37 | case scalaSeq(Type.Apply(t, _)) => 38 | val sub = if (inParam) { 39 | paramImport.foreach(i => patch += globalImports.add(i)) 40 | config.paramType 41 | } else { 42 | otherImport.foreach(i => patch += globalImports.add(i)) 43 | config.otherType 44 | } 45 | patch += Patch.replaceTree(t, sub) 46 | 47 | case _ => 48 | super.apply(tree) 49 | } 50 | }.run(doc.tree) 51 | } 52 | 53 | object ScalaSeq { 54 | private val scalaSeq = SymbolMatcher.exact("scala/package.Seq#") 55 | 56 | final case class Config( 57 | paramType: String = "collection.Seq", 58 | otherType: String = "immutable.Seq", 59 | paramImport: String = "scala.collection", 60 | otherImport: String = "scala.collection.immutable", 61 | ) 62 | 63 | object Config { 64 | implicit val surface: Surface[Config] = metaconfig.generic.deriveSurface[Config] 65 | implicit val decoder: ConfDecoder[Config] = metaconfig.generic.deriveDecoder(new Config()) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/fix/scala213/Varargs.scala: -------------------------------------------------------------------------------- 1 | package fix.scala213 2 | 3 | import scala.meta._ 4 | 5 | import scalafix.v1._ 6 | 7 | import impl.SignatureMatcher 8 | 9 | object Varargs { 10 | val scSeq = SignatureMatcher.exact("scala/collection/Seq#") 11 | }; import Varargs._ 12 | 13 | final class Varargs extends SemanticRule("fix.scala213.Varargs") { 14 | override def fix(implicit doc: SemanticDocument): Patch = 15 | doc.tree.collect { 16 | case Term.Repeated(scSeq(expr)) => Patch.addRight(expr, ".toSeq") 17 | }.asPatch 18 | } 19 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/CollectingTraverser.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scalafix.v1._ 4 | 5 | import scala.meta._ 6 | 7 | abstract class CollectingTraverser extends Traverser { 8 | final protected val globalImports = new GlobalImports 9 | final protected var patch = Patch.empty; 10 | 11 | final def run(tree: Tree): Patch = { apply(tree); patch } 12 | } 13 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/CompilerDependentRule.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scalafix.internal.rule.CompilerException 4 | import scalafix.internal.v1.LazyValue 5 | import scalafix.v1.{Patch, SemanticDocument, SemanticRule} 6 | 7 | import scala.meta.inputs.Input 8 | import scala.meta.internal.pc.ScalafixGlobal 9 | import scala.util.control.Exception.nonFatalCatch 10 | 11 | abstract class CompilerDependentRule(global: LazyValue[ScalafixGlobal], name: String) extends SemanticRule(name) { 12 | protected def unsafeFix()(implicit doc: SemanticDocument): Patch 13 | 14 | override def fix(implicit doc: SemanticDocument): Patch = { 15 | try unsafeFix() catch { 16 | case e: CompilerException => 17 | println(s"[info] retry fix $name on ${path(doc.input)}: $e") 18 | // Give it another shot (good old "retry.retry") 19 | // as the presentation compiler sometimes just dies and succeeds the next time... 20 | shutdownAndResetCompiler() 21 | try unsafeFix() catch { 22 | case e: CompilerException => 23 | println(s"[error] abort fix $name on ${path(doc.input)}: $e") 24 | e.printStackTrace() 25 | // Give up on fixing this file as compiling it crashed the (presentation) compiler twice 26 | // but first reset the state of the compiler for the next file 27 | shutdownAndResetCompiler() 28 | Patch.empty 29 | } 30 | } 31 | } 32 | 33 | override def afterComplete(): Unit = shutdownAndResetCompiler() 34 | 35 | def shutdownAndResetCompiler(): Unit = { 36 | for (g <- global) { 37 | nonFatalCatch { 38 | g.askShutdown() 39 | g.close() 40 | } 41 | } 42 | global.restart() // more of a "reset", as nothing's eagerly started 43 | } 44 | 45 | private def path(input: Input): String = input match { 46 | case Input.File(path, _) => path.toString() 47 | case Input.VirtualFile(path, _) => path 48 | case Input.Slice(i, _, _) => path(i) 49 | case _ => input.getClass.getSimpleName 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/GlobalImports.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scalafix.v1._ 4 | 5 | import scala.collection.mutable 6 | import scala.meta._ 7 | 8 | final class GlobalImports { 9 | private val known = mutable.Set.empty[String] 10 | 11 | def add(importer: Importer): Patch = 12 | if (known.add(importer.syntax)) 13 | Patch.addGlobalImport(importer) 14 | else 15 | Patch.empty 16 | } 17 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/NullaryPower.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scalafix.internal.rule.CompilerException 4 | import scalafix.v1._ 5 | 6 | import scala.annotation.tailrec 7 | import scala.meta.Tree 8 | import scala.meta.internal.pc.ScalafixGlobal 9 | import scala.meta.internal.proxy.GlobalProxy 10 | 11 | trait NullaryPower { 12 | val g: ScalafixGlobal 13 | implicit val doc: SemanticDocument 14 | private lazy val unit = g.newCompilationUnit(doc.input.text, doc.input.syntax) 15 | 16 | /** Similar to `nextOverriddenSymbol` but loop through ancestors.reverse 17 | * @see [[scala.reflect.internal.Symbols.Symbol.nextOverriddenSymbol]] */ 18 | private def rootOverriddenSymbol(s: g.Symbol): g.Symbol = { 19 | import g._, s._ 20 | @tailrec def loop(bases: List[Symbol]): Symbol = bases match { 21 | case Nil => NoSymbol 22 | case base :: rest => 23 | val sym = overriddenSymbol(base) 24 | if (sym == NoSymbol) loop(rest) else sym 25 | } 26 | if (isOverridingSymbol) loop(owner.ancestors.reverse) else NoSymbol 27 | } 28 | 29 | @tailrec private def isNullary(t: g.Type): Boolean = t match { 30 | case g.NullaryMethodType(_) => true 31 | case m: g.MethodType => m.isImplicit 32 | case g.PolyType(_, r) => isNullary(r) 33 | case _ => false 34 | } 35 | 36 | def isNullaryMethod(t: Tree): Option[Boolean] = try { 37 | val meth: g.Symbol = { 38 | val sSym = t.symbol 39 | if (sSym.isLocal) { 40 | GlobalProxy.typedTreeAt(g, unit.position(t.pos.start)).symbol 41 | } else { 42 | val gSymbols = g.inverseSemanticdbSymbols(sSym.value) 43 | gSymbols.find(gSym => g.semanticdbSymbol(gSym) == sSym.value).getOrElse(g.NoSymbol) 44 | } 45 | } 46 | val isJavaDefined = meth.overrideChain.exists(sym => sym.isJavaDefined || sym.owner == g.definitions.AnyClass) 47 | 48 | if (isJavaDefined) None 49 | else rootOverriddenSymbol(meth) match { 50 | case m: g.MethodSymbol => Some(isNullary(m.info)) 51 | case _ => None 52 | } 53 | } catch { 54 | case e: Throwable => throw CompilerException(e) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/Power.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scala.meta._ 4 | import scala.meta.internal.pc.ScalafixGlobal 5 | 6 | import scalafix.v1._ 7 | import scalafix.internal.rule.CompilerException 8 | 9 | /** Necessary b/c Scalafix/Scalameta don't maintain any overriding info. */ 10 | // From scalafix-rules/src/main/scala/scalafix/internal/rule/CompilerTypePrinter.scala#L55 11 | // in scalacenter/scalafix (at db93793c2a8f80d0e7bbcacef729dc5c2fa7ceb8). 12 | final class Power(val g: ScalafixGlobal)(implicit val doc: SemanticDocument) 13 | extends IPower 14 | 15 | trait IPower { 16 | val g: ScalafixGlobal 17 | implicit val doc: SemanticDocument 18 | 19 | def isJavaDefined(t: Tree): Boolean = try { 20 | gsymbol(t).overrideChain.exists(sym => sym.isJavaDefined || sym.owner == g.definitions.AnyClass) 21 | } catch { 22 | case e: Throwable => throw CompilerException(e) 23 | } 24 | 25 | protected def gsymbol(t: Tree): g.Symbol = { 26 | val sSym = t.symbol 27 | val gSymbols = g.inverseSemanticdbSymbols(sSym.value) 28 | gSymbols.find(gSym => g.semanticdbSymbol(gSym) == sSym.value).getOrElse(g.NoSymbol) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/SignatureMatcher.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scala.meta._ 4 | 5 | import scalafix.v1._ 6 | 7 | object SignatureMatcher { 8 | def exact(symbols: String*): SignatureMatcher = 9 | new SignatureMatcher(SymbolMatcher.exact(symbols: _*)) 10 | } 11 | 12 | final class SignatureMatcher(symbolMatcher: SymbolMatcher) { 13 | def unapply(tree: Tree)(implicit sdoc: SemanticDocument): Option[Tree] = 14 | tree.symbol.info.map(_.signature).collect { 15 | case ValueSignature(TypeRef(_, symbolMatcher(_), _)) => tree 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/impl/TreeReplacers.scala: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import scala.PartialFunction.cond 4 | import scala.collection.mutable 5 | 6 | import scala.meta._ 7 | 8 | import scalafix.Patch 9 | 10 | abstract class ReplacingTraverser extends CollectingTraverser { 11 | type Replacer = PartialFunction[Tree, Patch] 12 | 13 | private val replaced = mutable.Set.empty[Tree] // replaced trees, so skip children 14 | 15 | def replacers: List[Replacer] 16 | 17 | final override def apply(tree: Tree) = { 18 | for (replacer <- replacers) 19 | replacer.runWith(patch += _)(tree) 20 | if (!replaced.contains(tree)) 21 | super.apply(tree) 22 | } 23 | 24 | final protected def replaceTree(from: Tree, to0: String): Patch = { 25 | def interpolating = from.parent.exists(_.isInstanceOf[Term.Interpolate]) 26 | def toParses = cond(to0.parse[Term]) { case Parsed.Success(_: Term.Name) => true } 27 | val to = from match { 28 | case _: Term.Name if interpolating && !toParses => s"{$to0}" 29 | case _ => to0 30 | } 31 | replaced += from 32 | Patch.replaceTree(from, to) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rewrites/src/main/scala/scalafixinternal/ProcedureSyntax.scala: -------------------------------------------------------------------------------- 1 | package scalafixinternal 2 | 3 | import scala.meta._ 4 | import scalafix.v1._ 5 | import scalafix.util.Trivia 6 | 7 | /** same as [[scalafix.internal.rule.ProcedureSyntax]] */ 8 | object ProcedureSyntax { 9 | def collector(implicit doc: SemanticDocument): PartialFunction[Tree, Patch] = { 10 | case t: Decl.Def if t.decltpe.tokens.isEmpty => 11 | Patch.addRight(t.tokens.last, s": Unit").atomic 12 | case t: Defn.Def 13 | if t.decltpe.exists(_.tokens.isEmpty) && 14 | t.body.tokens.head.is[Token.LeftBrace] => 15 | val fixed = for { 16 | bodyStart <- t.body.tokens.headOption 17 | toAdd <- doc.tokenList.leading(bodyStart).find(!_.is[Trivia]) 18 | } yield Patch.addRight(toAdd, s": Unit =").atomic 19 | fixed.getOrElse(Patch.empty) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/src/test/scala/fix/BeforeAndAfterAllConfigMapAlt.scala: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import org.scalatest._ 4 | 5 | object BeforeAndAfterAllConfigMapAlt { 6 | private def anExceptionThatShouldCauseAnAbort(throwable: Throwable): Boolean = throwable match { 7 | case _: java.lang.annotation.AnnotationFormatError | 8 | _: java.nio.charset.CoderMalfunctionError | 9 | _: javax.xml.parsers.FactoryConfigurationError | 10 | _: LinkageError | 11 | _: ThreadDeath | 12 | _: javax.xml.transform.TransformerFactoryConfigurationError | 13 | _: VirtualMachineError => true 14 | case e if e.getClass.getName == "java.awt.AWTError" => true 15 | case _ => false 16 | } 17 | }; import BeforeAndAfterAllConfigMapAlt._ 18 | 19 | trait BeforeAndAfterAllConfigMapAlt extends SuiteMixin { _: Suite => 20 | protected def beforeAllAlt(configMap: ConfigMap) = () 21 | protected def afterAllAlt(configMap: ConfigMap) = () 22 | 23 | abstract override def run(testName: Option[String], args: Args): Status = { 24 | val shouldRun = !args.runTestInNewInstance && expectedTestCount(args.filter) > 0 25 | var exception: Exception = null 26 | var status: Status = FailedStatus 27 | 28 | try { 29 | if (shouldRun) beforeAllAlt(args.configMap) 30 | status = super.run(testName, args) 31 | } catch {case e: Exception => exception = e} 32 | 33 | try { 34 | val statusToReturn = if (shouldRun) { 35 | status.withAfterEffect { 36 | try afterAllAlt(args.configMap) catch { 37 | case e: Exception if !anExceptionThatShouldCauseAnAbort(e) && exception != null => 38 | } 39 | } 40 | } else status 41 | if (exception != null) throw exception 42 | statusToReturn 43 | } catch { 44 | case e: Exception => 45 | if (exception != null) throw exception 46 | throw e 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /tests/src/test/scala/fix/RuleSuite.scala: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import scala.reflect.ensureAccessible 4 | import org.scalatest.{ConfigMap, FunSpecLike} 5 | import scalafix.testkit.AbstractSemanticRuleSuite 6 | 7 | class RuleSuite extends AbstractSemanticRuleSuite with FunSpecLike with BeforeAndAfterAllConfigMapAlt { 8 | val isSaveExpectField = ensureAccessible(classOf[AbstractSemanticRuleSuite].getDeclaredField("isSaveExpect")) 9 | 10 | // If you invoke the test as "tests/testOnly -- -Doverwrite=true" it will save the expected 11 | override protected def beforeAllAlt(configMap: ConfigMap) = { 12 | val overwrite = configMap.getWithDefault("overwrite", "false").equalsIgnoreCase("true") 13 | isSaveExpectField.setBoolean(this, overwrite) 14 | } 15 | 16 | // NullaryOverride rule need 2 phases: collect and rewrite 17 | // so simple test will fail. 18 | // TODO figure how to test that rule 19 | testsToRun 20 | .filterNot(_.path.toString().contains("NullaryOverride")) 21 | .foreach(runOn) 22 | // runAllTests() 23 | } 24 | -------------------------------------------------------------------------------- /tests3/src/test/scala/fix/DottyMigrateApplier.scala: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import java.nio.charset.StandardCharsets.UTF_8 4 | import java.nio.file.Files 5 | import scala.meta._ 6 | import scalafix.testkit.{AbstractSemanticRuleSuite, RuleTest, SemanticRuleSuite} 7 | 8 | class DottyMigrateApplier extends AbstractSemanticRuleSuite with org.scalatest.FunSuiteLike { 9 | runAllTests() 10 | 11 | override def evaluateTestBody(diffTest: RuleTest): Unit = { 12 | val (rule, sdoc) = diffTest.run.apply() 13 | rule.beforeStart() 14 | 15 | val ret = 16 | try rule.semanticPatch(sdoc, suppress = false) 17 | finally rule.afterComplete() 18 | val tokens = ret.fixed.tokenize.get 19 | val obtained = SemanticRuleSuite.stripTestkitComments(tokens) 20 | 21 | val output = props.outputSourceDirectories.head.resolve(diffTest.path.testPath).toNIO 22 | 23 | Files.createDirectories(output.getParent) 24 | Files.write(output, obtained.getBytes(UTF_8)) 25 | } 26 | } 27 | --------------------------------------------------------------------------------