Skip to content

Commit

Permalink
Split warnings method
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Jan 17, 2025
1 parent 4479b5d commit add05d0
Showing 1 changed file with 119 additions and 94 deletions.
213 changes: 119 additions & 94 deletions compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
Original file line number Diff line number Diff line change
Expand Up @@ -469,98 +469,104 @@ object CheckUnused:
val warnings = ArrayBuilder.make[MessageInfo]
def warnAt(pos: SrcPos)(msg: UnusedSymbol, origin: String = ""): Unit = warnings.addOne((msg, pos, origin))
val infos = refInfos
for (sym, pos) <- infos.defs.iterator if !sym.hasAnnotation(defn.UnusedAnnot) do
if infos.refs(sym) then
if sym.isLocalToBlock then
if ctx.settings.WunusedHas.locals && sym.is(Mutable) && !infos.asss(sym) then
warnAt(pos)(UnusedSymbol.unsetLocals)
else if ctx.settings.WunusedHas.privates && sym.isAllOf(Private | Mutable) && !infos.asss(sym) then
warnAt(pos)(UnusedSymbol.unsetPrivates)
else if sym.is(Private, butNot = ParamAccessor) then
if ctx.settings.WunusedHas.privates
&& !sym.isConstructor
&& sym.is(Private, butNot = SelfName | Synthetic | CaseAccessor)
&& !sym.isSerializationSupport
&& !(sym.is(Mutable) && sym.isSetter && sym.owner.is(Trait)) // tracks sym.underlyingSymbol sibling getter
then
warnAt(pos)(UnusedSymbol.privateMembers)
else if sym.is(Param, butNot = Given | Implicit) then
val m = sym.owner
def allowed =
val dd = defn
m.isDeprecated
|| m.is(Synthetic) && !m.isAnonymousFunction
|| m.hasAnnotation(defn.UnusedAnnot) // param of unused method
|| sym.info.isSingleton
|| m.isConstructor && m.owner.thisType.baseClasses.contains(defn.AnnotationClass)
def checkExplicit(): Unit =
// A class param is unused if its param accessor is unused.
// (The class param is not assigned to a field until constructors.)
// A local param accessor warns as a param; a private accessor as a private member.
// Avoid warning for case class elements because they are aliased via unapply.
if m.isPrimaryConstructor then
val alias = m.owner.info.member(sym.name)
if alias.exists then
val aliasSym = alias.symbol
if aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor) && !infos.refs(alias.symbol) then
if aliasSym.is(Local) then
if ctx.settings.WunusedHas.explicits then
warnAt(pos)(UnusedSymbol.explicitParams)
else
if ctx.settings.WunusedHas.privates then
warnAt(pos)(UnusedSymbol.privateMembers)
else if ctx.settings.WunusedHas.explicits
&& !sym.is(Synthetic) // param to setter is unused bc there is no field yet
&& !(sym.owner.is(ExtensionMethod) && {
m.paramSymss.dropWhile(_.exists(_.isTypeParam)) match
case (h :: Nil) :: Nil => h == sym // param is the extended receiver
case _ => false
})
&& !sym.name.isInstanceOf[DerivedName]
&& !ctx.platform.isMainMethod(m)
then
warnAt(pos)(UnusedSymbol.explicitParams)
end checkExplicit
if !infos.skip(m)
&& !allowed
then
checkExplicit()
else if sym.is(Param) then // Given | Implicit
val m = sym.owner
def allowed =
val dd = defn
m.isDeprecated
|| m.is(Synthetic)
|| sym.name.is(ContextFunctionParamName) // a ubiquitous parameter
|| sym.name.is(ContextBoundParamName) && sym.info.typeSymbol.isMarkerTrait // a ubiquitous parameter
|| m.hasAnnotation(dd.UnusedAnnot) // param of unused method
|| sym.info.typeSymbol.match // more ubiquity
case dd.DummyImplicitClass | dd.SubTypeClass | dd.SameTypeClass => true
case _ => false
|| sym.info.isSingleton // DSL friendly
|| sym.isCanEqual
|| sym.info.typeSymbol.hasAnnotation(dd.LanguageFeatureMetaAnnot)
|| sym.info.isInstanceOf[RefinedType] // can't be expressed as a context bound
if ctx.settings.WunusedHas.implicits
&& !infos.skip(m)
&& !allowed
then
if m.isPrimaryConstructor then
val alias = m.owner.info.member(sym.name)
if alias.exists then
val aliasSym = alias.symbol
if aliasSym.is(ParamAccessor) && !infos.refs(alias.symbol) then
warnAt(pos)(UnusedSymbol.implicitParams)
else
warnAt(pos)(UnusedSymbol.implicitParams)
else if sym.isLocalToBlock then
if ctx.settings.WunusedHas.locals
&& !sym.is(InlineProxy)
&& !sym.isCanEqual

def checkUnassigned(sym: Symbol, pos: SrcPos) =
if sym.isLocalToBlock then
if ctx.settings.WunusedHas.locals && sym.is(Mutable) && !infos.asss(sym) then
warnAt(pos)(UnusedSymbol.unsetLocals)
else if ctx.settings.WunusedHas.privates && sym.isAllOf(Private | Mutable) && !infos.asss(sym) then
warnAt(pos)(UnusedSymbol.unsetPrivates)

def checkPrivate(sym: Symbol, pos: SrcPos) =
if ctx.settings.WunusedHas.privates
&& !sym.isConstructor
&& sym.is(Private, butNot = SelfName | Synthetic | CaseAccessor)
&& !sym.isSerializationSupport
&& !(sym.is(Mutable) && sym.isSetter && sym.owner.is(Trait)) // tracks sym.underlyingSymbol sibling getter
then
warnAt(pos)(UnusedSymbol.privateMembers)

def checkParam(sym: Symbol, pos: SrcPos) =
val m = sym.owner
def allowed =
val dd = defn
m.isDeprecated
|| m.is(Synthetic) && !m.isAnonymousFunction
|| m.hasAnnotation(defn.UnusedAnnot) // param of unused method
|| sym.info.isSingleton
|| m.isConstructor && m.owner.thisType.baseClasses.contains(defn.AnnotationClass)
def checkExplicit(): Unit =
// A class param is unused if its param accessor is unused.
// (The class param is not assigned to a field until constructors.)
// A local param accessor warns as a param; a private accessor as a private member.
// Avoid warning for case class elements because they are aliased via unapply.
if m.isPrimaryConstructor then
val alias = m.owner.info.member(sym.name)
if alias.exists then
val aliasSym = alias.symbol
if aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor) && !infos.refs(alias.symbol) then
if aliasSym.is(Local) then
if ctx.settings.WunusedHas.explicits then
warnAt(pos)(UnusedSymbol.explicitParams)
else
if ctx.settings.WunusedHas.privates then
warnAt(pos)(UnusedSymbol.privateMembers)
else if ctx.settings.WunusedHas.explicits
&& !sym.is(Synthetic) // param to setter is unused bc there is no field yet
&& !(sym.owner.is(ExtensionMethod) && {
m.paramSymss.dropWhile(_.exists(_.isTypeParam)) match
case (h :: Nil) :: Nil => h == sym // param is the extended receiver
case _ => false
})
&& !sym.name.isInstanceOf[DerivedName]
&& !ctx.platform.isMainMethod(m)
then
warnAt(pos)(UnusedSymbol.localDefs)
warnAt(pos)(UnusedSymbol.explicitParams)
end checkExplicit
// begin
if !infos.skip(m)
&& !allowed
then
checkExplicit()
end checkParam

def checkImplicit(sym: Symbol, pos: SrcPos) =
val m = sym.owner
def allowed =
val dd = defn
m.isDeprecated
|| m.is(Synthetic)
|| sym.name.is(ContextFunctionParamName) // a ubiquitous parameter
|| sym.name.is(ContextBoundParamName) && sym.info.typeSymbol.isMarkerTrait // a ubiquitous parameter
|| m.hasAnnotation(dd.UnusedAnnot) // param of unused method
|| sym.info.typeSymbol.match // more ubiquity
case dd.DummyImplicitClass | dd.SubTypeClass | dd.SameTypeClass => true
case _ => false
|| sym.info.isSingleton // DSL friendly
|| sym.isCanEqual
|| sym.info.typeSymbol.hasAnnotation(dd.LanguageFeatureMetaAnnot)
|| sym.info.isInstanceOf[RefinedType] // can't be expressed as a context bound
if ctx.settings.WunusedHas.implicits
&& !infos.skip(m)
&& !allowed
then
if m.isPrimaryConstructor then
val alias = m.owner.info.member(sym.name)
if alias.exists then
val aliasSym = alias.symbol
if aliasSym.is(ParamAccessor) && !infos.refs(alias.symbol) then
warnAt(pos)(UnusedSymbol.implicitParams)
else
warnAt(pos)(UnusedSymbol.implicitParams)

if ctx.settings.WunusedHas.patvars then
def checkLocal(sym: Symbol, pos: SrcPos) =
if ctx.settings.WunusedHas.locals
&& !sym.is(InlineProxy)
&& !sym.isCanEqual
then
warnAt(pos)(UnusedSymbol.localDefs)

def checkPatvars() =
// convert the one non-synthetic span so all are comparable
def uniformPos(sym: Symbol, pos: SrcPos): SrcPos =
if pos.span.isSynthetic then pos else pos.sourcePos.withSpan(pos.span.toSynthetic)
Expand All @@ -580,10 +586,10 @@ object CheckUnused:
else if sym.is(Private) then
warnAt(pos)(UnusedSymbol.unsetPrivates)

// TODO check for unused masking import
import scala.jdk.CollectionConverters.given
import Rewrites.ActionPatch
if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then
def checkImports() =
// TODO check for unused masking import
import scala.jdk.CollectionConverters.given
import Rewrites.ActionPatch
type ImpSel = (Import, ImportSelector)
def isUsable(imp: Import, sel: ImportSelector): Boolean =
sel.isImportExclusion || infos.sels.containsKey(sel) || imp.isLoose(sel)
Expand Down Expand Up @@ -707,6 +713,25 @@ object CheckUnused:
index = nextImport
end while

// begin
for (sym, pos) <- infos.defs.iterator if !sym.hasAnnotation(defn.UnusedAnnot) do
if infos.refs(sym) then
checkUnassigned(sym, pos)
else if sym.is(Private, butNot = ParamAccessor) then
checkPrivate(sym, pos)
else if sym.is(Param, butNot = Given | Implicit) then
checkParam(sym, pos)
else if sym.is(Param) then // Given | Implicit
checkImplicit(sym, pos)
else if sym.isLocalToBlock then
checkLocal(sym, pos)

if ctx.settings.WunusedHas.patvars then
checkPatvars()

if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then
checkImports()

warnings.result().sortBy(_._2.span.point)
end warnings

Expand Down

0 comments on commit add05d0

Please sign in to comment.