Skip to content

Commit 0babef5

Browse files
committed
Support rewrite
1 parent 9f50b0c commit 0babef5

File tree

4 files changed

+51
-8
lines changed

4 files changed

+51
-8
lines changed

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import reporting._
2424
import io.{AbstractFile, NoAbstractFile, PlainFile, Path}
2525
import scala.io.Codec
2626
import collection.mutable
27+
import parsing.Parsers
2728
import printing._
2829
import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings, ScalaRelease}
2930
import classfile.ReusableDataReader
@@ -1025,6 +1026,7 @@ object Contexts:
10251026
/** Collect information about the run for purposes of additional diagnostics.
10261027
*/
10271028
class Usages:
1029+
import rewrites.Rewrites.patch
10281030
private val selectors = mutable.Map.empty[ImportInfo, Set[untpd.ImportSelector]].withDefaultValue(Set.empty)
10291031
private val importInfos = mutable.Map.empty[CompilationUnit, List[(ImportInfo, Symbol)]].withDefaultValue(Nil)
10301032

@@ -1048,19 +1050,56 @@ object Contexts:
10481050
end if
10491051
def checkUsed(info: ImportInfo, owner: Symbol): Unit =
10501052
val used = selectors(info)
1053+
var needsPatch = false
10511054
def cull(toCheck: List[untpd.ImportSelector]): Unit =
10521055
toCheck match
10531056
case selector :: rest =>
10541057
cull(rest) // reverse
10551058
if !selector.isMask && !used(selector) then
10561059
unusages ::= ((info, owner, selector))
1060+
needsPatch = true
10571061
case _ =>
10581062
cull(info.selectors)
1063+
val decidingFactor = false // whether to apply patches
1064+
if decidingFactor && needsPatch && !ctx.settings.rewrite.value.isEmpty then
1065+
val retained = info.selectors.filter(sel => sel.isMask || used(sel))
1066+
val infoPos = info.qualifier.sourcePos
1067+
val lineText = infoPos.lineContent
1068+
val src = ctx.compilationUnit.source
1069+
val lineSpan = src.lineSpan(infoPos.start)
1070+
val selectorSpan = info.selectors.map(_.span).reduce(_ union _)
1071+
val lineSource = SourceFile.virtual(name = "import-line.scala", content = lineText)
1072+
val parser = Parsers.Parser(lineSource)
1073+
val lineTree = parser.parse()
1074+
val PackageDef(_, pieces) = lineTree: @unchecked
1075+
// patch if there's just one import on the line, i.e., not import a.b, c.d
1076+
if pieces.length == 1 then
1077+
if retained.isEmpty then
1078+
patch(src, lineSpan, "") // line deletion
1079+
else if retained.size == 1 && info.selectors.size > 1 then
1080+
var starting = info.selectors.head.span.start
1081+
while starting > lineSpan.start && src.content()(starting) != '{' do starting -= 1
1082+
var ending = info.selectors.last.span.end
1083+
while ending <= lineSpan.end && src.content()(ending) != '}' do ending += 1
1084+
if ending < lineSpan.end then ending += 1 // past the close brace
1085+
val widened = selectorSpan.withStart(starting).withEnd(ending)
1086+
patch(src, widened, toText(retained)) // try to remove braces
1087+
else
1088+
patch(src, selectorSpan, toText(retained))
10591089
end checkUsed
10601090
importInfos.remove(ctx.compilationUnit).foreach(_.foreach(checkUsed))
10611091
unusages
10621092
end unused
10631093

1094+
// just the selectors, no need to add braces
1095+
private def toText(retained: List[untpd.ImportSelector])(using Context): String =
1096+
def selected(sel: untpd.ImportSelector) =
1097+
if sel.isGiven then "given"
1098+
else if sel.isWildcard then "*"
1099+
else if sel.name == sel.rename then sel.name.show
1100+
else s"${sel.name.show} as ${sel.rename.show}"
1101+
retained.map(selected).mkString(", ")
1102+
10641103
def clear()(using Context): Unit =
10651104
importInfos.clear()
10661105
selectors.clear()

compiler/src/dotty/tools/dotc/util/SourceFile.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ import core.Contexts._
99
import scala.io.Codec
1010
import Chars._
1111
import scala.annotation.internal.sharable
12-
import scala.collection.mutable
1312
import scala.collection.mutable.ArrayBuffer
1413

15-
import java.io.IOException
1614
import java.nio.charset.StandardCharsets
1715
import java.util.Optional
18-
import java.util.concurrent.atomic.AtomicInteger
1916
import java.util.regex.Pattern
2017

2118
object ScriptSourceFile {
@@ -56,7 +53,6 @@ object ScriptSourceFile {
5653
}
5754

5855
class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends interfaces.SourceFile {
59-
import SourceFile._
6056

6157
private var myContent: Array[Char] = null
6258

@@ -194,6 +190,10 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
194190
def lineContent(offset: Int): String =
195191
content.slice(startOfLine(offset), nextLine(offset)).mkString
196192

193+
/** The span of the line containing position `offset` */
194+
def lineSpan(offset: Int): Span =
195+
Spans.Span(startOfLine(offset), nextLine(offset))
196+
197197
/** The column corresponding to `offset`, starting at 0 */
198198
def column(offset: Int): Int = {
199199
var idx = startOfLine(offset)

compiler/src/dotty/tools/dotc/util/Spans.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dotty.tools.dotc
22
package util
3-
import language.implicitConversions
43

54
/** The offsets part of a full position, consisting of 2 or 3 entries:
65
* - start: the start offset of the span, in characters from start of file
@@ -143,6 +142,10 @@ object Spans {
143142
def ==(that: Span): Boolean = this.coords == that.coords
144143
def !=(that: Span): Boolean = this.coords != that.coords
145144
}
145+
object Span:
146+
extension (span: Span)
147+
def spread: Int = span.end - span.start
148+
end Span
146149

147150
private def fromOffsets(start: Int, end: Int, pointDelta: Int) =
148151
//assert(start <= end || start == 1 && end == 0, s"$start..$end")

project/Build.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -750,9 +750,10 @@ object Build {
750750
"-Ddotty.tests.classes.dottyTastyInspector=" + jars("scala3-tasty-inspector"),
751751
)
752752
},
753-
scalacOptions ++= Seq(
754-
//"-Wunused:imports",
755-
),
753+
//scalacOptions ++= Seq(
754+
// "-Wunused:imports",
755+
// "-rewrite",
756+
//),
756757
packageAll := {
757758
(`scala3-compiler` / packageAll).value ++ Seq(
758759
"scala3-compiler" -> (Compile / packageBin).value.getAbsolutePath,

0 commit comments

Comments
 (0)