Permalink
Browse files

Updated Symbol to record classfile origin.

This change should be transparent to anything using sourceFile,
unless it was drinking from the inheritance well too deeply. Rather
than squander the already allocated field for every ClassSymbol not
being compiled from source, I'm now populating it with the file
representing the class. This will make a broad range of things easier,
like debugging, issuing useful error messages, symbol invalidation,
signature verification, you name it.

  def sourceFile      - still returns only source code files
  def binaryFile      - returns only class files
  def associatedFile  - returns whatever is there, if anything

Performance: I may be mistaken, but I believe this is a zero-impact
change.  No new fields are allocated; fields which were null now hold
a useful reference.  The reference is to a file instance which was
already being allocated and already long-lived.

Compare error messages:

  // Version 1
  % scalac a.scala
  error: type _$1 is defined twice

  // Version 2
  % scalac a.scala
  error: type _$1 is defined twice
    conflicting symbols both originated in file './foo/package.class'
    Note: this may be due to a bug in the compiler involving wildcards in package objects
  one error found

Bonus for people who read commit logs.  Try this in the repl
after starting power mode.

ListClass.info.members groupBy (_.associatedFile) foreach {
  case (k, vs) => println("%s\n  %s\n".format(k, vs map (_.defString) mkString "\n  "))
}
  • Loading branch information...
paulp committed May 5, 2012
1 parent ecbf895 commit 0fb12fa0620c5f9999e309a4de7831549b283c24
@@ -162,7 +162,7 @@ trait SymbolFlags {
case _ => abort("setNotFlag on invalid flag: " + flag)
})
protected def shortSymbolClass = getClass.getName.split('.').last.stripPrefix("Symbols$")
def shortSymbolClass = getClass.getName.split('.').last.stripPrefix("Symbols$")
def symbolCreationString: String = (
"%s%25s | %-40s | %s".format(
if (settings.uniqid.value) "%06d | ".format(id) else "",
@@ -2036,13 +2036,27 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def isExpandedModuleClass: Boolean = name(name.length - 1) == '$'
*/
def sourceFile: AbstractFileType =
if (isModule) moduleClass.sourceFile
else enclosingTopLevelClass.sourceFile
def sourceFile_=(f: AbstractFileType) {
abort("sourceFile_= inapplicable for " + this)
}
/** Desire to re-use the field in ClassSymbol which stores the source
* file to also store the classfile, but without changing the behavior
* of sourceFile (which is expected at least in the IDE only to
* return actual source code.) So sourceFile has classfiles filtered out.
*/
private def sourceFileOnly(file: AbstractFileType): AbstractFileType =
if ((file eq null) || (file.path endsWith ".class")) null else file
private def binaryFileOnly(file: AbstractFileType): AbstractFileType =
if ((file eq null) || !(file.path endsWith ".class")) null else file
final def binaryFile: AbstractFileType = binaryFileOnly(associatedFile)
final def sourceFile: AbstractFileType = sourceFileOnly(associatedFile)
/** Overridden in ModuleSymbols to delegate to the module class. */
def associatedFile: AbstractFileType = enclosingTopLevelClass.associatedFile
def associatedFile_=(f: AbstractFileType) { abort("associatedFile_= inapplicable for " + this) }
@deprecated("Use associatedFile_= instead", "2.10.0")
def sourceFile_=(f: AbstractFileType): Unit = associatedFile_=(f)
/** If this is a sealed class, its known direct subclasses.
* Otherwise, the empty set.
@@ -2423,6 +2437,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def distinguishingFlag = MODULE
private var flatname: TermName = null
override def associatedFile = moduleClass.associatedFile
override def associatedFile_=(f: AbstractFileType) { moduleClass.associatedFile = f }
override def isModule = true
override def moduleClass = referenced
override def companionClass =
@@ -2718,9 +2735,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
extends TypeSymbol(initOwner, initPos, initName) with ClassSymbolApi {
type TypeOfClonedSymbol = ClassSymbol
private[this] var flatname: TypeName = _
private[this] var source: AbstractFileType = _
private[this] var thissym: Symbol = this
private[this] var flatname: TypeName = _
private[this] var _associatedFile: AbstractFileType = _
private[this] var thissym: Symbol = this
private[this] var thisTypeCache: Type = _
private[this] var thisTypePeriod = NoPeriod
@@ -2819,10 +2836,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
if (c.isOverloaded) c.alternatives.head else c
}
override def sourceFile =
if (owner.isPackageClass) source
else super.sourceFile
override def sourceFile_=(f: AbstractFileType) { source = f }
override def associatedFile = if (owner.isPackageClass) _associatedFile else super.associatedFile
override def associatedFile_=(f: AbstractFileType) { _associatedFile = f }
override def reset(completer: Type): this.type = {
super.reset(completer)
@@ -2867,6 +2882,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
clone.typeOfThis = typeOfThis
clone.thisSym setName thisSym.name
}
if (_associatedFile ne null)
clone.associatedFile = _associatedFile
clone
}
@@ -3021,7 +3039,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
override def enclosingTopLevelClass: Symbol = this
override def enclosingPackageClass: Symbol = this
override def enclMethod: Symbol = this
override def sourceFile: AbstractFileType = null
override def associatedFile = null
override def ownerChain: List[Symbol] = List()
override def ownersIterator: Iterator[Symbol] = Iterator.empty
override def alternatives: List[Symbol] = List()
@@ -121,8 +121,8 @@ trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable =>
}
trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol {
override def sourceFile = synchronized { super.sourceFile }
override def sourceFile_=(f: AbstractFileType) = synchronized { super.sourceFile_=(f) }
override def associatedFile = synchronized { super.associatedFile }
override def associatedFile_=(f: AbstractFileType) = synchronized { super.associatedFile_=(f) }
override def thisSym: Symbol = synchronized { super.thisSym }
override def thisType: Type = synchronized { super.thisType }
override def typeOfThis: Type = synchronized { super.typeOfThis }
@@ -51,6 +51,7 @@ class PlainFile(val givenPath: Path) extends AbstractFile {
override def output = givenPath.toFile.outputStream()
override def sizeOption = Some(givenPath.length.toInt)
override def toString = path
override def hashCode(): Int = fpath.hashCode
override def equals(that: Any): Boolean = that match {
case x: PlainFile => fpath == x.fpath
@@ -232,6 +232,16 @@ abstract class SymbolLoaders {
protected def doComplete(root: Symbol) {
val start = startTimer(classReadNanos)
classfileParser.parse(classfile, root)
if (root.associatedFile eq null) {
root match {
// In fact, the ModuleSymbol forwards its setter to the module class
case _: ClassSymbol | _: ModuleSymbol =>
debuglog("ClassfileLoader setting %s.associatedFile = %s".format(root.name, classfile))
root.associatedFile = classfile
case _ =>
debuglog("Not setting associatedFile to %s because %s is a %s".format(classfile, root.name, root.shortSymbolClass))
}
}
stopTimer(classReadNanos, start)
}
override def sourcefile: Option[AbstractFile] = classfileParser.srcfile
@@ -1064,7 +1064,8 @@ abstract class ClassfileParser {
def className(name: Name): Name =
name.subName(name.lastPos('.') + 1, name.length)
def enterClassAndModule(entry: InnerClassEntry, completer: global.loaders.SymbolLoader, jflags: Int) {
def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) {
val completer = new global.loaders.ClassfileLoader(file)
val name = entry.originalName
var sflags = toScalaClassFlags(jflags)
val owner = getOwner(jflags)
@@ -1073,6 +1074,8 @@ abstract class ClassfileParser {
val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer
innerModule.moduleClass setInfo global.loaders.moduleClassLoader
List(innerClass, innerModule.moduleClass) foreach (_.associatedFile = file)
scope enter innerClass
scope enter innerModule
@@ -1094,7 +1097,7 @@ abstract class ClassfileParser {
val file = global.classPath.findSourceFile(entry.externalName.toString) getOrElse {
throw new AssertionError(entry.externalName)
}
enterClassAndModule(entry, new global.loaders.ClassfileLoader(file), entry.jflags)
enterClassAndModule(entry, file, entry.jflags)
}
}
}
@@ -622,19 +622,23 @@ trait ContextErrors {
setError(tree)
}
// checkNoDoubleDefs...
// @PP: I hacked the filename in (context0.unit) to work around SI-4893. It would be
// much better if every symbol could offer some idea of where it came from, else
// the obviously untrue claim that something has been defined twice can only frustrate.
// There's no direct test because partest doesn't work, but to reproduce, separately
// compile the next two lines:
// package object foo { val x: Class[_] = null }
// package foo
def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = {
// Most of this hard work is associated with SI-4893.
val isBug = sym0.isAbstractType && sym1.isAbstractType && (sym0.name startsWith "_$")
issueSymbolTypeError(sym0, sym1+" is defined twice in " + context0.unit
+ ( if (isBug) "\n(this error is likely due to a bug in the scala compiler involving wildcards in package objects)" else "" )
val addendums = List(
if (sym0.associatedFile eq sym1.associatedFile)
Some("conflicting symbols both originated in file '%s'".format(sym0.associatedFile.canonicalPath))
else if ((sym0.associatedFile ne null) && (sym1.associatedFile ne null))
Some("conflicting symbols originated in files '%s' and '%s'".format(sym0.associatedFile.canonicalPath, sym1.associatedFile.canonicalPath))
else None ,
if (isBug) Some("Note: this may be due to a bug in the compiler involving wildcards in package objects") else None
)
val addendum = addendums.flatten match {
case Nil => ""
case xs => xs.mkString("\n ", "\n ", "")
}
issueSymbolTypeError(sym0, sym1+" is defined twice" + addendum)
}
// cyclic errors
@@ -293,8 +293,12 @@ object ScalaRunTime {
*/
def stringOf(arg: Any): String = stringOf(arg, scala.Int.MaxValue)
def stringOf(arg: Any, maxElements: Int): String = {
def isScalaClass(x: AnyRef) =
Option(x.getClass.getPackage) exists (_.getName startsWith "scala.")
def packageOf(x: AnyRef) = x.getClass.getPackage match {
case null => ""
case p => p.getName
}
def isScalaClass(x: AnyRef) = packageOf(x) startsWith "scala."
def isScalaCompilerClass(x: AnyRef) = packageOf(x) startsWith "scala.tools.nsc."
// When doing our own iteration is dangerous
def useOwnToString(x: Any) = x match {
@@ -310,7 +314,8 @@ object ScalaRunTime {
case _: TraversableView[_, _] => true
// Don't want to a) traverse infinity or b) be overly helpful with peoples' custom
// collections which may have useful toString methods - ticket #3710
case x: Traversable[_] => !x.hasDefiniteSize || !isScalaClass(x)
// or c) print AbstractFiles which are somehow also Iterable[AbstractFile]s.
case x: Traversable[_] => !x.hasDefiniteSize || !isScalaClass(x) || isScalaCompilerClass(x)
// Otherwise, nothing could possibly go wrong
case _ => false
}
@@ -357,11 +357,11 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
private def compareOutput(dir: File, logFile: File): String = {
val checkFile = getCheckFilePath(dir, kind)
// if check file exists, compare with log file
val diff =
if (checkFile.canRead) compareFiles(logFile, checkFile.jfile)
else file2String(logFile)
// if check file exists, compare with log file
if (diff != "" && fileManager.updateCheck) {
NestUI.verbose("Updating checkfile " + checkFile.jfile)
val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "")
@@ -1,4 +1,2 @@
Impls_1.scala:4: error: macro implementations cannot have implicit parameters other than TypeTag evidences
def foo[U: c.TypeTag: Numeric](c: Ctx) = {
^
one error found
error: macro implementations cannot have implicit parameters other than TypeTag evidences
one error found
@@ -7,7 +7,8 @@ match argument types (List[a])
overloaded-unapply.scala:22: error: cannot resolve overloaded unapply
case List(x, xs) => 7
^
overloaded-unapply.scala:12: error: method unapply is defined twice in overloaded-unapply.scala
overloaded-unapply.scala:12: error: method unapply is defined twice
conflicting symbols both originated in file 'overloaded-unapply.scala'
def unapply[a](xs: List[a]): Option[Null] = xs match {
^
three errors found
@@ -1,6 +1,6 @@
A_3.scala:3: error: type mismatch;
B.scala:3: error: type mismatch;
found : Bippy
required: AC[Integer]
J_2.f(new Bippy())
^
J.f(new Bippy())
^
one error found
@@ -1,5 +1,5 @@
object Test {
def main(args: Array[String]): Unit = {
J_2.f(new Bippy())
J.f(new Bippy())
}
}
@@ -1,6 +1,6 @@
// java: often the java or scala compiler will save us from
// the untruth in the signature, but not always.
public class J_2 {
public class J {
public static Integer f(AC<Integer> x) { return x.f(); }
public static void main(String[] args) {
f(new Bippy());
@@ -1,4 +1,5 @@
t200.scala:7: error: method foo is defined twice in t200.scala
t200.scala:7: error: method foo is defined twice
conflicting symbols both originated in file 't200.scala'
def foo: Int;
^
one error found
@@ -1,4 +1,5 @@
t2779.scala:16: error: method f is defined twice in t2779.scala
t2779.scala:16: error: method f is defined twice
conflicting symbols both originated in file 't2779.scala'
override def f = List(M1)
^
one error found
@@ -4,7 +4,8 @@ t278.scala:5: error: overloaded method value a with alternatives:
does not take type parameters
println(a[A])
^
t278.scala:4: error: method a is defined twice in t278.scala
t278.scala:4: error: method a is defined twice
conflicting symbols both originated in file 't278.scala'
def a = (p:A) => ()
^
two errors found
@@ -0,0 +1,4 @@
error: type _$1 is defined twice
conflicting symbols both originated in file 't5504/s_1.scala'
Note: this may be due to a bug in the compiler involving wildcards in package objects
one error found
@@ -0,0 +1,4 @@
// a.scala
package object foo {
val m: List[_] = Nil
}
@@ -0,0 +1,8 @@
// b.scala
package foo
object Test {
def main(args: Array[String]): Unit = {
println(foo.m)
}
}
@@ -1,4 +1,5 @@
t591.scala:38: error: method input_= is defined twice in t591.scala
t591.scala:38: error: method input_= is defined twice
conflicting symbols both originated in file 't591.scala'
def input_=(in : Input) = {}
^
one error found
@@ -1,13 +1,16 @@
t800.scala:4: error: qualification is already defined as value qualification
val qualification = false;
^
t800.scala:8: error: method qualification is defined twice in t800.scala
t800.scala:8: error: method qualification is defined twice
conflicting symbols both originated in file 't800.scala'
val qualification = false;
^
t800.scala:12: error: value qualification is defined twice in t800.scala
t800.scala:12: error: value qualification is defined twice
conflicting symbols both originated in file 't800.scala'
var qualification = false;
^
t800.scala:16: error: method qualification is defined twice in t800.scala
t800.scala:16: error: method qualification is defined twice
conflicting symbols both originated in file 't800.scala'
var qualification = false;
^
four errors found

0 comments on commit 0fb12fa

Please sign in to comment.