summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2015-09-24 14:27:58 +1000
committerJason Zaugg <jzaugg@gmail.com>2015-09-24 16:56:18 +1000
commitfb299f80f3f85e935695d5b7cb7f2c712b150e3c (patch)
tree4d95bc66e8354e0eb9135aaf1fa102761fb59dff
parent2c9e506bc32248f9ae4929790a0cb7484a53a66e (diff)
downloadscala-fb299f80f3f85e935695d5b7cb7f2c712b150e3c.tar.gz
scala-fb299f80f3f85e935695d5b7cb7f2c712b150e3c.tar.bz2
scala-fb299f80f3f85e935695d5b7cb7f2c712b150e3c.zip
Improve presentation compilation of annotations
A trio of problems were hampering autocompletion of annotations. First, given that that annotation is written before the annotated member, it is very common to end parse incomplete code that has a floating annotation without an anotatee. The parser was discarding the annotations (ie, the modifiers) and emitting an `EmptyTree`. Second, the presetation compiler was only looking for annotations in the Modifiers of a member def, but after typechecking annotations are moved into the symbol. Third, if an annotation failed to typecheck, it was being discarded in place of `ErroneousAnnotation`. This commit: - modifies the parser to uses a dummy class- or type-def tree, instead of EmptyTree, which can carry the annotations. - updates the locator to look in the symbol annotations of the modifiers contains no annotations. - uses a separate instance of `ErroneousAnnotation` for each erroneous annotation, and stores the original tree in its `original` tree.
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala10
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala1
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Positions.scala9
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala1
-rw-r--r--test/junit/scala/tools/nsc/interpreter/CompletionTest.scala11
6 files changed, 29 insertions, 5 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 70abdf6bc0..4494a8ac8d 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -2680,7 +2680,10 @@ self =>
case t if t == SUPERTYPE || t == SUBTYPE || t == COMMA || t == RBRACE || isStatSep(t) =>
TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
case _ =>
- syntaxErrorOrIncompleteAnd("`=', `>:', or `<:' expected", skipIt = true)(EmptyTree)
+ syntaxErrorOrIncompleteAnd("`=', `>:', or `<:' expected", skipIt = true)(
+ // assume a dummy type def so as to have somewhere to stash the annotations
+ TypeDef(mods, tpnme.ERROR, Nil, EmptyTree)
+ )
}
}
}
@@ -2713,7 +2716,10 @@ self =>
case CASEOBJECT =>
objectDef(pos, (mods | Flags.CASE) withPosition (Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'object', thus take prev*/)))
case _ =>
- syntaxErrorOrIncompleteAnd("expected start of definition", skipIt = true)(EmptyTree)
+ syntaxErrorOrIncompleteAnd("expected start of definition", skipIt = true)(
+ // assume a class definition so as to have somewhere to stash the annotations
+ atPos(pos)(gen.mkClassDef(mods, tpnme.ERROR, Nil, Template(Nil, noSelfType, Nil)))
+ )
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 8113cd9b96..6b73a538df 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3558,6 +3558,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def typedAnnotation(ann: Tree, mode: Mode = EXPRmode): AnnotationInfo = {
var hasError: Boolean = false
val pending = ListBuffer[AbsTypeError]()
+ def ErroneousAnnotation = new ErroneousAnnotation().setOriginal(ann)
def finish(res: AnnotationInfo): AnnotationInfo = {
if (hasError) {
diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
index 6863cdfd82..b923541b56 100644
--- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala
+++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
@@ -404,7 +404,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil)
- object ErroneousAnnotation extends CompleteAnnotationInfo(ErrorType, Nil, Nil)
+ class ErroneousAnnotation() extends CompleteAnnotationInfo(ErrorType, Nil, Nil)
/** Extracts symbol of thrown exception from AnnotationInfo.
*
diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala
index 4d0e31b037..15d68bcdfe 100644
--- a/src/reflect/scala/reflect/internal/Positions.scala
+++ b/src/reflect/scala/reflect/internal/Positions.scala
@@ -252,7 +252,14 @@ trait Positions extends api.Positions { self: SymbolTable =>
super.traverse(t)
} else t match {
case mdef: MemberDef =>
- traverseTrees(mdef.mods.annotations)
+ val annTrees = mdef.mods.annotations match {
+ case Nil if mdef.symbol != null =>
+ // After typechecking, annotations are mvoed from the modifiers
+ // to the annotation on the symbol of the anotatee.
+ mdef.symbol.annotations.map(_.original)
+ case anns => anns
+ }
+ traverseTrees(annTrees)
case _ =>
}
}
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 5477bdd6d4..7725e4a2f0 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -106,7 +106,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.AnnotationInfo
this.Annotation
this.UnmappableAnnotation
- this.ErroneousAnnotation
this.ThrownException
this.typeNames
this.tpnme
diff --git a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
index d00a9bf69a..78ebb7cf9c 100644
--- a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
+++ b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
@@ -52,6 +52,17 @@ class CompletionTest {
}
@Test
+ def annotations(): Unit = {
+ val intp = newIMain()
+ val completer = new PresentationCompilerCompleter(intp)
+ checkExact(completer, "def foo[@specialize", " A]")("specialized")
+ checkExact(completer, "def foo[@specialize")("specialized")
+ checkExact(completer, """@deprecatedN""", """ class Foo""")("deprecatedName")
+ checkExact(completer, """@deprecateN""")("deprecatedName")
+ checkExact(completer, """{@deprecateN""")("deprecatedName")
+ }
+
+ @Test
def incompleteStringInterpolation(): Unit = {
val intp = newIMain()
val completer = new PresentationCompilerCompleter(intp)