summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuc Bourlier <luc.bourlier@typesafe.com>2013-11-13 17:24:59 +0100
committerLuc Bourlier <luc.bourlier@typesafe.com>2013-11-21 12:24:48 +0100
commit053a2744c6dfa339e004fbdab8994dc435e60b45 (patch)
tree39f5f9e600cc1686611d9b0ded067183ec05bed2
parenteb9f0f797535386d44f9bc1f9e0c1765622baa29 (diff)
downloadscala-053a2744c6dfa339e004fbdab8994dc435e60b45.tar.gz
scala-053a2744c6dfa339e004fbdab8994dc435e60b45.tar.bz2
scala-053a2744c6dfa339e004fbdab8994dc435e60b45.zip
[nomaster] SI-7280 Scope completion not returning members provided by imports
Updates localeContext() to return the best context possible when there are none directly associated with the given position. It happens when an expression cannot be successfully typed, as no precise ContextTree covers the expression location, or if the position is not inside any expression. Adds corresponding tests (cherry picked from commit 3028327e2a2b553b12ee45519413515c8aa0865f)
-rw-r--r--src/compiler/scala/tools/nsc/interactive/ContextTrees.scala65
-rw-r--r--test/files/presentation/scope-completion-import.check141
-rw-r--r--test/files/presentation/scope-completion-import/Test.scala3
-rw-r--r--test/files/presentation/scope-completion-import/src/Completions.scala64
4 files changed, 248 insertions, 25 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
index b2568e34bd..4a61a98921 100644
--- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
+++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
@@ -6,7 +6,7 @@ package scala.tools.nsc
package interactive
import scala.collection.mutable.ArrayBuffer
-import scala.reflect.internal.util.Position
+import scala.annotation.tailrec
trait ContextTrees { self: Global =>
@@ -29,44 +29,59 @@ trait ContextTrees { self: Global =>
override def toString = "ContextTree("+pos+", "+children+")"
}
- /** Optionally returns the smallest context that contains given `pos`, or None if none exists.
+ /** Returns the most precise context possible for the given `pos`.
+ *
+ * It looks for the finest ContextTree containing `pos`, and then look inside
+ * this ContextTree for a child ContextTree located immediately before `pos`.
+ * If such a child exists, returns its context, otherwise returns the context of
+ * the parent ContextTree.
+ *
+ * This is required to always return a context which contains the all the imports
+ * declared up to `pos` (see SI-7280 for a test case).
+ *
+ * Can return None if `pos` is before any valid Scala code.
*/
def locateContext(contexts: Contexts, pos: Position): Option[Context] = synchronized {
- def locateNearestContextTree(contexts: Contexts, pos: Position, recent: Array[ContextTree]): Option[ContextTree] = {
- locateContextTree(contexts, pos) match {
- case Some(x) =>
- recent(0) = x
- locateNearestContextTree(x.children, pos, recent)
- case None => recent(0) match {
- case null => None
- case x => Some(x)
- }
+ @tailrec
+ def locateFinestContextTree(context: ContextTree): ContextTree = {
+ if (context.pos includes pos) {
+ locateContextTree(context.children, pos) match {
+ case Some(x) =>
+ locateFinestContextTree(x)
+ case None =>
+ context
+ }
+ } else {
+ context
}
}
- locateNearestContextTree(contexts, pos, new Array[ContextTree](1)) map (_.context)
+ locateContextTree(contexts, pos) map locateFinestContextTree map (_.context)
}
+ /** Returns the ContextTree containing `pos`, or the ContextTree positioned just before `pos`,
+ * or None if `pos` is located before all ContextTrees.
+ */
def locateContextTree(contexts: Contexts, pos: Position): Option[ContextTree] = {
if (contexts.isEmpty) None
else {
- val hi = contexts.length - 1
- if ((contexts(hi).pos properlyPrecedes pos) || (pos properlyPrecedes contexts(0).pos)) None
- else {
- def loop(lo: Int, hi: Int): Option[ContextTree] = {
+ @tailrec
+ def loop(lo: Int, hi: Int, previousSibling: Option[ContextTree]): Option[ContextTree] = {
+ if (pos properlyPrecedes contexts(lo).pos)
+ previousSibling
+ else if (contexts(hi).pos properlyPrecedes pos)
+ Some(contexts(hi))
+ else {
val mid = (lo + hi) / 2
val midpos = contexts(mid).pos
- if ((pos precedes midpos) && (mid < hi))
- loop(lo, mid)
- else if ((midpos precedes pos) && (lo < mid))
- loop(mid, hi)
- else if (midpos includes pos)
+ if (midpos includes pos)
Some(contexts(mid))
- else if (contexts(mid+1).pos includes pos)
- Some(contexts(mid+1))
- else None
+ else if (midpos properlyPrecedes pos)
+ loop(mid + 1, hi, Some(contexts(mid)))
+ else
+ loop(lo, mid, previousSibling)
}
- loop(0, hi)
}
+ loop(0, contexts.length - 1, None)
}
}
diff --git a/test/files/presentation/scope-completion-import.check b/test/files/presentation/scope-completion-import.check
new file mode 100644
index 0000000000..1d84abd617
--- /dev/null
+++ b/test/files/presentation/scope-completion-import.check
@@ -0,0 +1,141 @@
+reload: Completions.scala
+
+askScopeCompletion at Completions.scala(15,4)
+================================================================================
+[response] askScopeCompletion at (15,4)
+retrieved 10 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo()test.Foo`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `method fOOO=> Int`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value otest.O.type`
+================================================================================
+
+askScopeCompletion at Completions.scala(19,4)
+================================================================================
+[response] askScopeCompletion at (19,4)
+retrieved 9 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo()test.Foo`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `method fOOO=> Int`
+[accessible: true] `object Otest.O.type`
+================================================================================
+
+askScopeCompletion at Completions.scala(24,4)
+================================================================================
+[response] askScopeCompletion at (24,4)
+retrieved 9 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo()test.Foo`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value ctest.C`
+================================================================================
+
+askScopeCompletion at Completions.scala(27,5)
+================================================================================
+[response] askScopeCompletion at (27,5)
+retrieved 8 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo()test.Foo`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value ctest.C`
+================================================================================
+
+askScopeCompletion at Completions.scala(30,5)
+================================================================================
+[response] askScopeCompletion at (30,5)
+retrieved 9 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo()test.Foo`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value ctest.C`
+================================================================================
+
+askScopeCompletion at Completions.scala(32,5)
+================================================================================
+[response] askScopeCompletion at (32,5)
+retrieved 10 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo()test.Foo`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `method fOOO=> Int`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value ctest.C`
+================================================================================
+
+askScopeCompletion at Completions.scala(41,4)
+================================================================================
+[response] askScopeCompletion at (41,4)
+retrieved 10 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo_1()test.Foo_1`
+[accessible: true] `method bar=> Unit`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `method fOOO=> Int`
+[accessible: true] `object Otest.O.type`
+================================================================================
+
+askScopeCompletion at Completions.scala(51,4)
+================================================================================
+[response] askScopeCompletion at (51,4)
+retrieved 11 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo_2()test.Foo_2`
+[accessible: true] `method bar=> Unit`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `method fOOO=> Int`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value otest.O.type`
+================================================================================
+
+askScopeCompletion at Completions.scala(61,4)
+================================================================================
+[response] askScopeCompletion at (61,4)
+retrieved 10 members
+[accessible: true] `class Ctest.C`
+[accessible: true] `class Foo_1test.Foo_1`
+[accessible: true] `class Foo_2test.Foo_2`
+[accessible: true] `class Foo_3test.Foo_3`
+[accessible: true] `class Footest.Foo`
+[accessible: true] `constructor Foo_3()test.Foo_3`
+[accessible: true] `method bar=> Unit`
+[accessible: true] `method fCCC=> Int`
+[accessible: true] `object Otest.O.type`
+[accessible: true] `value ctest.C`
+================================================================================
diff --git a/test/files/presentation/scope-completion-import/Test.scala b/test/files/presentation/scope-completion-import/Test.scala
new file mode 100644
index 0000000000..bec1131c4c
--- /dev/null
+++ b/test/files/presentation/scope-completion-import/Test.scala
@@ -0,0 +1,3 @@
+import scala.tools.nsc.interactive.tests.InteractiveTest
+
+object Test extends InteractiveTest \ No newline at end of file
diff --git a/test/files/presentation/scope-completion-import/src/Completions.scala b/test/files/presentation/scope-completion-import/src/Completions.scala
new file mode 100644
index 0000000000..6e08321283
--- /dev/null
+++ b/test/files/presentation/scope-completion-import/src/Completions.scala
@@ -0,0 +1,64 @@
+package test
+
+class C {
+ def fCCC : Int = 0
+}
+
+object O extends C {
+ def fOOO : Int = 0
+}
+
+class Foo {
+ {
+ val o = O
+ import o._
+ /*_*/
+ }
+ {
+ import O._
+ /*_*/
+ }
+ {
+ val c = new C
+ import c._
+ /*_*/
+ }
+ {
+ f/*_*/
+ val c = new C
+ import c._
+ f/*_*/
+ import O._
+ f/*_*/
+ }
+}
+
+class Foo_1 {
+
+ import O._
+
+ def bar {
+ /*_*/
+ }
+}
+
+class Foo_2 {
+
+ val o = O
+ import o._
+
+ def bar {
+ /*_*/
+ }
+}
+
+class Foo_3 {
+
+ val c = new C
+ import c._
+
+ def bar {
+ /*_*/
+ }
+}
+