Skip to content

Commit

Permalink
Merge pull request scala#5640 from optimizely/repl-import-handler
Browse files Browse the repository at this point in the history
SI-9881 Fix ImportHandler's reporting of importedNames and importedSymbols
  • Loading branch information
adriaanm committed Feb 20, 2017
2 parents 023a96a + aa7e335 commit 13f7b2a
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 19 deletions.
7 changes: 5 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Expand Up @@ -64,16 +64,19 @@ trait Contexts { self: Analyzer =>
for (imps <- allImportInfos.remove(unit)) {
for (imp <- imps.reverse.distinct) {
val used = allUsedSelectors(imp)
def isMask(s: ImportSelector) = s.name != nme.WILDCARD && s.rename == nme.WILDCARD

imp.tree.selectors filterNot (s => isMask(s) || used(s)) foreach { sel =>
imp.tree.selectors filterNot (s => isMaskImport(s) || used(s)) foreach { sel =>
reporter.warning(imp posOf sel, "Unused import")
}
}
allUsedSelectors --= imps
}
}

def isMaskImport(s: ImportSelector): Boolean = s.name != nme.WILDCARD && s.rename == nme.WILDCARD
def isIndividualImport(s: ImportSelector): Boolean = s.name != nme.WILDCARD && s.rename != nme.WILDCARD
def isWildcardImport(s: ImportSelector): Boolean = s.name == nme.WILDCARD

var lastAccessCheckDetails: String = ""

/** List of symbols to import from in a root context. Typically that
Expand Down
6 changes: 4 additions & 2 deletions src/reflect/scala/reflect/internal/Names.scala
Expand Up @@ -296,11 +296,13 @@ trait Names extends api.Names {
*/
final def pos(s: String, start: Int): Int = {
var i = pos(s.charAt(0), start)
while (i + s.length() <= len) {
val sLen = s.length()
if (sLen == 1) return i
while (i + sLen <= len) {
var j = 1
while (s.charAt(j) == chrs(index + i + j)) {
j += 1
if (j == s.length()) return i
if (j == sLen) return i
}
i = pos(s.charAt(0), i + 1)
}
Expand Down
14 changes: 10 additions & 4 deletions src/repl/scala/tools/nsc/interpreter/ExprTyper.scala
Expand Up @@ -13,6 +13,12 @@ trait ExprTyper {
import global.{ reporter => _, Import => _, _ }
import naming.freshInternalVarName

private def doInterpret(code: String): IR.Result = {
// interpret/interpretSynthetic may change the phase, which would have unintended effects on types.
val savedPhase = phase
try interpretSynthetic(code) finally phase = savedPhase
}

def symbolOfLine(code: String): Symbol = {
def asExpr(): Symbol = {
val name = freshInternalVarName()
Expand All @@ -21,7 +27,7 @@ trait ExprTyper {
// behind a def and strip the NullaryMethodType which wraps the expr.
val line = "def " + name + " = " + code

interpretSynthetic(line) match {
doInterpret(line) match {
case IR.Success =>
val sym0 = symbolOfTerm(name)
// drop NullaryMethodType
Expand All @@ -32,7 +38,7 @@ trait ExprTyper {
def asDefn(): Symbol = {
val old = repl.definedSymbolList.toSet

interpretSynthetic(code) match {
doInterpret(code) match {
case IR.Success =>
repl.definedSymbolList filterNot old match {
case Nil => NoSymbol
Expand All @@ -43,7 +49,7 @@ trait ExprTyper {
}
}
def asError(): Symbol = {
interpretSynthetic(code)
doInterpret(code)
NoSymbol
}
beSilentDuring(asExpr()) orElse beSilentDuring(asDefn()) orElse asError()
Expand Down Expand Up @@ -72,7 +78,7 @@ trait ExprTyper {
def asProperType(): Option[Type] = {
val name = freshInternalVarName()
val line = "def %s: %s = ???" format (name, typeString)
interpretSynthetic(line) match {
doInterpret(line) match {
case IR.Success =>
val sym0 = symbolOfTerm(name)
Some(sym0.asMethod.returnType)
Expand Down
33 changes: 22 additions & 11 deletions src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala
Expand Up @@ -213,29 +213,40 @@ trait MemberHandlers {

class ImportHandler(imp: Import) extends MemberHandler(imp) {
val Import(expr, selectors) = imp

def targetType = intp.global.rootMirror.getModuleIfDefined("" + expr) match {
case NoSymbol => intp.typeOfExpression("" + expr)
case sym => sym.thisType
case sym => sym.tpe
}
private def importableTargetMembers = importableMembers(targetType).toList
// wildcard imports, e.g. import foo._
private def selectorWild = selectors filter (_.name == nme.USCOREkw)
// renamed imports, e.g. import foo.{ bar => baz }
private def selectorRenames = selectors map (_.rename) filterNot (_ == null)

private def isFlattenedSymbol(sym: Symbol) =
sym.owner.isPackageClass &&
sym.name.containsName(nme.NAME_JOIN_STRING) &&
sym.owner.info.member(sym.name.take(sym.name.indexOf(nme.NAME_JOIN_STRING))) != NoSymbol

private def importableTargetMembers =
importableMembers(exitingTyper(targetType)).filterNot(isFlattenedSymbol).toList

// non-wildcard imports
private def individualSelectors = selectors filter analyzer.isIndividualImport

/** Whether this import includes a wildcard import */
val importsWildcard = selectorWild.nonEmpty
val importsWildcard = selectors exists analyzer.isWildcardImport

def implicitSymbols = importedSymbols filter (_.isImplicit)
def importedSymbols = individualSymbols ++ wildcardSymbols

private val selectorNames = selectorRenames filterNot (_ == nme.USCOREkw) flatMap (_.bothNames) toSet
lazy val individualSymbols: List[Symbol] = exitingTyper(importableTargetMembers filter (m => selectorNames(m.name)))
lazy val wildcardSymbols: List[Symbol] = exitingTyper(if (importsWildcard) importableTargetMembers else Nil)
lazy val importableSymbolsWithRenames = {
val selectorRenameMap = individualSelectors.flatMap(x => x.name.bothNames zip x.rename.bothNames).toMap
importableTargetMembers flatMap (m => selectorRenameMap.get(m.name) map (m -> _))
}

lazy val individualSymbols: List[Symbol] = importableSymbolsWithRenames map (_._1)
lazy val wildcardSymbols: List[Symbol] = if (importsWildcard) importableTargetMembers else Nil

/** Complete list of names imported by a wildcard */
lazy val wildcardNames: List[Name] = wildcardSymbols map (_.name)
lazy val individualNames: List[Name] = individualSymbols map (_.name)
lazy val individualNames: List[Name] = importableSymbolsWithRenames map (_._2)

/** The names imported by this statement */
override lazy val importedNames: List[Name] = wildcardNames ++ individualNames
Expand Down
36 changes: 36 additions & 0 deletions test/files/run/t9880-9881.check
@@ -0,0 +1,36 @@

scala> // import in various ways

scala> import java.util.Date
import java.util.Date

scala> import scala.util._
import scala.util._

scala> import scala.reflect.runtime.{universe => ru}
import scala.reflect.runtime.{universe=>ru}

scala> import ru.TypeTag
import ru.TypeTag

scala>

scala> // show the imports

scala> :imports
1) import java.lang._ (...)
2) import scala._ (...)
3) import scala.Predef._ (...)
4) import java.util.Date (...)
5) import scala.util._ (...)
6) import scala.reflect.runtime.{universe=>ru} (...)
7) import ru.TypeTag (...)

scala>

scala> // should be able to define this class with the imports above

scala> class C[T](date: Date, rand: Random, typeTag: TypeTag[T])
defined class C

scala> :quit
29 changes: 29 additions & 0 deletions test/files/run/t9880-9881.scala
@@ -0,0 +1,29 @@
import scala.tools.partest.ReplTest
import scala.tools.nsc.Settings

object Test extends ReplTest {

override def transformSettings(s: Settings): Settings = {
s.Yreplclassbased.value = true
s
}

lazy val normalizeRegex = """(import\s.*)\(.*\)""".r

override def normalize(s: String): String = normalizeRegex.replaceFirstIn(s, "$1(...)")

def code =
"""
|// import in various ways
|import java.util.Date
|import scala.util._
|import scala.reflect.runtime.{universe => ru}
|import ru.TypeTag
|
|// show the imports
|:imports
|
|// should be able to define this class with the imports above
|class C[T](date: Date, rand: Random, typeTag: TypeTag[T])
""".stripMargin
}
25 changes: 25 additions & 0 deletions test/junit/scala/reflect/internal/NamesTest.scala
Expand Up @@ -92,4 +92,29 @@ class NamesTest {
assert(h1 string_== h2)
assert(h1 string_== h1y)
}

@Test
def pos(): Unit = {
def check(nameString: String, sub: String) = {
val name = TermName(nameString)
val javaResult = name.toString.indexOf(sub) match { case -1 => name.length case x => x }
val nameResult = name.pos(sub)
assertEquals(javaResult, nameResult)
if (sub.length == 1) {
val nameResultChar = name.pos(sub.head)
assertEquals(javaResult, nameResultChar)
}
}

check("a", "a") // was "String index out of range: 1
check("a", "b")
check("a", "ab")
check("a", "ba")
check("ab", "a")
check("ab", "b")
check("ab", "ab")
check("ab", "ba")
check("", "x")
check("", "xy")
}
}

0 comments on commit 13f7b2a

Please sign in to comment.