Skip to content

Commit

Permalink
Merge pull request #5536 from bplommer/lint-fs2-compiler
Browse files Browse the repository at this point in the history
Add scalafix linter for use of fs2 Sync compiler
  • Loading branch information
rossabaker committed Nov 5, 2021
2 parents 23e0b3e + 0edbe2a commit 0527127
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 5 deletions.
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

0 comments on commit 0527127

Please sign in to comment.