-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[Semester Project] Add new front-end phase for unused entities and add support for unused imports #16157
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
Merged
Merged
[Semester Project] Add new front-end phase for unused entities and add support for unused imports #16157
Changes from 8 commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
27bfda8
Report simple unused import
PaulCoral 93ba6c9
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral f2c1551
Add warnings for unused wildcard imports
PaulCoral d6fa4cb
Add tests and fixes for unused warning
PaulCoral 9f75334
Add warning unused given imports
PaulCoral b131601
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral e37c2f9
Add tests for inline method
PaulCoral 2d6da62
Add an `isRunnable` for `CheckUnused`
PaulCoral 617155d
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral 0801dd6
Ignore exclusion when unused import
PaulCoral a6e7d05
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral e1188b6
Add "-Wunused:locals" warning support
PaulCoral 5447cdb
Add "-Wunused:privates" warning option
PaulCoral 950e7de
Fix CheckUnused phasename
PaulCoral 9ad1daa
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral 5ecb0eb
fix overlapping warning
PaulCoral a1c2bae
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral f993782
Fix warning location
PaulCoral b2fd8cb
Add -Wunused:params,explicits,implicits,patvars
PaulCoral 368748a
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral e69c4a3
Merge branch 'feature/linter/unused' of github.com:PaulCoral/dotty in…
PaulCoral 8ac97b3
Fix requested changes, add new tests
PaulCoral 892621b
Annotation usage, allow unsed Self, add tests
PaulCoral 6d8d20d
Refactor CheckUnused, fix pkg false negative
PaulCoral 412412c
No -Wunused:imports for language imports
PaulCoral f354b96
Clean CheckUnused phase code, add doc+comments
PaulCoral d41c6dd
Remove report unused implicit imports
PaulCoral 95c8a40
Revert to unused implicit check, also synthetics
PaulCoral b2f0fa1
Add warn Annotated Types and "new" kw
PaulCoral 82290c8
Fix unused imports in import stmt
PaulCoral 5a755dd
Reports more import alternatives
PaulCoral 2ed1f1f
Warn unused Imports methods overload
PaulCoral 8a91af1
Add an helpful message for `-W` option
PaulCoral ca8dbaf
Merge branch 'feature/linter/unused' of github.com:PaulCoral/dotty in…
PaulCoral 426d34a
Fix typo in help -W
PaulCoral 5ab55f8
Fix isBlank not member of String on CI
PaulCoral 0cb2af0
Add `-Wunused:strict-no-implicit-warn` warning option
PaulCoral d1f9441
Fix test i15503j
PaulCoral e158a9f
Add better support for unused renamed import
PaulCoral e2b6b61
Clean, refine scope, add comments
PaulCoral b0790d1
Refactor CheckUnused into a MiniPhase
PaulCoral 7f04ce3
Add support for @annotation.unused
PaulCoral 779ec7d
Add support for trivial body
PaulCoral d37d99e
Fix the import precedence in -Wunused:imports
PaulCoral ae24d64
Improve -Wunused:locals
PaulCoral 527aa31
Fix -Wunused:privates trait accessor
PaulCoral bfa20a1
Fix -Wunused:locals,privates with recursive
PaulCoral c70fa68
Fixes for -Wunused:params
PaulCoral 7de90b3
Add better support for trivial methods -Wunused
PaulCoral c89e27c
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral 422ecb8
Merge branch 'lampepfl:main' into feature/linter/unused
PaulCoral 4a06bc6
Remove unused method in CheckUnused
PaulCoral 1db9040
Merge branch 'feature/linter/unused' of github.com:PaulCoral/dotty in…
PaulCoral 08f807c
Make -Wunused:patvars to unsafe
PaulCoral File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package dotty.tools.dotc.transform | ||
|
||
import dotty.tools.dotc.ast.tpd | ||
import dotty.tools.dotc.ast.tpd.TreeTraverser | ||
import dotty.tools.dotc.ast.untpd | ||
import dotty.tools.dotc.ast.untpd.ImportSelector | ||
import dotty.tools.dotc.config.ScalaSettings | ||
import dotty.tools.dotc.core.Contexts._ | ||
import dotty.tools.dotc.core.Decorators.i | ||
import dotty.tools.dotc.core.Flags.Given | ||
import dotty.tools.dotc.core.Phases.Phase | ||
import dotty.tools.dotc.core.StdNames | ||
import dotty.tools.dotc.report | ||
import dotty.tools.dotc.reporting.Message | ||
import dotty.tools.dotc.typer.ImportInfo | ||
import dotty.tools.dotc.util.Property | ||
|
||
/** | ||
* A compiler phase that checks for unused imports or definitions | ||
* | ||
* Basically, it gathers definition/imports and their usage. If a | ||
* definition/imports does not have any usage, then it is reported. | ||
*/ | ||
class CheckUnused extends Phase: | ||
import CheckUnused.UnusedData | ||
|
||
private val _key = Property.Key[UnusedData] | ||
|
||
override def phaseName: String = CheckUnused.phaseName | ||
|
||
override def description: String = CheckUnused.description | ||
|
||
override def isRunnable(using Context): Boolean = | ||
ctx.settings.WunusedHas.imports | ||
|
||
override def run(using Context): Unit = | ||
val tree = ctx.compilationUnit.tpdTree | ||
val data = UnusedData(ctx) | ||
val fresh = ctx.fresh.setProperty(_key, data) | ||
traverser.traverse(tree)(using fresh) | ||
PaulCoral marked this conversation as resolved.
Show resolved
Hide resolved
|
||
reportUnusedImport(data.getUnused) | ||
|
||
/** | ||
* This traverse is the **main** component of this phase | ||
* | ||
* It traverse the tree the tree and gather the data in the | ||
* corresponding context property | ||
*/ | ||
private def traverser = new TreeTraverser { | ||
import tpd._ | ||
|
||
override def traverse(tree: tpd.Tree)(using Context): Unit = tree match | ||
case imp@Import(_, sels) => sels.foreach { s => | ||
ctx.property(_key).foreach(_.registerImport(imp)) | ||
} | ||
case ident: Ident => | ||
val id = ident.symbol.id | ||
PaulCoral marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ctx.property(_key).foreach(_.registerUsed(id)) | ||
traverseChildren(tree) | ||
case sel: Select => | ||
val id = sel.symbol.id | ||
ctx.property(_key).foreach(_.registerUsed(id)) | ||
traverseChildren(tree) | ||
case tpd.Block(_,_) | tpd.Template(_,_,_,_) => | ||
ctx.property(_key).foreach(_.pushScope()) | ||
traverseChildren(tree) | ||
ctx.property(_key).foreach(_.popScope()) | ||
case _ => traverseChildren(tree) | ||
|
||
} | ||
|
||
private def reportUnusedImport(sels: List[ImportSelector])(using Context) = | ||
if ctx.settings.WunusedHas.imports then | ||
sels.foreach { s => | ||
report.warning(i"unused import", s.srcPos) | ||
} | ||
end CheckUnused | ||
|
||
object CheckUnused: | ||
val phaseName: String = "check unused" | ||
PaulCoral marked this conversation as resolved.
Show resolved
Hide resolved
|
||
val description: String = "check for unused elements" | ||
|
||
/** | ||
* A stateful class gathering the infos on : | ||
* - imports | ||
* - definitions | ||
* - usage | ||
*/ | ||
private class UnusedData(initctx: Context): | ||
import collection.mutable.{Set => MutSet, Map => MutMap, Stack, ListBuffer} | ||
|
||
|
||
private val used = Stack(MutSet[Int]()) | ||
private val impInScope = Stack(MutMap[Int, ListBuffer[ImportSelector]]()) | ||
private val unused = ListBuffer[ImportSelector]() | ||
|
||
private def isImportExclusion(sel: ImportSelector): Boolean = sel.renamed match | ||
case [email protected](name) => name == StdNames.nme.WILDCARD | ||
case _ => false | ||
|
||
/** Register the id of a found (used) symbol */ | ||
def registerUsed(id: Int): Unit = used.top += id | ||
|
||
/** Register an import */ | ||
def registerImport(imp: tpd.Import)(using Context): Unit = | ||
val tpd.Import(tree, sels) = imp | ||
val map = impInScope.top | ||
val entries = sels.flatMap{ s => | ||
if s.isWildcard then | ||
tree.tpe.allMembers | ||
.filter(m => m.symbol.is(Given) == s.isGiven) // given imports | ||
.map(_.symbol.id -> s) | ||
else | ||
val id = tree.tpe.member(s.name.toTermName).symbol.id | ||
val typeId = tree.tpe.member(s.name.toTypeName).symbol.id | ||
List(id -> s, typeId -> s) | ||
} | ||
entries.foreach{(id, sel) => | ||
map.get(id) match | ||
case None => map.put(id, ListBuffer(sel)) | ||
case Some(value) => value += sel | ||
} | ||
|
||
/** enter a new scope */ | ||
def pushScope(): Unit = | ||
used.push(MutSet()) | ||
impInScope.push(MutMap()) | ||
|
||
/** leave the current scope */ | ||
def popScope(): Unit = | ||
val usedImp = MutSet[ImportSelector]() | ||
val poppedImp = impInScope.pop() | ||
val notDefined = used.pop().filter{id => | ||
poppedImp.remove(id) match | ||
case None => true | ||
case Some(value) => | ||
usedImp.addAll(value) | ||
false | ||
} | ||
if used.size > 0 then | ||
used.top.addAll(notDefined) | ||
poppedImp.values.flatten.foreach{ sel => | ||
// If **any** of the entities used by the import is used, | ||
// do not add to the `unused` Set | ||
if !usedImp(sel) then | ||
unused += sel | ||
} | ||
|
||
/** | ||
* Leave the scope and return a `List` of unused `ImportSelector`s | ||
* | ||
* The given `List` is sorted by line and then column of the position | ||
*/ | ||
def getUnused(using Context): List[ImportSelector] = | ||
popScope() | ||
unused.toList.sortBy{ sel => | ||
val pos = sel.srcPos.sourcePos | ||
(pos.line, pos.column) | ||
} | ||
|
||
end UnusedData | ||
end CheckUnused | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// scalac: -Wunused:imports | ||
|
||
|
||
object FooUnused: | ||
import collection.mutable.Set // error | ||
import collection.mutable.{Map => MutMap} // error | ||
import collection.mutable._ // error | ||
|
||
object FooWildcardUnused: | ||
import collection.mutable._ // error | ||
|
||
object Foo: | ||
import collection.mutable.Set // OK | ||
import collection.mutable.{Map => MutMap} // OK | ||
|
||
val bar = Set() // OK | ||
val baz = MutMap() // OK | ||
|
||
object FooWildcard: | ||
import collection.mutable._ // OK | ||
|
||
val bar = Set() // OK | ||
|
||
object FooNestedUnused: | ||
import collection.mutable.Set // error | ||
object Nested: | ||
def hello = 1 | ||
|
||
object FooNested: | ||
import collection.mutable.Set // OK | ||
object Nested: | ||
def hello = Set() | ||
|
||
object FooGivenUnused: | ||
import SomeGivenImports.given // error | ||
|
||
object FooGiven: | ||
import SomeGivenImports.given // OK | ||
import SomeGivenImports._ // error | ||
|
||
val foo = summon[Int] | ||
|
||
/** | ||
* Import used as type name are considered | ||
* as used. | ||
* | ||
* Import here are only used as types, not as | ||
* Term | ||
*/ | ||
object FooTypeName: | ||
import collection.mutable.Set // OK | ||
import collection.mutable.Map // OK | ||
import collection.mutable.Seq // OK | ||
import collection.mutable.ArrayBuilder // OK | ||
import collection.mutable.ListBuffer // error | ||
|
||
def checkImplicit[A](using Set[A]) = () | ||
def checkParamType[B](a: Map[B,B]): Seq[B] = ??? | ||
def checkTypeParam[A] = () | ||
|
||
checkTypeParam[ArrayBuilder[Int]] | ||
|
||
|
||
object InlineChecks: | ||
object InlineFoo: | ||
import collection.mutable.Set // OK | ||
import collection.mutable.Map // error | ||
inline def getSet = Set(1) | ||
|
||
object InlinedBar: | ||
import collection.mutable.Set // error | ||
import collection.mutable.Map // error | ||
val a = InlineFoo.getSet | ||
|
||
object MacroChecks: | ||
object StringInterpol: | ||
import collection.mutable.Set // OK | ||
import collection.mutable.Map // OK | ||
println(s"This is a mutableSet : ${Set[Map[Int,Int]]()}") | ||
|
||
/** | ||
* Some given values for the test | ||
*/ | ||
object SomeGivenImports: | ||
given Int = 0 | ||
given String = "foo" | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.