Skip to content

Commit

Permalink
Add a global flag to suppress deprecation warnings (#3406)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gedochao authored Jan 13, 2025
1 parent d8b3313 commit 66c2d35
Show file tree
Hide file tree
Showing 23 changed files with 341 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ import scala.build.Logger
import scala.build.errors.Diagnostic.TextEdit
import scala.build.internal.Constants
import scala.build.internal.util.WarningMessages.{deprecatedToolkitLatest, deprecatedWarning}
import scala.build.preprocessing.directives.{
DirectiveHandler,
DirectiveUtil,
StrictDirective,
Toolkit
}
import scala.build.options.SuppressWarningOptions
import scala.build.preprocessing.directives.{DirectiveHandler, StrictDirective, Toolkit}
import scala.build.warnings.DeprecatedWarning

object DeprecatedDirectives {
Expand All @@ -20,27 +16,27 @@ object DeprecatedDirectives {
* @param values
* representation of deprecated value
*/
case class DirectiveTemplate(keys: Seq[String], values: Option[Seq[String]]) {
private case class DirectiveTemplate(keys: Seq[String], values: Option[Seq[String]]) {
def appliesTo(foundKey: String, foundValues: Seq[String]): Boolean =
(keys.isEmpty || keys.contains(foundKey)) &&
// FIXME values.contains is not perfect, but is enough for now since we don't look for specific multiple values
(values.isEmpty || values.contains(foundValues))
}

type WarningAndReplacement = (String, DirectiveTemplate)
private type WarningAndReplacement = (String, DirectiveTemplate)

private def keyReplacement(replacement: String)(warning: String): WarningAndReplacement =
(warning, DirectiveTemplate(Seq(replacement), None))

private def valueReplacement(replacements: String*)(warning: String): WarningAndReplacement =
(warning, DirectiveTemplate(Nil, Some(replacements.toSeq)))

private def allAliasesOf(key: String, handler: DirectiveHandler[_]): Seq[String] =
private def allAliasesOf(key: String, handler: DirectiveHandler[?]): Seq[String] =
handler.keys.find(_.nameAliases.contains(key))
.toSeq
.flatMap(_.nameAliases)

private def allKeysFrom(handler: DirectiveHandler[_]): Seq[String] =
private def allKeysFrom(handler: DirectiveHandler[?]): Seq[String] =
handler.keys.flatMap(_.nameAliases)

private val deprecatedCombinationsAndReplacements = Map[DirectiveTemplate, WarningAndReplacement](
Expand Down Expand Up @@ -73,33 +69,35 @@ object DeprecatedDirectives {
)
)

def warningAndReplacement(directive: StrictDirective): Option[WarningAndReplacement] =
private def warningAndReplacement(directive: StrictDirective): Option[WarningAndReplacement] =
deprecatedCombinationsAndReplacements
.find(_._1.appliesTo(directive.key, directive.toStringValues))
.map(_._2) // grab WarningAndReplacement

def issueWarnings(
path: Either[String, os.Path],
directives: Seq[StrictDirective],
suppressWarningOptions: SuppressWarningOptions,
logger: Logger
) =
directives.map(d => d -> warningAndReplacement(d))
.foreach {
case (directive, Some(warning, replacement)) =>
val newKey = replacement.keys.headOption.getOrElse(directive.key)
val newValues = replacement.values.getOrElse(directive.toStringValues)
val newText = s"$newKey ${newValues.mkString(" ")}"
): Unit =
if !suppressWarningOptions.suppressDeprecatedFeatureWarning.getOrElse(false) then
directives.map(d => d -> warningAndReplacement(d))
.foreach {
case (directive, Some(warning, replacement)) =>
val newKey = replacement.keys.headOption.getOrElse(directive.key)
val newValues = replacement.values.getOrElse(directive.toStringValues)
val newText = s"$newKey ${newValues.mkString(" ")}"

// TODO use key and/or value positions instead of whole directive
val position = directive.position(path)
// TODO use key and/or value positions instead of whole directive
val position = directive.position(path)

val diagnostic = DeprecatedWarning(
warning,
Seq(position),
Some(TextEdit(s"Change to: $newText", newText))
)
logger.log(Seq(diagnostic))
case _ => ()
}
val diagnostic = DeprecatedWarning(
warning,
Seq(position),
Some(TextEdit(s"Change to: $newText", newText))
)
logger.log(Seq(diagnostic))
case _ => ()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ case class DirectivesPreprocessor(
using ScalaCliInvokeData
) {
def preprocess(content: String): Either[BuildException, PreprocessedDirectives] = for {
directives <- ExtractedDirectives.from(content.toCharArray, path, logger, maybeRecoverOnError)
res <- preprocess(directives)
directives <- ExtractedDirectives.from(
content.toCharArray,
path,
suppressWarningOptions,
logger,
maybeRecoverOnError
)
res <- preprocess(directives)
} yield res

def preprocess(extractedDirectives: ExtractedDirectives)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import com.virtuslab.using_directives.custom.model.{
UsingDirectives,
Value
}
import com.virtuslab.using_directives.custom.utils.ast._
import com.virtuslab.using_directives.custom.utils.ast.*

import scala.annotation.targetName
import scala.build.errors.*
import scala.build.options.SuppressWarningOptions
import scala.build.preprocessing.UsingDirectivesOps.*
import scala.build.preprocessing.directives.{DirectiveUtil, ScopedDirective, StrictDirective}
import scala.build.{Logger, Position}
Expand All @@ -34,16 +35,22 @@ object ExtractedDirectives {
def from(
contentChars: Array[Char],
path: Either[String, os.Path],
suppressWarningOptions: SuppressWarningOptions,
logger: Logger,
maybeRecoverOnError: BuildException => Option[BuildException]
): Either[BuildException, ExtractedDirectives] = {
val errors = new mutable.ListBuffer[Diagnostic]
val reporter = CustomDirectivesReporter.create(path) { diag =>
if (diag.severity == Severity.Warning)
logger.log(Seq(diag))
else
errors += diag
}
val reporter = CustomDirectivesReporter
.create(path) {
case diag
if diag.severity == Severity.Warning &&
diag.message.toLowerCase.contains("deprecated") &&
suppressWarningOptions.suppressDeprecatedFeatureWarning.getOrElse(false) =>
() // skip deprecated feature warnings if suppressed
case diag if diag.severity == Severity.Warning =>
logger.log(Seq(diag))
case diag => errors += diag
}
val processor = new UsingDirectivesProcessor(reporter)
val allDirectives = processor.extract(contentChars).asScala
val malformedDirectiveErrors =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import scala.build.EitherCps.{either, value}
import scala.build.Logger
import scala.build.errors.BuildException
import scala.build.internal.markdown.MarkdownCodeBlock
import scala.build.options.SuppressWarningOptions

object MarkdownCodeBlockProcessor {
def process(
codeBlocks: Seq[MarkdownCodeBlock],
reportingPath: Either[String, os.Path],
scopePath: ScopePath,
suppressWarningOptions: SuppressWarningOptions,
logger: Logger,
maybeRecoverOnError: BuildException => Option[BuildException]
): Either[BuildException, PreprocessedMarkdown] = either {
Expand All @@ -23,6 +25,7 @@ object MarkdownCodeBlockProcessor {
ExtractedDirectives.from(
contentChars = cb.body.toCharArray,
path = reportingPath,
suppressWarningOptions = suppressWarningOptions,
logger = logger,
maybeRecoverOnError = maybeRecoverOnError
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ case object MarkdownPreprocessor extends Preprocessor {
codeBlocks,
reportingPath,
scopePath,
suppressWarningOptions,
logger,
maybeRecoverOnError
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,11 @@ case object ScalaPreprocessor extends Preprocessor {
)(using ScalaCliInvokeData): Either[BuildException, Option[ProcessingOutput]] = either {
val (contentWithNoShebang, _) = SheBang.ignoreSheBangLines(content)
val extractedDirectives: ExtractedDirectives = value(ExtractedDirectives.from(
contentWithNoShebang.toCharArray,
path,
logger,
maybeRecoverOnError
contentChars = contentWithNoShebang.toCharArray,
path = path,
suppressWarningOptions = suppressWarningOptions,
logger = logger,
maybeRecoverOnError = maybeRecoverOnError
))
value {
processSources(
Expand All @@ -219,7 +220,12 @@ case object ScalaPreprocessor extends Preprocessor {
suppressWarningOptions: SuppressWarningOptions,
maybeRecoverOnError: BuildException => Option[BuildException]
)(using ScalaCliInvokeData): Either[BuildException, Option[ProcessingOutput]] = either {
DeprecatedDirectives.issueWarnings(path, extractedDirectives.directives, logger)
DeprecatedDirectives.issueWarnings(
path,
extractedDirectives.directives,
suppressWarningOptions,
logger
)

val (content0, isSheBang) = SheBang.ignoreSheBangLines(content)
val preprocessedDirectives: PreprocessedDirectives =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ trait RestrictableCommand[T](implicit myParser: Parser[T]) {
self: Command[T] =>

def shouldSuppressExperimentalFeatureWarnings: Boolean
def shouldSuppressDeprecatedFeatureWarnings: Boolean
def logger: Logger
protected def invokeData: ScalaCliInvokeData
override def parser: Parser[T] =
RestrictedCommandsParser(myParser, logger, shouldSuppressExperimentalFeatureWarnings)(using
invokeData
)
RestrictedCommandsParser(
parser = myParser,
logger = logger,
shouldSuppressExperimentalWarnings = shouldSuppressExperimentalFeatureWarnings,
shouldSuppressDeprecatedWarnings = shouldSuppressDeprecatedFeatureWarnings
)(using invokeData)

final def isRestricted: Boolean = scalaSpecificationLevel == SpecificationLevel.RESTRICTED

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ object RestrictedCommandsParser {
def apply[T](
parser: Parser[T],
logger: Logger,
shouldSuppressExperimentalWarnings: Boolean
shouldSuppressExperimentalWarnings: Boolean,
shouldSuppressDeprecatedWarnings: Boolean
)(using ScalaCliInvokeData): Parser[T] =
new Parser[T] {

Expand All @@ -37,7 +38,8 @@ object RestrictedCommandsParser {
RestrictedCommandsParser(
parser.withDefaultOrigin(origin),
logger,
shouldSuppressExperimentalWarnings
shouldSuppressExperimentalWarnings,
shouldSuppressDeprecatedWarnings
)

override def step(
Expand All @@ -58,7 +60,7 @@ object RestrictedCommandsParser {
logger.experimentalWarning(passedOption, FeatureType.Option)
r
case (r @ Right(Some(_, arg: Arg, _)), passedOption :: _)
if arg.isDeprecated =>
if arg.isDeprecated && !shouldSuppressDeprecatedWarnings =>
// TODO implement proper deprecation logic: https://github.com/VirtusLab/scala-cli/issues/3258
arg.deprecatedOptionAliases.find(_ == passedOption)
.foreach { deprecatedAlias =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ abstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T],
}
.getOrElse(false)

override def shouldSuppressDeprecatedFeatureWarnings: Boolean =
globalOptions.globalSuppress.suppressDeprecatedFeatureWarning
.orElse {
configDb.toOption
.flatMap(_.getOpt(Keys.suppressDeprecatedFeatureWarning))
}
.getOrElse(false)

override def logger: Logger = globalOptions.logging.logger

final override def main(progName: String, args: Array[String]): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object BuiltInRules extends CommandHelpers {

def runRules(
inputs: Inputs,
buildOptions: BuildOptions,
logger: Logger
)(using ScalaCliInvokeData): Unit = {
val (mainSources, testSources) = getProjectSources(inputs, logger)
Expand All @@ -57,8 +58,9 @@ object BuiltInRules extends CommandHelpers {

// Deal with directives from the Main scope
val (directivesFromWritableMainInputs, testDirectivesFromMain) = {
val originalMainDirectives = getExtractedDirectives(mainSources)
.filterNot(hasTargetDirectives)
val originalMainDirectives =
getExtractedDirectives(mainSources, buildOptions.suppressWarningOptions)
.filterNot(hasTargetDirectives)

val transformedMainDirectives = unifyCorrespondingNameAliases(originalMainDirectives)

Expand All @@ -83,8 +85,9 @@ object BuiltInRules extends CommandHelpers {
if (
testSources.paths.nonEmpty || testSources.inMemory.nonEmpty || testDirectivesFromMain.nonEmpty
) {
val originalTestDirectives = getExtractedDirectives(testSources)
.filterNot(hasTargetDirectives)
val originalTestDirectives =
getExtractedDirectives(testSources, buildOptions.suppressWarningOptions)
.filterNot(hasTargetDirectives)

val transformedTestDirectives = unifyCorrespondingNameAliases(originalTestDirectives)
.pipe(maybeTransformIntoTestEquivalent)
Expand Down Expand Up @@ -145,15 +148,24 @@ object BuiltInRules extends CommandHelpers {
(mainSources, testSources).traverseN
}

private def getExtractedDirectives(sources: Sources)(
private def getExtractedDirectives(
sources: Sources,
suppressWarningOptions: SuppressWarningOptions
)(
using loggingUtilities: LoggingUtilities
): Seq[ExtractedDirectives] = {
val logger = loggingUtilities.logger

val fromPaths = sources.paths.map { (path, _) =>
val (_, content) = SheBang.partitionOnShebangSection(os.read(path))
logger.debug(s"Extracting directives from ${loggingUtilities.relativePath(path)}")
ExtractedDirectives.from(content.toCharArray, Right(path), logger, _ => None).orExit(logger)
ExtractedDirectives.from(
contentChars = content.toCharArray,
path = Right(path),
suppressWarningOptions = suppressWarningOptions,
logger = logger,
maybeRecoverOnError = _ => None
).orExit(logger)
}

val fromInMemory = sources.inMemory.map { inMem =>
Expand All @@ -177,10 +189,11 @@ object BuiltInRules extends CommandHelpers {
val (_, contentWithNoShebang) = SheBang.partitionOnShebangSection(content)

ExtractedDirectives.from(
contentWithNoShebang.toCharArray,
originOrPath,
logger,
_ => None
contentChars = contentWithNoShebang.toCharArray,
path = originOrPath,
suppressWarningOptions = suppressWarningOptions,
logger = logger,
maybeRecoverOnError = _ => None
).orExit(logger)
}

Expand Down
8 changes: 5 additions & 3 deletions modules/cli/src/main/scala/scala/cli/commands/fix/Fix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ object Fix extends ScalaCommand[FixOptions] {

override def runCommand(options: FixOptions, args: RemainingArgs, logger: Logger): Unit = {
if options.areAnyRulesEnabled then {
val inputs = options.shared.inputs(args.all).orExit(logger)
val configDb = ConfigDbUtils.configDb.orExit(logger)
val inputs = options.shared.inputs(args.all).orExit(logger)
val buildOpts = buildOptionsOrExit(options)
val configDb = ConfigDbUtils.configDb.orExit(logger)
if options.enableBuiltInRules then {
logger.message("Running built-in rules...")
BuiltInRules.runRules(
inputs = inputs,
buildOptions = buildOpts,
logger = logger
)
logger.message("Built-in rules completed.")
Expand All @@ -45,7 +47,7 @@ object Fix extends ScalaCommand[FixOptions] {
.orElse(configDb.get(Keys.actions).getOrElse(None))
val scalafixExitCode: Int = value {
ScalafixRules.runRules(
buildOptions = buildOptionsOrExit(options),
buildOptions = buildOpts,
scalafixOptions = options.scalafix,
inputs = inputs,
check = options.check,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ abstract class PgpCommand[T](implicit myParser: Parser[T], help: Help[T])
override def shouldSuppressExperimentalFeatureWarnings: Boolean =
false // TODO add handling for scala-cli-signing

override def shouldSuppressDeprecatedFeatureWarnings: Boolean =
false // TODO add handling for scala-cli-signing

override def logger: Logger = CliLogger.default // TODO add handling for scala-cli-signing

override def hidden = true
Expand Down
Loading

0 comments on commit 66c2d35

Please sign in to comment.