Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactoring: Moving safeSymbol into its own ``SafeSymbol`` trait

Ignoring tests for existential types

Problem is that the compiler Tree created for an existential uses ``Ident`` AST nodes,
which do not have a symbol. Hence, it is currently not possible to classify existentials correctly.
  • Loading branch information...
commit fbb7d128a8a54d6f2b50677c33ca8cc5c37e4b8f 1 parent 3a74e14
@dotta dotta authored
View
34 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeTest.scala
@@ -42,5 +42,39 @@ class TypeTest extends AbstractSymbolClassifierTest {
""",
Map("T" -> Type, "V" -> TemplateVal, "C" -> Class))
}
+
+ @Test
+ @Ignore
+ def classify_existential_type_1() {
+ checkSymbolClassification(
+ """
+ object O {
+ def m(x : t forSome {type t <: AnyRef}) = x
+ }
+ """",
+ """
+ object O {
+ def m(x : t forSome {type t <: $ TPE$ }) = x
+ }
+ """",
+ Map("TPE" -> Type))
+ }
+
+ @Test
+ @Ignore
+ def classify_existential_type_2() {
+ checkSymbolClassification(
+ """
+ object O {
+ def m(x : t forSome {type t <: List [_]}) = x
+ }
+ """",
+ """
+ object O {
+ def m(x : t forSome {type t <: $TPE$[_]}) = x
+ }
+ """",
+ Map("TPE" -> Type))
+ }
}
View
87 org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
@@ -0,0 +1,87 @@
+package scala.tools.eclipse.semantichighlighting.classifier
+
+import scala.tools.refactoring.common.CompilerAccess
+import scala.tools.refactoring.common.PimpedTrees
+import scala.tools.eclipse.ScalaPresentationCompiler
+import scala.tools.nsc.util.SourceFile
+
+/**
+ * Return the Symbols corresponding to this `Tree`, if any.
+ *
+ * Scala trees contain symbols when they define or reference a definition. We
+ * need to special-case `TypeTree`, because it does not return `true` on `hasSymbol`,
+ * but nevertheless it may refer to a symbol through its type. Examples of `TypeTree`s
+ * are the type of `ValDef`s after type checking.
+ *
+ * Another special case is `Import`, because it's selectors are not trees, therefore
+ * they do not have a symbol. However, it is desirable to color them, so the symbols are
+ * looked up on the fly.
+ *
+ * A single tree may define more than one symbol, usually with the same name. For instance:
+ * - a 'val' defines both a private field and a getter
+ * - a 'var' defines a private field, and getter/setter methods
+ * - a class parameter defines a constructor parameter, possibly a field and a getter
+ * - Import will generate one per selector
+ *
+ * Lazy values are special-cased because the underlying local var has a different
+ * name and there are no trees for the getter yet (added by phase lazyvals). We need
+ * to return the accessor, who can later be classified as `lazy`.
+ */
+trait SafeSymbol extends CompilerAccess with PimpedTrees {
+
+ val global: ScalaPresentationCompiler
+
+ protected def sourceFile: SourceFile
+
+ import global._
+
+ /**
+ * Trees that have a direct correspondence in the source code have a RangePosition.
+ * TransparentPositions come into play for trees that don't have a source-code
+ * correspondence but still have children that are visible in the source.
+ */
+ protected def isSourceTree(t: Tree): Boolean = t.pos.isRange && !t.pos.isTransparent
+
+ protected def safeSymbol(t: Tree): List[(Symbol, Position)] = t match {
+
+ case tpeTree: TypeTree =>
+ val originalSym =
+ if (tpeTree.original == tpeTree) Nil
+ else safeSymbol(tpeTree.original)
+
+ // if the original tree did not find anything, we need to call
+ // symbol, which may trigger type checking of the underlying tree, so we
+ // wrap it in 'ask'
+ if (originalSym.isEmpty) {
+ val tpeSym = global.askOption(() => Option(t.symbol)).flatten.toList
+ tpeSym.zip(List(tpeTree.namePosition))
+ } else originalSym
+
+ case Import(expr, selectors) =>
+ (for (ImportSelector(name, namePos, _, _) <- selectors) yield {
+ // create a range position for this selector.
+ // TODO: remove special casing once scalac is fixed, and ImportSelectors are proper trees,
+ // with real positions, instead of just an Int
+ val pos = rangePos(sourceFile, namePos, namePos, namePos + name.length)
+
+ val sym1 = global.askOption { () =>
+ val typeSym = expr.tpe.member(name.toTypeName)
+ if (typeSym.exists) typeSym
+ else expr.tpe.member(name.toTermName)
+ }.getOrElse(NoSymbol)
+ if (sym1 eq NoSymbol) List()
+ else if (sym1.isOverloaded) sym1.alternatives.take(1).zip(List(pos))
+ else List((sym1, pos))
+ }).flatten
+
+ case _ =>
+ // the local variable backing a lazy value is called 'originalName$lzy'. We swap it here for its
+ // accessor, otherwise this symbol would fail the test in `getNameRegion`
+ val sym1 = Option(t.symbol).map { sym =>
+ if (sym.isLazy && sym.isMutable) sym.lazyAccessor
+ else sym
+ }.toList
+
+ sym1.zip(List(t.namePosition))
+ }
+}
View
88 org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SymbolClassification.scala
@@ -46,95 +46,25 @@ object SymbolClassification {
}
class SymbolClassification(protected val sourceFile: SourceFile, val global: ScalaPresentationCompiler, useSyntacticHints: Boolean)
- extends CompilerAccess with PimpedTrees with SymbolClassificationDebugger with SymbolTests with HasLogger {
+ extends SafeSymbol with SymbolClassificationDebugger with SymbolTests with HasLogger {
import SymbolClassification._
import global._
-
def compilationUnitOfFile(f: AbstractFile) = global.unitOfFile.get(f)
protected lazy val syntacticInfo =
if (useSyntacticHints) SyntacticInfo.getSyntacticInfo(sourceFile.content.mkString) else SyntacticInfo.noSyntacticInfo
- lazy val unitTree = global.loadedType(sourceFile)
-
- /** Return the Symbols corresponding to this `Tree`, if any.
- *
- * Scala trees contain symbols when they define or reference a definition. We
- * need to special-case `TypeTree`, because it does not return `true` on `hasSymbol`,
- * but nevertheless it may refer to a symbol through its type. Examples of `TypeTree`s
- * are the type of `ValDef`s after type checking.
- *
- * Another special case is `Import`, because it's selectors are not trees, therefore
- * they do not have a symbol. However, it is desirable to color them, so the symbols are
- * looked up on the fly.
- *
- * A single tree may define more than one symbol, usually with the same name. For instance:
- * - a 'val' defines both a private field and a getter
- * - a 'var' defines a private field, and getter/setter methods
- * - a class parameter defines a constructor parameter, possibly a field and a getter
- * - Import will generate one per selector
- *
- * Lazy values are special-cased because the underlying local var has a different
- * name and there are no trees for the getter yet (added by phase lazyvals). We need
- * to return the accessor, who can later be classified as `lazy`.
- */
- def safeSymbol(t: Tree): List[(Symbol, Position)] = {
- val syms = t match {
- case tpeTree: TypeTree =>
- //val originalSym = safeSymbol(tpeTree.original)
- val originalSym = tpeTree.original match {
- // we need to decompose types that take type parameters.
- case AppliedTypeTree(tpt, args) => (tpt :: args) flatMap(safeSymbol)
- case original @ _ =>
- //XXX: [mirco] Is this really safe? Why `ForAll t. original != t`?
- safeSymbol(original)
- }
-
- // if the original tree did not find anything, we need to call
- // symbol, which may trigger type checking of the underlying tree, so we
- // wrap it in 'ask'
- if (originalSym.isEmpty) {
- val tpeSym = global.askOption(() => Option(t.symbol)).flatten.toList
- tpeSym.zip(List(tpeTree.namePosition))
- } else originalSym
-
- case Import(expr, selectors) =>
- (for (ImportSelector(name, namePos, _, _) <- selectors) yield {
- // create a range position for this selector.
- // TODO: remove special casing once scalac is fixed, and ImportSelectors are proper trees,
- // with real positions, instead of just an Int
- val pos = rangePos(sourceFile, namePos, namePos, namePos + name.length)
-
- val sym1 = global.askOption{() =>
- val typeSym = expr.tpe.member(name.toTypeName)
- if(typeSym.exists) typeSym
- else expr.tpe.member(name.toTermName)
- }.getOrElse(NoSymbol)
- if (sym1 eq NoSymbol) List()
- else if (sym1.isOverloaded) sym1.alternatives.take(1).zip(List(pos))
- else List((sym1, pos))
- }).flatten
-
- case _ =>
- if(isSourceTree(t)) {
- // the local variable backing a lazy value is called 'originalName$lzy'. We swap it here for its
- // accessor, otherwise this symbol would fail the test in `getNameRegion`
- val sym1 = Option(t.symbol).map(sym => if (sym.isLazy && sym.isMutable) sym.lazyAccessor else sym).toList
-
- sym1.zip(t.namePosition :: Nil)
- }
- else Nil
- }
+ private lazy val unitTree = global.loadedType(sourceFile)
+
+ private def canSymbolBeReferencedInSource(sym: Symbol): Boolean = {
+ def isSyntheticMethodParam(sym: Symbol): Boolean = sym.isSynthetic && sym.isValueParameter
- syms
+ !sym.isAnonymousFunction &&
+ !sym.isAnonymousClass &&
+ !isSyntheticMethodParam(sym)
}
-
- /** Trees that have a direct correspondence in the source code have a RangePosition.
- * TransparentPositions come into play for trees that don't have a source-code
- * correspondence but still have children that are visible in the source.*/
- def isSourceTree(t: Tree): Boolean = t.pos.isRange && !t.pos.isTransparent
def classifySymbols: List[SymbolInfo] = {
val allSymbols: List[(Symbol, Position)] = debugTimed("allSymbols") {
@@ -142,7 +72,7 @@ class SymbolClassification(protected val sourceFile: SourceFile, val global: Sca
t <- unitTree
if (t.hasSymbol || t.isType) && isSourceTree(t)
(sym, pos) <- safeSymbol(t)
- if !sym.isAnonymousFunction && !sym.isAnonymousClass
+ if canSymbolBeReferencedInSource(sym)
} yield (sym, pos)
}
Please sign in to comment.
Something went wrong with that request. Please try again.