Skip to content

Commit e6bac4c

Browse files
committed
Merge pull request scala#3140 from skyluc/issue/completion-import-object-7280
SI-7280 Scope completion not returning members provided by imports
2 parents 202cf2c + 3028327 commit e6bac4c

30 files changed

+1037
-57
lines changed

src/interactive/scala/tools/nsc/interactive/ContextTrees.scala

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package scala.tools.nsc
66
package interactive
77

88
import scala.collection.mutable.ArrayBuffer
9+
import scala.annotation.tailrec
910

1011
trait ContextTrees { self: Global =>
1112

@@ -28,44 +29,59 @@ trait ContextTrees { self: Global =>
2829
override def toString = "ContextTree("+pos+", "+children+")"
2930
}
3031

31-
/** Optionally returns the smallest context that contains given `pos`, or None if none exists.
32+
/** Returns the most precise context possible for the given `pos`.
33+
*
34+
* It looks for the finest ContextTree containing `pos`, and then look inside
35+
* this ContextTree for a child ContextTree located immediately before `pos`.
36+
* If such a child exists, returns its context, otherwise returns the context of
37+
* the parent ContextTree.
38+
*
39+
* This is required to always return a context which contains the all the imports
40+
* declared up to `pos` (see SI-7280 for a test case).
41+
*
42+
* Can return None if `pos` is before any valid Scala code.
3243
*/
3344
def locateContext(contexts: Contexts, pos: Position): Option[Context] = synchronized {
34-
def locateNearestContextTree(contexts: Contexts, pos: Position, recent: Array[ContextTree]): Option[ContextTree] = {
35-
locateContextTree(contexts, pos) match {
36-
case Some(x) =>
37-
recent(0) = x
38-
locateNearestContextTree(x.children, pos, recent)
39-
case None => recent(0) match {
40-
case null => None
41-
case x => Some(x)
42-
}
45+
@tailrec
46+
def locateFinestContextTree(context: ContextTree): ContextTree = {
47+
if (context.pos includes pos) {
48+
locateContextTree(context.children, pos) match {
49+
case Some(x) =>
50+
locateFinestContextTree(x)
51+
case None =>
52+
context
53+
}
54+
} else {
55+
context
4356
}
4457
}
45-
locateNearestContextTree(contexts, pos, new Array[ContextTree](1)) map (_.context)
58+
locateContextTree(contexts, pos) map locateFinestContextTree map (_.context)
4659
}
4760

61+
/** Returns the ContextTree containing `pos`, or the ContextTree positioned just before `pos`,
62+
* or None if `pos` is located before all ContextTrees.
63+
*/
4864
def locateContextTree(contexts: Contexts, pos: Position): Option[ContextTree] = {
4965
if (contexts.isEmpty) None
5066
else {
51-
val hi = contexts.length - 1
52-
if ((contexts(hi).pos properlyPrecedes pos) || (pos properlyPrecedes contexts(0).pos)) None
53-
else {
54-
def loop(lo: Int, hi: Int): Option[ContextTree] = {
67+
@tailrec
68+
def loop(lo: Int, hi: Int, previousSibling: Option[ContextTree]): Option[ContextTree] = {
69+
if (pos properlyPrecedes contexts(lo).pos)
70+
previousSibling
71+
else if (contexts(hi).pos properlyPrecedes pos)
72+
Some(contexts(hi))
73+
else {
5574
val mid = (lo + hi) / 2
5675
val midpos = contexts(mid).pos
57-
if ((pos precedes midpos) && (mid < hi))
58-
loop(lo, mid)
59-
else if ((midpos precedes pos) && (lo < mid))
60-
loop(mid, hi)
61-
else if (midpos includes pos)
76+
if (midpos includes pos)
6277
Some(contexts(mid))
63-
else if (contexts(mid+1).pos includes pos)
64-
Some(contexts(mid+1))
65-
else None
78+
else if (midpos properlyPrecedes pos)
79+
loop(mid + 1, hi, Some(contexts(mid)))
80+
else
81+
loop(lo, mid, previousSibling)
6682
}
67-
loop(0, hi)
6883
}
84+
loop(0, contexts.length - 1, None)
6985
}
7086
}
7187

src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ abstract class InteractiveTest
6161
* Override this member if you need to change the default set of executed test actions.
6262
*/
6363
protected lazy val testActions: ListBuffer[PresentationCompilerTestDef] = {
64-
ListBuffer(new CompletionAction(compiler), new TypeAction(compiler), new HyperlinkAction(compiler))
64+
ListBuffer(new TypeCompletionAction(compiler), new ScopeCompletionAction(compiler), new TypeAction(compiler), new HyperlinkAction(compiler))
6565
}
6666

6767
/** Add new presentation compiler actions to test. Presentation compiler's test

src/interactive/scala/tools/nsc/interactive/tests/core/AskCommand.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ trait AskParse extends AskCommand {
4242
import compiler.Tree
4343

4444
/** `sources` need to be entirely parsed before running the test
45-
* (else commands such as `AskCompletionAt` may fail simply because
45+
* (else commands such as `AskTypeCompletionAt` may fail simply because
4646
* the source's AST is not yet loaded).
4747
*/
4848
def askParse(sources: Seq[SourceFile]) {
@@ -72,10 +72,10 @@ trait AskReload extends AskCommand {
7272
}
7373

7474
/** Ask the presentation compiler for completion at a given position. */
75-
trait AskCompletionAt extends AskCommand {
75+
trait AskTypeCompletionAt extends AskCommand {
7676
import compiler.Member
7777

78-
private[tests] def askCompletionAt(pos: Position)(implicit reporter: Reporter): Response[List[Member]] = {
78+
private[tests] def askTypeCompletionAt(pos: Position)(implicit reporter: Reporter): Response[List[Member]] = {
7979
reporter.println("\naskTypeCompletion at " + pos.source.file.name + ((pos.line, pos.column)))
8080

8181
ask {
@@ -84,6 +84,19 @@ trait AskCompletionAt extends AskCommand {
8484
}
8585
}
8686

87+
/** Ask the presentation compiler for scope completion at a given position. */
88+
trait AskScopeCompletionAt extends AskCommand {
89+
import compiler.Member
90+
91+
private[tests] def askScopeCompletionAt(pos: Position)(implicit reporter: Reporter): Response[List[Member]] = {
92+
reporter.println("\naskScopeCompletion at " + pos.source.file.name + ((pos.line, pos.column)))
93+
94+
ask {
95+
compiler.askScopeCompletion(pos, _)
96+
}
97+
}
98+
}
99+
87100
/** Ask the presentation compiler for type info at a given position. */
88101
trait AskTypeAt extends AskCommand {
89102
import compiler.Tree

src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ private[tests] trait CoreTestDefs
1212

1313
/** Ask the presentation compiler for completion at all locations
1414
* (in all sources) where the defined `marker` is found. */
15-
class CompletionAction(override val compiler: Global)
15+
class TypeCompletionAction(override val compiler: Global)
1616
extends PresentationCompilerTestDef
17-
with AskCompletionAt {
17+
with AskTypeCompletionAt {
1818

1919
override def runTest() {
20-
askAllSources(CompletionMarker) { pos =>
21-
askCompletionAt(pos)
20+
askAllSources(TypeCompletionMarker) { pos =>
21+
askTypeCompletionAt(pos)
2222
} { (pos, members) =>
2323
withResponseDelimiter {
24-
reporter.println("[response] askCompletionAt " + format(pos))
24+
reporter.println("[response] askTypeCompletion at " + format(pos))
2525
// we skip getClass because it changed signature between 1.5 and 1.6, so there is no
2626
// universal check file that we can provide for this to work
2727
reporter.println("retrieved %d members".format(members.size))
@@ -34,6 +34,40 @@ private[tests] trait CoreTestDefs
3434
}
3535
}
3636

37+
/** Ask the presentation compiler for completion at all locations
38+
* (in all sources) where the defined `marker` is found. */
39+
class ScopeCompletionAction(override val compiler: Global)
40+
extends PresentationCompilerTestDef
41+
with AskScopeCompletionAt {
42+
43+
def memberPrinter(member: compiler.Member): String =
44+
"[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString() + member.tpe.toString()).trim() + "`"
45+
46+
override def runTest() {
47+
askAllSources(ScopeCompletionMarker) { pos =>
48+
askScopeCompletionAt(pos)
49+
} { (pos, members) =>
50+
withResponseDelimiter {
51+
reporter.println("[response] askScopeCompletion at " + format(pos))
52+
try {
53+
// exclude members not from source (don't have position), for more focused and self contained tests.
54+
def eligible(sym: compiler.Symbol) = sym.pos != compiler.NoPosition
55+
val filtered = members.filter(member => eligible(member.sym))
56+
57+
reporter.println("retrieved %d members".format(filtered.size))
58+
compiler ask { () =>
59+
reporter.println(filtered.map(_.forceInfoString).sorted mkString "\n")
60+
}
61+
} catch {
62+
case t: Throwable =>
63+
t.printStackTrace()
64+
}
65+
66+
}
67+
}
68+
}
69+
}
70+
3771
/** Ask the presentation compiler for type info at all locations
3872
* (in all sources) where the defined `marker` is found. */
3973
class TypeAction(override val compiler: Global)
@@ -57,7 +91,7 @@ private[tests] trait CoreTestDefs
5791
class HyperlinkAction(override val compiler: Global)
5892
extends PresentationCompilerTestDef
5993
with AskTypeAt
60-
with AskCompletionAt {
94+
with AskTypeCompletionAt {
6195

6296
override def runTest() {
6397
askAllSources(HyperlinkMarker) { pos =>

src/interactive/scala/tools/nsc/interactive/tests/core/TestMarker.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ abstract case class TestMarker(marker: String) {
2020
TestMarker.checkForDuplicate(this)
2121
}
2222

23-
object CompletionMarker extends TestMarker("/*!*/")
23+
object TypeCompletionMarker extends TestMarker("/*!*/")
24+
25+
object ScopeCompletionMarker extends TestMarker("/*_*/")
2426

2527
object TypeMarker extends TestMarker("/*?*/")
2628

test/files/presentation/callcc-interpreter.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ reload: CallccInterpreter.scala
22

33
askTypeCompletion at CallccInterpreter.scala(51,38)
44
================================================================================
5-
[response] askCompletionAt (51,38)
5+
[response] askTypeCompletion at (51,38)
66
retrieved 59 members
77
abstract trait Term extends AnyRef
88
abstract trait Value extends AnyRef

test/files/presentation/completion-implicit-chained.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ reload: Completions.scala
22

33
askTypeCompletion at Completions.scala(11,16)
44
================================================================================
5-
[response] askCompletionAt (11,16)
5+
[response] askTypeCompletion at (11,16)
66
retrieved 24 members
77
[inaccessible] protected[package lang] def clone(): Object
88
[inaccessible] protected[package lang] def finalize(): Unit

test/files/presentation/ide-bug-1000349.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ reload: CompletionOnEmptyArgMethod.scala
22

33
askTypeCompletion at CompletionOnEmptyArgMethod.scala(2,17)
44
================================================================================
5-
[response] askCompletionAt (2,17)
5+
[response] askTypeCompletion at (2,17)
66
retrieved 32 members
77
def +(other: String): String
88
def ->[B](y: B): (Foo, B)

test/files/presentation/ide-bug-1000475.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ reload: Foo.scala
22

33
askTypeCompletion at Foo.scala(3,7)
44
================================================================================
5-
[response] askCompletionAt (3,7)
5+
[response] askTypeCompletion at (3,7)
66
retrieved 31 members
77
[inaccessible] protected[package lang] def clone(): Object
88
[inaccessible] protected[package lang] def finalize(): Unit
@@ -36,7 +36,7 @@ final def wait(x$1: Long,x$2: Int): Unit
3636

3737
askTypeCompletion at Foo.scala(6,10)
3838
================================================================================
39-
[response] askCompletionAt (6,10)
39+
[response] askTypeCompletion at (6,10)
4040
retrieved 31 members
4141
[inaccessible] protected[package lang] def clone(): Object
4242
[inaccessible] protected[package lang] def finalize(): Unit
@@ -70,7 +70,7 @@ final def wait(x$1: Long,x$2: Int): Unit
7070

7171
askTypeCompletion at Foo.scala(7,7)
7272
================================================================================
73-
[response] askCompletionAt (7,7)
73+
[response] askTypeCompletion at (7,7)
7474
retrieved 31 members
7575
[inaccessible] protected[package lang] def clone(): Object
7676
[inaccessible] protected[package lang] def finalize(): Unit

test/files/presentation/ide-bug-1000531.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ reload: CrashOnLoad.scala
22

33
askTypeCompletion at CrashOnLoad.scala(6,12)
44
================================================================================
5-
[response] askCompletionAt (6,12)
5+
[response] askTypeCompletion at (6,12)
66
retrieved 120 members
77
[inaccessible] protected[package lang] def clone(): Object
88
[inaccessible] protected[package lang] def finalize(): Unit

0 commit comments

Comments
 (0)