Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scalafix linter for use of fs2 Sync compiler #5536

Merged
merged 7 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .scalafix.conf
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
rules = [
Http4sFs2Linters
Http4sGeneralLinters
Http4sUseLiteralsSyntax
OrganizeImports
]

triggered.rules = [
Http4sFs2Linters
Http4sGeneralLinters
Http4sUseLiteralsSyntax
]
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ThisBuild / publishGithubUser := "rossabaker"
ThisBuild / publishFullName := "Ross A. Baker"

ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbOptions ++= Seq("-P:semanticdb:synthetics:on").filter(_ => !isScala3.value)
ThisBuild / semanticdbVersion := scalafixSemanticdb.revision
ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value)
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.5.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package org.http4s
package ember.core

import cats.effect.Concurrent
import cats.effect.IO
import cats.effect.Sync
import cats.syntax.all._
import org.http4s.headers.`Content-Length`
import org.http4s.syntax.literals._
Expand All @@ -28,7 +28,7 @@ class EncoderSuite extends Http4sSuite {
def stripLines(s: String): String = s.replace("\r\n", "\n")

// Only for Use with Text Requests
def encodeRequestRig[F[_]: Sync](req: Request[F]): F[String] =
def encodeRequestRig[F[_]: Concurrent](req: Request[F]): F[String] =
Encoder
.reqToBytes(req)
.through(fs2.text.utf8.decode[F])
Expand All @@ -37,7 +37,7 @@ class EncoderSuite extends Http4sSuite {
.map(stripLines)

// Only for Use with Text Requests
def encodeResponseRig[F[_]: Sync](resp: Response[F]): F[String] =
def encodeResponseRig[F[_]: Concurrent](resp: Response[F]): F[String] =
Encoder
.respToBytes(resp)
.through(fs2.text.utf8.decode[F])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
rule = Http4sFs2Linters
*/

package fix

import cats.effect._
import fs2._

object Fs2SyncCompilerTest {
def usesSyncInnocently[F[_]](implicit F: Sync[F]) = F.delay(println("hi"))
def usesSyncCompiler[F[_]](implicit F: Sync[F]) = Stream(1, 2, 3).covary[F].compile.drain // assert: Http4sFs2Linters.noFs2SyncCompiler
def usesConcurrentCompiler[F[_]](implicit F: Concurrent[F]) = Stream(1, 2, 3).covary[F].compile.drain
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
fix.GeneralLinters
fix.UseLiteralsSyntax
fix.UseLiteralsSyntax
fix.Fs2Linters
44 changes: 44 additions & 0 deletions scalafix-internal/rules/src/main/scala-2/fix/Fs2Linters.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2021 http4s.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package fix

import scalafix.v1._

import scala.meta._

class Fs2Linters extends SemanticRule("Http4sFs2Linters") {
override def fix(implicit doc: SemanticDocument): Patch =
doc.tree.collect { case Stream_compile_M(t @ Term.Select(_, _)) =>
t.synthetics.collect { case ApplyTree(_, List(ApplyTree(_, List(target)))) =>
target.symbol.collect { case Target_forSync_M(_) =>
Patch.lint(NoFs2SyncCompiler(t))
}.asPatch
}.asPatch
}.asPatch

val Stream_compile_M = SymbolMatcher.exact("fs2/Stream#compile().")
val Target_forSync_M = SymbolMatcher.exact("fs2/Compiler.TargetLowPriority#forSync().")
}

final case class NoFs2SyncCompiler(t: Tree) extends Diagnostic {
override def message: String =
"fs2's Sync compiler should be avoided. Usually this means a Sync constraint needs to be changed to Concurrent or upgraded to Async."

override def position: Position = t.pos

override def categoryID: String = "noFs2SyncCompiler"
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object ChunkAggregator {
http: Kleisli[F, A, Response[G]]): Kleisli[F, A, Response[G]] =
http.flatMapF { response =>
f(
response.body.chunks.compile.toVector
response.body.chunks.compile.toVector // scalafix:ok Http4sFs2Linters.noFs2SyncCompiler; bincompat until 1.0
.map { vec =>
val body = Chunk.concat(vec)
response
Expand Down