8000 Merge pull request #8712 from dwijnand/2.12/favour-class-based · scala/scala@4c726db · GitHub
[go: up one dir, main page]

Skip to content

Commit 4c726db

Browse files
authored
Merge pull request #8712 from dwijnand/2.12/favour-class-based
[2.12] Improve -Yrepl-class-based
2 parents c919d61 + eb5862b commit 4c726db

34 files changed

+892
-177
lines changed

src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ abstract class DefaultMacroCompiler extends Resolvers
8686
case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) =>
8787
val bundle = result.tpe.typeSymbol
8888
if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError()
89-
if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError()
89+
if (!bundle.owner.isStaticOwner) {
90+
val isReplClassBased = settings.Yreplclassbased.value && bundle.owner.enclosingTopLevelClass.isInterpreterWrapper
91+
MacroBundleNonStaticError(isReplClassBased)
92+
}
9093
bundleResult.get
9194
case _ =>
9295
vanillaResult.get

src/compiler/scala/reflect/macros/compiler/Errors.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,15 @@ trait Errors extends Traces {
4545
"macro implementation reference is ambiguous: makes sense both as\n"+
4646
"a macro bundle method reference and a vanilla object method reference")
4747

48-
def MacroBundleNonStaticError() = bundleRefError("macro bundles must be st F438 atic")
48+
private def replClassBasedMacroAddendum(isReplClassBased: Boolean): String =
49+
if (isReplClassBased)
50+
"\nnote: macro definition is not supported in the REPL when using -Yrepl-classbased."
51+
else ""
52+
53+
54+
def MacroBundleNonStaticError(isReplClassBased: Boolean) = {
55+
bundleRefError("macro bundles must be static" + replClassBasedMacroAddendum(isReplClassBased))
56+
}
4957

5058
def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter")
5159

@@ -54,10 +62,10 @@ trait Errors extends Traces {
5462

5563
// sanity check errors
5664

57-
def MacroImplReferenceWrongShapeError() = implRefError(
65+
def MacroImplReferenceWrongShapeError(isReplClassBased: Boolean = false) = implRefError(
5866
"macro implementation reference has wrong shape. required:\n"+
5967
"macro [<static object>].<method name>[[<type args>]] or\n" +
60-
"macro [<macro bundle>].<method name>[[<type args>]]")
68+
"macro [<macro bundle>].<method name>[[<type args>]]" + replClassBasedMacroAddendum(isReplClassBased))
6169

6270
def MacroImplWrongNumberOfTypeArgumentsError() = {
6371
val diagnostic = if (macroImpl.typeParams.length > targs.length) "has too few type arguments" else "has too many arguments"

src/compiler/scala/reflect/macros/compiler/Validators.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ trait Validators {
4141
val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner
4242
val effectivelyStatic = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner
4343
val correctBundleness = if (isImplMethod) macroImplOwner.isModuleClass else macroImplOwner.isClass && !macroImplOwner.isModuleClass
44-
if (!effectivelyStatic || !correctBundleness) MacroImplReferenceWrongShapeError()
44+
if (!effectivelyStatic || !correctBundleness) {
45+
val isReplClassBased = settings.Yreplclassbased.value && effectiveOwner.enclosingTopLevelClass.isInterpreterWrapper
46+
MacroImplReferenceWrongShapeError(isReplClassBased)
47+
}
4548
}
4649

4750
private def checkMacroDefMacroImplCorrespondence() = {

src/compiler/scala/tools/nsc/settings/MutableSettings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ class MutableSettings(val errorFn: String => Unit, val pathFactory: PathFactory)
367367
* Subclasses each define a `value` field of the appropriate type.
368368
*/
369369
abstract class Setting(val name: String, val helpDescription: String) extends AbsSetting with SettingValue with Mutable {
370+
def withDefault(value: T): this.type = { v = value; this }
371+
370372
/** Will be called after this Setting is set for any extra work. */
371373
private var _postSetHook: this.type => Unit = (x: this.type) => ()
372374
override def postSetHook(): Unit = _postSetHook(this)

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ trait ScalaSettings extends AbsScalaSettings
237237
val YmacroFresh = BooleanSetting ("-Ymacro-global-fresh-names", "Should fresh names in macros be unique across all compilation units")
238238
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
239239
val Yreplclassbased = BooleanSetting ("-Yrepl-class-based", "Use classes to wrap REPL snippets instead of objects")
240-
val YreplMagicImport = BooleanSetting ("-Yrepl-use-magic-imports", "In the code the wraps REPL snippes, use magic imports to rather than nesting wrapper object/classes")
240+
val YreplMagicImport = BooleanSetting ("-Yrepl-use-magic-imports", "In the code that wraps REPL snippets, use magic imports rather than nesting wrapper object/classes")
241241
val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "")
242242
val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overridden methods.")
243243
val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")

src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala

Lines changed: 136 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -489,147 +489,152 @@ trait TypeDiagnostics {
489489
def inMode(context: Context, mode: Mode, tree: Tree): Tree = if (mode.typingMonoExprByValue) apply(context, tree) else tree
490490
}
491491

492-
class checkUnused(typer: Typer) {
492+
object UnusedPrivates {
493493
val ignoreNames: Set[TermName] = Set(
494494
"readResolve", "readObject", "writeObject", "writeReplace"
495495
).map(TermName(_))
496+
}
496497

497-
class UnusedPrivates extends Traverser {
498-
val defnTrees = ListBuffer[MemberDef]()
499-
val targets = mutable.Set[Symbol]()
500-
val setVars = mutable.Set[Symbol]()
501-
val treeTypes = mutable.Set[Type]()
502-
val params = mutable.Set[Symbol]()
503-
val patvars = mutable.Set[Symbol]()
504-
505-
def defnSymbols = defnTrees.toList map (_.symbol)
506-
def localVars = defnSymbols filter (t => t.isLocalToBlock && t.isVar)
507-
508-
def qualifiesTerm(sym: Symbol) = (
509-
(sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocalToBlock)
510-
&& !nme.isLocalName(sym.name)
511-
&& !sym.isParameter
512-
&& !sym.isParamAccessor // could improve this, but it's a pain
513-
&& !sym.isEarlyInitialized // lots of false positives in the way these are encoded
514-
&& !(sym.isGetter && sym.accessed.isEarlyInitialized)
515-
)
516-
def qualifiesType(sym: Symbol) = !sym.isDefinedInPackage
517-
def qualifies(sym: Symbol) = (
518-
(sym ne null)
519-
&& (sym.isTerm && qualifiesTerm(sym) || sym.isType && qualifiesType(sym))
520-
)
521-
def isExisting(sym: Symbol) = sym != null && sym.exists
522-
523-
override def traverse(t: Tree): Unit = {
524-
val sym = t.symbol
525-
t match {
526-
case m: MemberDef if qualifies(sym) && !t.isErrorTyped =>
527-
t match {
528-
case ValDef(mods@_, name@_, tpt@_, rhs@_) if wasPatVarDef(t) =>
529-
if (settings.warnUnusedPatVars && !atBounded(t)) patvars += sym
530-
case DefDef(mods@_, name@_, tparams@_, vparamss, tpt@_, rhs@_) if !sym.isAbstract && !sym.isDeprecated && !sym.isMacro =>
531-
if (sym.isPrimaryConstructor)
532-
for (cpa <- sym.owner.constrParamAccessors if cpa.isPrivateLocal) params += cpa
533-
else if (sym.isSynthetic && sym.isImplicit) return
534-
else if (!sym.isConstructor && rhs.symbol != Predef_???)
535-
for (vs <- vparamss) params ++= vs.map(_.symbol)
536-
defnTrees += m
537-
case _ =>
538-
defnTrees += m
539-
}
540-
case CaseDef(pat, guard@_, rhs@_) if settings.warnUnusedPatVars && !t.isErrorTyped =>
541-
pat.foreach {
542-
case b @ Bind(n, _) if !atBounded(b) && n != nme.DEFAULT_CASE => patvars += b.symbol
543-
case _ =>
544-
}
545-
case _: RefTree if isExisting(sym) => targets += sym
546-
case Assign(lhs, _) if isExisting(lhs.symbol) => setVars += lhs.symbol
547-
case Function(ps, _) if settings.warnUnusedParams && !t.isErrorTyped => params ++=
548-
ps.filterNot(p => atBounded(p) || p.symbol.isSynthetic).map(_.symbol)
549-
case _ =>
550-
}
498+
class UnusedPrivates extends Traverser {
499+
import UnusedPrivates.ignoreNames
500+
def isEffectivelyPrivate(sym: Symbol): Boolean = false
501+
val defnTrees = ListBuffer[MemberDef]()
502+
val targets = mutable.Set[Symbol]()
503+
val setVars = mutable.Set[Symbol]()
504+
val treeTypes = mutable.Set[Type]()
505+
val params = mutable.Set[Symbol]()
506+
val patvars = mutable.Set[Symbol]()
507+
508+
def defnSymbols = defnTrees.toList map (_.symbol)
509+
def localVars = defnSymbols filter (t => t.isLocalToBlock && t.isVar)
510+
511+
def qualifiesTerm(sym: Symbol) = (
512+
(sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocalToBlock || isEffectivelyPrivate(sym))
513+
&& !nme.isLocalName(sym.name)
514+
&& !sym.isParameter
515+
&& !sym.isParamAccessor // could improve this, but it's a pain
516+
&& !sym.isEarlyInitialized // lots of false positives in the way these are encoded
517+
&& !(sym.isGetter && sym.accessed.isEarlyInitialized)
518+
)
519+
def qualifiesType(sym: Symbol) = !sym.isDefinedInPackage
520+
def qualifies(sym: Symbol) = (
521+
(sym ne null)
522+
&& (sym.isTerm && qualifiesTerm(sym) || sym.isType && qualifiesType(sym))
523+
)
524+
def isExisting(sym: Symbol) = sym != null && sym.exists
551525

552-
if (t.tpe ne null) {
553-
for (tp <- t.tpe) if (!treeTypes(tp)) {
554-
// Include references to private/local aliases (which might otherwise refer to an enclosing class)
555-
val isAlias = {
556-
val td = tp.typeSymbolDirect
557-
td.isAliasType && (td.isLocal || td.isPrivate)
558-
}
559-
// Ignore type references to an enclosing class. A reference to C must be outside C to avoid warning.
560-
if (isAlias || !currentOwner.hasTransOwner(tp.typeSymbol)) tp match {
561-
case NoType | NoPrefix =>
562-
case NullaryMethodType(_) =>
563-
case MethodType(_, _) =>
564-
case SingleType(_, _) =>
565-
case ConstantType(Constant(k: Type)) =>
566-
log(s"classOf $k referenced from $currentOwner")
567-
treeTypes += k
568-
case _ =>
569-
log(s"${if (isAlias) "alias " else ""}$tp referenced from $currentOwner")
570-
treeTypes += tp
571-
}
526+
override def traverse(t: Tree): Unit = {
527+
val sym = t.symbol
528+
t match {
529+
case m: MemberDef if qualifies(sym) && !t.isErrorTyped =>
530+
t match {
531+
case ValDef(mods@_, name@_, tpt@_, rhs@_) if wasPatVarDef(t) =>
532+
if (settings.warnUnusedPatVars && !atBounded(t)) patvars += sym
533+
case DefDef(mods@_, name@_, tparams@_, vparamss, tpt@_, rhs@_) if !sym.isAbstract && !sym.isDeprecated && !sym.isMacro =>
534+
if (sym.isPrimaryConstructor)
535+
for (cpa <- sym.owner.constrParamAccessors if cpa.isPrivateLocal) params += cpa
536+
else if (sym.isSynthetic && sym.isImplicit) return
537+
else if (!sym.isConstructor && rhs.symbol != Predef_???)
538+
for (vs <- vparamss) params ++= vs.map(_.symbol)
539+
defnTrees += m
540+
case _ =>
541+
defnTrees += m
572542
}
573-
// e.g. val a = new Foo ; new a.Bar ; don't let a be reported as unused.
574-
for (p <- t.tpe.prefix) condOpt(p) {
575-
case SingleType(_, sym) => targets += sym
543+
case CaseDef(pat, guard@_, rhs@_) if settings.warnUnusedPatVars && !t.isErrorTyped =>
544+
pat.foreach {
545+
case b @ Bind(n, _) if !atBounded(b) && n != nme.DEFAULT_CASE => patvars += b.symbol
546+
case _ =>
576547
}
577-
}
578-
super.traverse(t)
548+
case _: RefTree if isExisting(sym) => targets += sym
549+
case Assign(lhs, _) if isExisting(lhs.symbol) => setVars += lhs.symbol
550+
case Function(ps, _) if settings.warnUnusedParams && !t.isErrorTyped => params ++=
551+
ps.filterNot(p => atBounded(p) || p.symbol.isSynthetic).map(_.symbol)
552+
case _ =>
579553
}
580-
def isUnusedType(m: Symbol): Boolean = (
581-
m.isType
582-
&& !m.isTypeParameterOrSkolem // would be nice to improve this
583-
&& (m.isPrivate || m.isLocalToBlock)
584-
&& !(treeTypes.exists(_.exists(_.typeSymbolDirect == m)))
585-
)
586-
def isSyntheticWarnable(sym: Symbol) = (
587-
sym.isDefaultGetter
588-
)
589-
def isUnusedTerm(m: Symbol): Boolean = (
590-
m.isTerm
591-
&& (!m.isSynthetic || isSyntheticWarnable(m))
592-
&& ((m.isPrivate && !(m.isConstructor && m.owner.isAbstract)) || m.isLocalToBlock)
593-
&& !targets(m)
594-
&& !(m.name == nme.WILDCARD) // e.g. val _ = foo
595-
&& (m.isValueParameter || !ignoreNames(m.name.toTermName)) // serialization methods
596-
&& !isConstantType(m.info.resultType) // subject to constant inlining
597-
&& !treeTypes.exists(_ contains m) // e.g. val a = new Foo ; new a.Bar
598-
)
599-
def isUnusedParam(m: Symbol): Boolean = (
600-
isUnusedTerm(m)
601-
&& !m.isDeprecated
602-
&& !m.owner.isDefaultGetter
603-
&& !(m.isParamAccessor && (
604-
m.owner.isImplicit ||
605-
targets.exists(s => s.isParameter
606-
&& s.name == m.name && s.owner.isConstructor && s.owner.owner == m.owner) // exclude ctor params
607-
))
608-
)
609-
def sympos(s: Symbol): Int =
610-
if (s.pos.isDefined) s.pos.point else if (s.isTerm) s.asTerm.referenced.pos.point else -1
611-
def treepos(t: Tree): Int =
612-
if (t.pos.isDefined) t.pos.point else sympos(t.symbol)
613-
614-
def unusedTypes = defnTrees.toList.filter(t => isUnusedType(t.symbol)).sortBy(treepos)
615-
def unusedTerms = {
616-
val all = defnTrees.toList.filter(v => isUnusedTerm(v.symbol))
617-
618-
// is this a getter-setter pair? and why is this a difficult question for traits?
619-
def sameReference(g: Symbol, s: Symbol) =
620-
if (g.accessed.exists && s.accessed.exists) g.accessed == s.accessed
621-
else g.owner == s.owner && g.setterName == s.name //sympos(g) == sympos(s)
622-
623-
// filter out setters if already warning for getter.
624-
val clean = all.filterNot(v => v.symbol.isSetter && all.exists(g => g.symbol.isGetter && sameReference(g.symbol, v.symbol)))
625-
clean.sortBy(treepos)
554+
555+
if (t.tpe ne null) {
556+
for (tp <- t.tpe) if (!treeTypes(tp)) {
557+
// Include references to private/local aliases (which might otherwise refer to an enclosing class)
558+
val isAlias = {
559+
val td = tp.typeSymbolDirect
560+
td.isAliasType && (td.isLocal || td.isPrivate)
561+
}
562+
// Ignore type references to an enclosing class. A reference to C must be outside C to avoid warning.
563+
if (isAlias || !currentOwner.hasTransOwner(tp.typeSymbol)) tp match {
564+
case NoType | NoPrefix =>
565+
case NullaryMethodType(_) =>
566+
case MethodType(_, _) =>
567+
case SingleType(_, _) =>
568+
case ConstantType(Constant(k: Type)) =>
569+
log(s"classOf $k referenced from $currentOwner")
570+
treeTypes += k
571+
case _ =>
572+
log(s"${if (isAlias) "alias " else ""}$tp referenced from $currentOwner")
573+
treeTypes += tp
574+
}
575+
}
576+
// e.g. val a = new Foo ; new a.Bar ; don't let a be reported as unused.
577+
for (p <- t.tpe.prefix) condOpt(p) {
578+
case SingleType(_, sym) => targets += sym
579+
}
626580
}
627-
// local vars which are never set, except those already returned in unused
628-
def unsetVars = localVars.filter(v => !setVars(v) && !isUnusedTerm(v)).sortBy(sympos)
629-
def unusedParams = params.toList.filter(isUnusedParam).sortBy(sympos)
630-
def inDefinedAt(p: Symbol) = p.owner.isMethod && p.owner.name == nme.isDefinedAt && p.owner.owner.isAnonymousFunction
631-
def unusedPatVars = patvars.toList.filter(p => isUnusedTerm(p) && !inDefinedAt(p)).sortBy(sympos)
581+
super.traverse(t)
632582
}
583+
def isUnusedType(m: Symbol): Boolean = (
584+
m.isType
585+
&& !m.isTypeParameterOrSkolem // would be nice to improve this
586+
&& (m.isPrivate || m.isLocalToBlock || isEffectivelyPrivate(m))
587+
&& !(treeTypes.exists(_.exists(_.typeSymbolDirect == m)))
588+
)
589+
def isSyntheticWarnable(sym: Symbol) = (
590+
sym.isDefaultGetter
591+
)
592+
def isUnusedTerm(m: Symbol): Boolean = (
593+
m.isTerm
594+
&& (!m.isSynthetic || isSyntheticWarnable(m))
595+
&& ((m.isPrivate && !(m.isConstructor && m.owner.isAbstract)) || m.isLocalToBlock || isEffectivelyPrivate(m))
596+
&& !targets(m)
597+
&& !(m.name == nme.WILDCARD) // e.g. val _ = foo
598+
&& (m.isValueParameter || !ignoreNames(m.name.toTermName)) // serialization/repl methods
599+
&& !isConstantType(m.info.resultType) // subject to constant inlining
600+
&& !treeTypes.exists(_ contains m) // e.g. val a = new Foo ; new a.Bar
601+
)
602+
def isUnusedParam(m: Symbol): Boolean = (
603+
isUnusedTerm(m)
604+
&& !m.isDeprecated
605+
&& !m.owner.isDefaultGetter
606+
&& !(m.isParamAccessor && (
607+
m.owner.isImplicit ||
608+
targets.exists(s => s.isParameter
609+
&& s.name == m.name && s.owner.isConstructor && s.owner.owner == m.owner) // exclude ctor params
610+
))
611+
)
612+
def sympos(s: Symbol): Int =
613+
if (s.pos.isDefined) s.pos.point else if (s.isTerm) s.asTerm.referenced.pos.point else -1
614+
def treepos(t: Tree): Int =
615+
if (t.pos.isDefined) t.pos.point else sympos(t.symbol)
616+
617+
def unusedTypes = defnTrees.toList.filter(t => isUnusedType(t.symbol)).sortBy(treepos)
618+
def unusedTerms = {
619+
val all = defnTrees.toList.filter(v => isUnusedTerm(v.symbol))
620+
621+
// is this a getter-setter pair? and why is this a difficult question for traits?
622+
def sameReference(g: Symbol, s: Symbol) =
623+
if (g.accessed.exists && s.accessed.exists) g.accessed == s.accessed
624+
else g.owner == s.owner && g.setterName == s.name //sympos(g) == sympos(s)
625+
626+
// filter out setters if already warning for getter.
627+
val clean = all.filterNot(v => v.symbol.isSetter && all.exists(g => g.symbol.isGetter && sameReference(g.symbol, v.symbol)))
628+
clean.sortBy(treepos)
629+
}
630+
// local vars which are never set, except those already returned in unused
631+
def unsetVars = localVars.filter(v => !setVars(v) && !isUnusedTerm(v)).sortBy(sympos)
632+
def unusedParams = params.toList.filter(isUnusedParam).sortBy(sympos)
633+
def inDefinedAt(p: Symbol) = p.owner.isMethod && p.owner.name == nme.isDefinedAt && p.owner.owner.isAnonymousFunction
634+
def unusedPatVars = patvars.toList.filter(p => isUnusedTerm(p) && !inDefinedAt(p)).sortBy(sympos)
635+
}
636+
637+
class checkUnused(typer: Typer) {
633638

634639
object skipMacroCall extends UnusedPrivates {
635640
override def qualifiesTerm(sym: Symbol): Boolean =

0 commit comments

Comments
 (0)
0