From 2c9e506bc32248f9ae4929790a0cb7484a53a66e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 24 Sep 2015 13:22:29 +1000 Subject: Support completion in erroneous string interpolation. In the code: ``` s"${fooo Date: Thu, 24 Sep 2015 14:27:58 +1000 Subject: 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. --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 10 ++++++++-- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 1 + src/reflect/scala/reflect/internal/AnnotationInfos.scala | 2 +- src/reflect/scala/reflect/internal/Positions.scala | 9 ++++++++- src/reflect/scala/reflect/runtime/JavaUniverseForce.scala | 1 - test/junit/scala/tools/nsc/interpreter/CompletionTest.scala | 11 +++++++++++ 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 @@ -51,6 +51,17 @@ class CompletionTest { assertEquals(List("prefix_aaa", "prefix_nnn", "prefix_zzz"), completer.complete( """class C { def prefix_nnn = 0; def prefix_zzz = 0; def prefix_aaa = 0; prefix_""").candidates) } + @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() -- cgit v1.2.3