Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Issue/semantic highlight type params #179

Merged
merged 11 commits into from over 1 year ago

3 participants

Simon Schäfer scala-jenkins Mirco Dotta
Simon Schäfer

This PR adds a lot of semantic highlighting support for type params.

I'm not completely satisfied with the result because the presentation compiler doesn't store enough information in the AST to resolve all problems. Sometimes I had to analyze the AST by myself and add a some additional test cases to be sure that I did not broke anything. However, at the end, with each commit I could improve semantic highlighting.

  • compound types are not completely highlighted (only the type params of parameterized types)
  • nothing in structural types is highlighted but the method names are
  • view bounds were quite difficult but I hope the current solution (+ test cases) is enough.

Please review about

  • commit messages. Are they enough?
  • ignored test cases. Is it good to have them separated from the ones working?
  • my manually AST traverses. Maybe I have overseen a simple solution.
scala-jenkins
Collaborator
scala-jenkins
Collaborator

Started jenkins job pr-validator-master-trunk at https://jenkins.scala-ide.org:8496/jenkins/job/pr-validator-master-trunk/98/

scala-jenkins
Collaborator
scala-jenkins
Collaborator
...de.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeParameterTest.scala
... ...
@@ -70,4 +70,181 @@ class TypeParameterTest extends AbstractSymbolClassifierTest {
70 70
       """,
71 71
       Map("TPARAM" -> TypeParameter, "T" -> Type))
72 72
   }
  73
+
  74
+  @Test
  75
+  def partial_compound_type_param() {
  76
+    checkSymbolClassification("""
  77
+      trait X {
  78
+        def xs[TypeParam]: Seq[TypeParam] with collection.IterableLike[TypeParam, TypeParam]
  79
+      }
  80
+      """, """
  81
+      trait X {
  82
+        def xs[$TPARAM $]: Seq[$TPARAM $] with collection.IterableLike[$TPARAM $, $TPARAM $]
  83
+      }
  84
+      """,
  85
+      Map("TPARAM" -> TypeParameter, "T" -> Type))
2
Mirco Dotta Owner
dotta added a note August 12, 2012

I think you don't need "T" -> Type (wild copy/paste? :-))

Simon Schäfer
sschaef added a note August 12, 2012

I will remove it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...de.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeParameterTest.scala
((13 lines not shown))
  82
+        def xs[$TPARAM $]: Seq[$TPARAM $] with collection.IterableLike[$TPARAM $, $TPARAM $]
  83
+      }
  84
+      """,
  85
+      Map("TPARAM" -> TypeParameter, "T" -> Type))
  86
+  }
  87
+
  88
+  @Test
  89
+  @Ignore("does not work until presentation compiler stores more information in the AST")
  90
+  def full_compound_type_param() {
  91
+    checkSymbolClassification("""
  92
+      trait X {
  93
+        def xs[TypeParam]: Seq[TypeParam] with collection.IterableLike[TypeParam, TypeParam]
  94
+      }
  95
+      """, """
  96
+      trait X {
  97
+        def xs[$TPARAM $]: $T$[$TPARAM $] with collection.$Trait     $[$TPARAM $, $TPARAM $]
1
Mirco Dotta Owner
dotta added a note August 12, 2012

This collection.$Trait looks suspicious. But I may be missing something

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...de.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeParameterTest.scala
((16 lines not shown))
  85
+      Map("TPARAM" -> TypeParameter, "T" -> Type))
  86
+  }
  87
+
  88
+  @Test
  89
+  @Ignore("does not work until presentation compiler stores more information in the AST")
  90
+  def full_compound_type_param() {
  91
+    checkSymbolClassification("""
  92
+      trait X {
  93
+        def xs[TypeParam]: Seq[TypeParam] with collection.IterableLike[TypeParam, TypeParam]
  94
+      }
  95
+      """, """
  96
+      trait X {
  97
+        def xs[$TPARAM $]: $T$[$TPARAM $] with collection.$Trait     $[$TPARAM $, $TPARAM $]
  98
+      }
  99
+      """,
  100
+      Map("TPARAM" -> TypeParameter, "T" -> Type, "TRAIT" -> Trait))
3
Mirco Dotta Owner
dotta added a note August 12, 2012

Do you need "T" -> Type, "TRAIT" -> Trait?

Simon Schäfer
sschaef added a note August 12, 2012

Yes, typo error.

Mirco Dotta Owner
dotta added a note August 12, 2012

Ok, this also explains my above comment ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Mirco Dotta dotta commented on the diff August 12, 2012
...de.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeParameterTest.scala
((64 lines not shown))
  133
+  }
  134
+
  135
+  @Test
  136
+  def tuple_literal_type_param() {
  137
+    checkSymbolClassification("""
  138
+      trait X {
  139
+        def f: (Int, String)
  140
+        def g: Tuple2[Int, String]
  141
+      }
  142
+      """, """
  143
+      trait X {
  144
+        def f: ($C$, $TYPE$)
  145
+        def g: $CC  $[$C$, $TYPE$]
  146
+      }
  147
+      """,
  148
+      Map("TYPE" -> Type, "C" -> Class, "CC" -> CaseClass))
3
Mirco Dotta Owner
dotta added a note August 12, 2012

"CC" -> CaseClass not needed.

Simon Schäfer
sschaef added a note August 12, 2012

def g needs CC. Have you overseen it or do you mean that it is unnecessary to check the tuple class?

Mirco Dotta Owner
dotta added a note August 12, 2012

Ooops. Sorry, my mistake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
... ...
@@ -52,7 +52,7 @@ trait SafeSymbol extends CompilerAccess with PimpedTrees {
52 52
       // if the original tree did not find anything, we need to call
53 53
       // symbol, which may trigger type checking of the underlying tree, so we
54 54
       // wrap it in 'ask'
55  
-      if (originalSym.isEmpty) {
  55
+      if (originalSym.isEmpty && tpeTree.pos.isRange) {
1
Mirco Dotta Owner
dotta added a note August 12, 2012

Why the check for range position? Could you add a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
((7 lines not shown))
91 117
   }
  118
+
  119
+  private def isViewBound(args: List[Tree]): Boolean =
  120
+    args.size == 2
  121
+
  122
+  private def isProbableTypeBound(name: Name): Boolean =
  123
+    name.startsWith("evidence$")
  124
+
  125
+  private def isStructuralType(ts: List[Tree]): Boolean =
  126
+    ts.size == 1
  127
+
  128
+  private def isTupleOrFunctionLiteral(tpt: Tree, qualifier: Tree, name: Name): Boolean = (
  129
+       qualifier.nameString == "scala"
  130
+    && (name.toString.startsWith("Function") || name.toString.startsWith("Tuple"))
2
Mirco Dotta Owner
dotta added a note August 12, 2012

Why not using a regex, so that the naming match is precise. WDYT?

Simon Schäfer
sschaef added a note August 12, 2012

Yes, good idea. I didn't think to that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
((10 lines not shown))
  84
+
  85
+    case tpe @ SelectFromTypeTree(qualifier, _) =>
  86
+      tpe.symbol -> tpe.namePosition :: safeSymbol(qualifier)
  87
+
  88
+    case CompoundTypeTree(Template(parents, _, body)) =>
  89
+      (if (isStructuralType(parents)) body else parents).flatMap(safeSymbol)
  90
+
  91
+    case TypeBoundsTree(lo, hi) =>
  92
+      List(lo, hi).flatMap(safeSymbol)
  93
+
  94
+    case ValDef(_, name, tpt: TypeTree, _) if isProbableTypeBound(name) =>
  95
+      tpt.original match {
  96
+        case AppliedTypeTree(_, args) if isViewBound(args) =>
  97
+          safeSymbol(args(1))
  98
+        case AppliedTypeTree(tpe, args) if isContextBound(args) =>
  99
+          List(tpe.symbol -> tpe.namePosition)
2
Mirco Dotta Owner
dotta added a note August 12, 2012

tpt.symbol can trigger side-effects (i.e., lazy typechecking), you should wrap this in a askOption, i.e., replace List(tpe.symbol -> tpe.namePosition) with global.askOption(() => (tpe.symbol, tpe.namePosition)).toList

Simon Schäfer
sschaef added a note August 12, 2012

Ok, good to know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
((7 lines not shown))
79 82
     case AppliedTypeTree(tpe, args) =>
80  
-      tpe.symbol -> tpe.namePosition :: args.flatMap(safeSymbol)
  83
+      (tpe :: args).flatMap(safeSymbol)
  84
+
  85
+    case tpe @ SelectFromTypeTree(qualifier, _) =>
  86
+      tpe.symbol -> tpe.namePosition :: safeSymbol(qualifier)
1
Mirco Dotta Owner
dotta added a note August 12, 2012

tpt.symbol can trigger side-effects (i.e., lazy typechecking), you should wrap this in a askOption, i.e., replace tpe.symbol -> tpe.namePosition :: safeSymbol(qualifier) with global.askOption(() => (tpe.symbol, tpe.namePosition)).toList ::: safeSymbol(qualifier)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
((7 lines not shown))
91 117
   }
  118
+
  119
+  private def isViewBound(args: List[Tree]): Boolean =
  120
+    args.size == 2
  121
+
  122
+  private def isProbableTypeBound(name: Name): Boolean =
  123
+    name.startsWith("evidence$")
2
Mirco Dotta Owner
dotta added a note August 12, 2012

If it works, it's better to use nme.EVIDENCE_PARAM_PREFIX. Though, it may not compile with Scala 2.9. (you need to test it :-)). If the name constant is not available in Scala 2.9, keep your code, but add a note so that we remember to get back to this after we drop support for Scala 2.9.

Simon Schäfer
sschaef added a note August 12, 2012

I executed the 2.9 test suite and all tests succeeded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Mirco Dotta
Owner

WOW! This is such an awesome PR, Simon. Great job!

Please review about

* commit messages. Are they enough?

Are there any opened ticket for the issues you have fixed? If yes, remember to have a Fix #<number> in your commit message.

* ignored test cases. Is it good to have them separated from the ones working?

I'm ok with this, however you didn't separate them in this PR, did you?

* my manually AST traverses. Maybe I have overseen a simple solution.

Looks good to me, but I'm not the compiler guru (Iulian is, but he's currently on holiday ;-) \cc @dragos)

Mirco Dotta
Owner

Oh, there is one more thing I should ask you to do: can you rebase your PR on the latest master? The reason is that 2.10 build should then succeed.

  • Update: Actually, you may not need to do this, the PR validator was not in a clean state. Let me try to re-build
Mirco Dotta
Owner

And I noticed another thing: In this commit message you say:

The presentation compiler doesn't store enough information in the AST in order to exactly determine if a view bound is found

The one to blame is not the presentation compiler, but the actual compiler itself ;-) (you may want to update the message)

Mirco Dotta
Owner

PLS REBUILD ALL

scala-jenkins
Collaborator

Started jenkins job pr-validator-master-trunk at https://jenkins.scala-ide.org:8496/jenkins/job/pr-validator-master-trunk/98/

scala-jenkins
Collaborator
scala-jenkins
Collaborator
scala-jenkins
Collaborator
Simon Schäfer

I didn't push the amends (I did only amends no new commits) yet. Can I do this normally with git push origin issue/semantic-highlight-ype-params or will this break some comments above (pointing to line numbers)?

Mirco Dotta
Owner

No luck, your branch is based on a old master that does no longer compile because of binary incompatibilities in sbt (which we use as the IDE internal project's builder). You'll have to rebase...

Simon Schäfer

The one to blame is not the presentation compiler, but the actual compiler itself ;-) (you may want to update the message)

I don't know where one ends and the other begins. ;) I thought the typer phase (the phase I working with in semantic highlighting?) is only done by the presentation compiler.

Mirco Dotta
Owner

I didn't push the amends (I did only amends no new commits) yet. Can I do this normally with git push origin issue/semantic-highlight-ype-params or will this break some comments above (pointing to line numbers)?

When you amend a commit, you are changing the history of your branch, and hence pushing will fail (you can always safely try out git commands by appending --dry-run). What you can do is a force push (i.e., git push -f origin issue/semantic-highlight-type-params), and this PR will be automatically updated. If you do so, make sure to go through all comments first, I'm not sure what will happen to the comments once you force push the branch (they are not deleted, but they can get into some weird unsync state and no longer be linked to a code line).

Simon Schäfer

No luck, your branch is based on a old master that does no longer compile because of binary incompatibilities in sbt (which we use as the IDE internal project's builder). You'll have to rebase...

How to do this? A normal

git checkout master
git fetch upstream
git merge upstream/master
git checkout issue/semantic-highlighting-type-params
git rebase master

did not work (all up to date).

Simon Schäfer

Are there any opened ticket for the issues you have fixed? If yes, remember to have a Fix # in your commit message.

No, there aren't. I was to lazy to create one. Is it useful to do it?

I'm ok with this, however you didn't separate them in this PR, did you?

With separating I meant separating them into an own test case.

Mirco Dotta
Owner

I don't know where one ends and the other begins. ;) I thought the typer phase (the phase I working with in semantic highlighting?) is only done by the presentation compiler.

That's a good question :-)

The presentation compiler executes only a subset of phases compared to the standard scala compiler, but these phases are the very same. The only real difference between the two is that a presentation compiler pass stops at the typer phase, instead of going until bytecode generation.

The main responsability of the presentation compiler is providing an interface for communicating with the scala compiler.

Mirco Dotta
Owner
  • About the rebasing, your commands looks good to me. Since it failed, try to go with an interactive rebase, i.e., git rebase -i master. You may need to solve some conflict, but I'd expect this to be easy for this PR.

  • It would be good to create at least one ticket with the list of functionalities you have implemented. This helps us creating a meaningful changelog when we release (so that people can know what has improved in the release)

With separating I meant separating them into an own test case.

What's a test case, a method? a class? Sorry, I'm having troubles following this. But if you want to split working and ignored tests in two separated test classes, I'm fine with this, as long as you add both classes in the TestSuite

added some commits August 12, 2012
Simon Schäfer Add semantic highlighting for view bounds
The compiler doesn't store enough information in the AST in order to
exactly determine if a view bound is found. Thus, it is necessary to
analyze the members of the tree types manually which is error-prone.
To be sure that no bugs are caused, additional test cases were introduced.
9c00a68
Simon Schäfer Add test case for higher kinded types to semantic highlighting test s…
…uite
3327343
Simon Schäfer Add semantic highlighting for existential types 00e131a
Simon Schäfer Add semantic highlighting for type projections and path dependent types cee80b8
Simon Schäfer

PLS REBUILD ALL

scala-jenkins
Collaborator
scala-jenkins
Collaborator
scala-jenkins
Collaborator
scala-jenkins
Collaborator
Simon Schäfer

git rebase -i master works fine, thanks.

What's a test case, a method?

Yes, I meant a method. Sry for the wrong word. I'll let it as it is, it is ok.

Mirco Dotta
Owner

Yes, I meant a method. Sry for the wrong word. I'll let it as it is, it is ok.

Perfect. So, the only thing that is missing is a ticket that lists all your changes.

Mirco Dotta
Owner

I also wonder if there isn't a more precise way to check isViewBound, isStructuralType, isContextBound and similar. I'll ask the EPFL folks and get back to you during the day.

Mirco Dotta
Owner

I also wonder if there isn't a more precise way to check isViewBound, isStructuralType, isContextBound and similar. I'll ask the EPFL folks and get back to you during the day.

I checked and apparently there is no better way, so it's all good. As soon as you create a ticket for this, we can merge this PR.

Simon Schäfer

As soon as you create a ticket for this, we can merge this PR.

Done: http://www.assembla.com/spaces/scala-ide/tickets/1001204

Does it provide enough information or should I add more (links to PR for example)?

Mirco Dotta dotta merged commit 3f934f8 into from August 13, 2012
Mirco Dotta dotta closed this August 13, 2012
Mirco Dotta
Owner

Fantastic! I have merged and updated the ticket. Keep on rockin' :-)

Mirco Dotta
Owner

I see now we have quite a few open tickets for Semantich Highlighting: http://scala-ide-portfolio.assembla.com/spaces/ae55a-oWSr36hpeJe5avMc/tickets/report/u362933

Are you sure that none of them can be closed after your PR? (or, if you are having fun with semantic highlighting, you could have a look at them ;-))

Simon Schäfer

I already looked at these tickets, but this PR doesn't solve any one them. Half of the tickets there are submitted by me and I spent already some time on them, but again it was the Scala compiler which doesn't store enough information in the AST. Thus, I could not solve the problems. :(

I will going on working on semantic highlighting. Hopefully I will find a ticket which can be solved...

Mirco Dotta
Owner

I already looked at these tickets, but this PR doesn't solve any one them. Half of the tickets there are submitted by me and I spent already some time on them, but again it was the Scala compiler which doesn't store enough information in the AST. Thus, I could not solve the problems. :(

Eh eh eh, I did not pay attention to the fact that it was you that filed the tickets :-) And you are absolutely right, the compiler AST doesn't always provide enough information, and that's why semantic highlight also use the scalariform AST (did you noticed it?). Maybe with the scalariform AST you can find the missing information (it's something to investigate if you have time and will).

I will going on working on semantic highlighting. Hopefully I will find a ticket which can be solved...

I know we're nowhere near XMas, but here is my wish list :-)

Simon Schäfer

What shall I do with my local branch after the merge? Shall I delete it?

Simon Schäfer

and that's why semantic highlight also use the scalariform AST (did you noticed it?).

Yeah, I noticed it, but didn't understand why it is there ;)

Maybe with the scalariform AST you can find the missing information (it's something to investigate if you have time and will).

Let's see what I can do.

Mirco Dotta
Owner

What shall I do with my local branch after the merge? Shall I delete it?

There is really no reason to keep them around ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 11 unique commits by 1 author.

Aug 12, 2012
Simon Schäfer Add semantic highlighting for compound types 29187b4
Simon Schäfer Add semantic highlighting for context bounds aefde34
Simon Schäfer Add semantic highlighting for function literals 6646111
Simon Schäfer Add semantic highlighting for tuple literals a247aaa
Simon Schäfer Add semantic highlighting for structural types 60cb2f6
Simon Schäfer Add semantic highlighting for upper and lower bounds 4799de3
Simon Schäfer Update of `RegionParser`
The previous version of `RegionParser` was unable to parse regions which
contain the delimiter sign. Now it is possible to put delimiter signs to
the parsed text by prefixing them with an escape sign.
d3c3114
Aug 13, 2012
Simon Schäfer Add semantic highlighting for view bounds
The compiler doesn't store enough information in the AST in order to
exactly determine if a view bound is found. Thus, it is necessary to
analyze the members of the tree types manually which is error-prone.
To be sure that no bugs are caused, additional test cases were introduced.
9c00a68
Simon Schäfer Add test case for higher kinded types to semantic highlighting test s…
…uite
3327343
Simon Schäfer Add semantic highlighting for existential types 00e131a
Simon Schäfer Add semantic highlighting for type projections and path dependent types cee80b8
This page is out of date. Refresh to see the latest.
72  org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/RegionParser.scala
... ...
@@ -1,27 +1,57 @@
1 1
 package scala.tools.eclipse.semantichighlighting.classifier
2 2
 
3  
-/**
4  
- * Search for regions delimited with $ signs.
5  
- */
6 3
 object RegionParser {
7 4
 
8  
-    def getRegions(text: String): Map[Region, String] = {
9  
-      var regions = Map[Region, String]()
10  
-      var regionStartOpt: Option[Int] = None
  5
+  /**
  6
+   * Search for regions delimited with a sign. In the default case the
  7
+   * delimited sign is a '$'.
  8
+   *
  9
+   * It is possible to put delimiter signs into the text by escaping them with
  10
+   * a '\'.
  11
+   *
  12
+   * @example {{{
  13
+   * scala> getRegions("""$abc$ def $ghi$""")
  14
+   * res200: Map[Region,String] = Map(Region(0,5) -> abc, Region(10,5) -> ghi)
  15
+   *
  16
+   * scala> getRegions("""$a\$bc$ de\$f $ghi$""")
  17
+   * res201: Map[Region,String] = Map(Region(0,6) -> a$bc, Region(12,5) -> ghi)
  18
+   *
  19
+   * scala> getRegions("""|a\|bc| de\|f |ghi|""", delimiter = '|')
  20
+   * res202: Map[Region,String] = Map(Region(0,6) -> a|bc, Region(12,5) -> ghi)
  21
+   * }}}
  22
+   * '''Note:''' When a delimiter sign is escaped, the resulting `Region` instances
  23
+   * are handled as there were no escape sign. This means that the String `$a\$b$`
  24
+   * is treated as  `$a$b$`.
  25
+   */
  26
+  def getRegions(text: String, delimiter: Char = '$'): Map[Region, String] = {
  27
+    val sb = new StringBuilder
  28
+    var curPos = 0
  29
+    var offset = 0
  30
+    var regions = Map.empty[Region, String]
11 31
 
12  
-      for (('$', pos) <- text.zipWithIndex)
13  
-        regionStartOpt match {
14  
-          case Some(regionStart) =>
15  
-            regionStartOpt = None
16  
-            val region = Region(regionStart, pos - regionStart + 1)
17  
-            val regionLabel = text.substring(regionStart + 1, pos).trim
18  
-            regions += (region -> regionLabel)
19  
-          case None =>
20  
-            regionStartOpt = Some(pos)
21  
-        }
22  
-
23  
-      require(regionStartOpt.isEmpty)
24  
-      regions
  32
+    while (curPos < text.length) {
  33
+      text.charAt(curPos) match {
  34
+        case '\\' if text.charAt(curPos+1) == delimiter =>
  35
+          if (!sb.isEmpty)
  36
+            sb += delimiter
  37
+          offset += 1
  38
+          curPos += 1
  39
+        case `delimiter` =>
  40
+          if (sb.isEmpty)
  41
+            sb += text.charAt(curPos)
  42
+          else {
  43
+            val start = curPos-sb.length
  44
+            val label = sb.substring(1, sb.length).trim
  45
+            sb.clear()
  46
+            regions += (Region(start-offset, curPos-start+1) -> label)
  47
+          }
  48
+        case _ =>
  49
+          if (!sb.isEmpty)
  50
+            sb += text.charAt(curPos)
  51
+      }
  52
+      curPos += 1
25 53
     }
26  
-
27  
-  }
  54
+    require(sb.isEmpty, "odd number of '"+delimiter+"' signs in text")
  55
+    regions
  56
+  }
  57
+}
177  ...scala-ide.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeParameterTest.scala
@@ -70,4 +70,181 @@ class TypeParameterTest extends AbstractSymbolClassifierTest {
70 70
       """,
71 71
       Map("TPARAM" -> TypeParameter, "T" -> Type))
72 72
   }
  73
+
  74
+  @Test
  75
+  def partial_compound_type_param() {
  76
+    checkSymbolClassification("""
  77
+      trait X {
  78
+        def xs[TypeParam]: Seq[TypeParam] with collection.IterableLike[TypeParam, TypeParam]
  79
+      }
  80
+      """, """
  81
+      trait X {
  82
+        def xs[$TPARAM $]: Seq[$TPARAM $] with collection.IterableLike[$TPARAM $, $TPARAM $]
  83
+      }
  84
+      """,
  85
+      Map("TPARAM" -> TypeParameter))
  86
+  }
  87
+
  88
+  @Test
  89
+  @Ignore("does not work until presentation compiler stores more information in the AST")
  90
+  def full_compound_type_param() {
  91
+    checkSymbolClassification("""
  92
+      trait X {
  93
+        def xs[TypeParam]: Seq[TypeParam] with collection.IterableLike[TypeParam, TypeParam]
  94
+      }
  95
+      """, """
  96
+      trait X {
  97
+        def xs[$TPARAM $]: $T$[$TPARAM $] with collection.$TRAIT     $[$TPARAM $, $TPARAM $]
  98
+      }
  99
+      """,
  100
+      Map("TPARAM" -> TypeParameter, "T" -> Type, "TRAIT" -> Trait))
  101
+  }
  102
+
  103
+  @Test
  104
+  def context_bound_type_param() {
  105
+    checkSymbolClassification("""
  106
+      trait X {
  107
+        def xs[A : Ordering](a: A)
  108
+        def ys[TypeParam](a: TypeParam)(implicit evidence$1: Ordering[TypeParam])
  109
+      }
  110
+      """, """
  111
+      trait X {
  112
+        def xs[A : $TYPE  $](a: A)
  113
+        def ys[$TPARAM $](a: $TPARAM $)(implicit evidence\$1: $TYPE  $[$TPARAM $])
  114
+      }
  115
+      """,
  116
+      Map("TPARAM" -> TypeParameter, "TYPE" -> Type))
  117
+  }
  118
+
  119
+  @Test
  120
+  def function_literal_type_param() {
  121
+    checkSymbolClassification("""
  122
+      trait X {
  123
+        def f: Int => String
  124
+        def g: Function1[Int, String]
  125
+      }
  126
+      """, """
  127
+      trait X {
  128
+        def f: $C$ => $TYPE$
  129
+        def g: $TRAIT  $[$C$, $TYPE$]
  130
+      }
  131
+      """,
  132
+      Map("TYPE" -> Type, "C" -> Class, "TRAIT" -> Trait))
  133
+  }
  134
+
  135
+  @Test
  136
+  def tuple_literal_type_param() {
  137
+    checkSymbolClassification("""
  138
+      trait X {
  139
+        def f: (Int, String)
  140
+        def g: Tuple2[Int, String]
  141
+      }
  142
+      """, """
  143
+      trait X {
  144
+        def f: ($C$, $TYPE$)
  145
+        def g: $CC  $[$C$, $TYPE$]
  146
+      }
  147
+      """,
  148
+      Map("TYPE" -> Type, "C" -> Class, "CC" -> CaseClass))
  149
+  }
  150
+
  151
+  @Test
  152
+  def partial_structural_type_param() {
  153
+    checkSymbolClassification("""
  154
+      trait X {
  155
+        def f(s: { def foo[TypeParam](i: TypeParam): Int })
  156
+      }
  157
+      """, """
  158
+      trait X {
  159
+        def f(s: { def $M$[TypeParam](i: TypeParam): Int })
  160
+      }
  161
+      """,
  162
+      Map("M" -> Method))
  163
+  }
  164
+
  165
+  @Test
  166
+  @Ignore("does not work until presentation compiler stores more information in the AST")
  167
+  def full_structural_type_param() {
  168
+    checkSymbolClassification("""
  169
+      trait X {
  170
+        def f(s: { def foo[TypeParam](i: TypeParam): Int })
  171
+      }
  172
+      """, """
  173
+      trait X {
  174
+        def f(s: { def $M$[$TPARAM $](i: $TPARAM $): $C$ })
  175
+      }
  176
+      """,
  177
+      Map("TPARAM" -> TypeParameter, "C" -> Class, "M" -> Method))
  178
+  }
  179
+
  180
+  @Test
  181
+  def bounded_type_param() {
  182
+    checkSymbolClassification("""
  183
+      trait X {
  184
+        type Type[TypeParam] >: List[TypeParam] <: Iterable[TypeParam]
  185
+      }
  186
+      """, """
  187
+      trait X {
  188
+        type $T $[$TPARAM $] >: $T $[$TPARAM $] <: $T     $[$TPARAM $]
  189
+      }
  190
+      """,
  191
+      Map("TPARAM" -> TypeParameter, "T" -> Type))
  192
+  }
  193
+
  194
+  @Test
  195
+  def view_bound_type_param() {
  196
+    checkSymbolClassification("""
  197
+      trait X {
  198
+        def xs[TypeParam <% Ordering[TypeParam]](a: TypeParam)
  199
+        def ys[TypeParam](a: TypeParam)(implicit evidence$1: TypeParam => Ordering[TypeParam])
  200
+      }
  201
+      """, """
  202
+      trait X {
  203
+        def xs[$TPARAM $ <% $TYPE  $[$TPARAM $]](a: $TPARAM $)
  204
+        def ys[$TPARAM $](a: $TPARAM $)(implicit evidence\$1: $TPARAM $ => $TYPE  $[$TPARAM $])
  205
+      }
  206
+      """,
  207
+      Map("TPARAM" -> TypeParameter, "TYPE" -> Type))
  208
+  }
  209
+
  210
+  @Test
  211
+  def higher_kinded_type_param() {
  212
+    checkSymbolClassification("""
  213
+      trait M[A[_]]
  214
+      trait H extends M[List]
  215
+      """, """
  216
+      trait M[A[_]]
  217
+      trait H extends M[$HK$]
  218
+      """,
  219
+      Map("HK" -> Type))
  220
+  }
  221
+
  222
+  @Test
  223
+  def partial_existential_type_param() {
  224
+    checkSymbolClassification("""
  225
+      trait X {
  226
+        def xs[TypeParam]: Res[TypeParam] forSome { type Res[_] <: Seq[_] }
  227
+      }
  228
+      """, """
  229
+      trait X {
  230
+        def xs[$TPARAM $]: $T$[$TPARAM $] forSome { type $T$[_] <: Seq[_] }
  231
+      }
  232
+      """,
  233
+      Map("TPARAM" -> TypeParameter, "T" -> Type))
  234
+  }
  235
+
  236
+  @Test
  237
+  @Ignore("does not work until presentation compiler stores more information in the AST")
  238
+  def full_existential_type_param() {
  239
+    checkSymbolClassification("""
  240
+      trait X {
  241
+        def xs[TypeParam]: Res[TypeParam] forSome { type Res[_] <: Seq[_] }
  242
+      }
  243
+      """, """
  244
+      trait X {
  245
+        def xs[$TPARAM $]: $T$[$TPARAM $] forSome { type $T$[_] <: $T$[_] }
  246
+      }
  247
+      """,
  248
+      Map("TPARAM" -> TypeParameter, "T" -> Type))
  249
+  }
73 250
 }
51  org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/semantichighlighting/classifier/TypeTest.scala
@@ -43,39 +43,36 @@ class TypeTest extends AbstractSymbolClassifierTest {
43 43
       """,
44 44
       Map("T" -> Type, "V" -> TemplateVal, "C" -> Class))
45 45
   }
46  
-  
  46
+
47 47
   @Test
48  
-  @Ignore
49  
-  def classify_existential_type_1() {
50  
-    checkSymbolClassification(
51  
-    """
52  
-      object O {
53  
-        def m(x : t forSome {type t <: AnyRef}) = x
  48
+  def path_dependent_type() {
  49
+    checkSymbolClassification("""
  50
+      trait MTrait { trait KTrait[A] }
  51
+      trait X {
  52
+        def xs(m: MTrait)(k: m.KTrait[Int])
54 53
       }
55  
-    """", 
56  
-    """
57  
-      object O {
58  
-        def m(x : t forSome {type t <: $ TPE$ }) = x
  54
+      """, """
  55
+      trait MTrait { trait KTrait[A] }
  56
+      trait X {
  57
+        def xs(m: $TT  $)(k: m.$TT  $[$C$])
59 58
       }
60  
-    """", 
61  
-    Map("TPE" -> Type))
  59
+      """,
  60
+      Map("C" -> Class, "TT" -> Trait))
62 61
   }
63  
-  
  62
+
64 63
   @Test
65  
-  @Ignore
66  
-  def classify_existential_type_2() {
67  
-    checkSymbolClassification(
68  
-    """
69  
-      object O {
70  
-        def m(x : t forSome {type t <: List [_]}) = x
  64
+  def type_projection() {
  65
+    checkSymbolClassification("""
  66
+      trait MTrait { trait KTrait[A] }
  67
+      trait X {
  68
+        def xs(m: MTrait#KTrait[Int])
71 69
       }
72  
-    """", 
73  
-    """
74  
-      object O {
75  
-        def m(x : t forSome {type t <: $TPE$[_]}) = x
  70
+      """, """
  71
+      trait MTrait { trait KTrait[A] }
  72
+      trait X {
  73
+        def xs(m: $TT  $#$TT  $[$C$])
76 74
       }
77  
-    """", 
78  
-    Map("TPE" -> Type))
  75
+      """,
  76
+      Map("C" -> Class, "TT" -> Trait))
79 77
   }
80  
-
81 78
 }
65  org.scala-ide.sdt.core/src/scala/tools/eclipse/semantichighlighting/classifier/SafeSymbol.scala
@@ -40,7 +40,7 @@ trait SafeSymbol extends CompilerAccess with PimpedTrees {
40 40
    * TransparentPositions come into play for trees that don't have a source-code
41 41
    * correspondence but still have children that are visible in the source.
42 42
    */
43  
-  protected def isSourceTree(t: Tree): Boolean = t.pos.isRange && !t.pos.isTransparent
  43
+  protected def isSourceTree(t: Tree): Boolean = hasSourceCodeRepresentation(t) && !t.pos.isTransparent
44 44
 
45 45
   protected def safeSymbol(t: Tree): List[(Symbol, Position)] = t match {
46 46
 
@@ -52,7 +52,7 @@ trait SafeSymbol extends CompilerAccess with PimpedTrees {
52 52
       // if the original tree did not find anything, we need to call
53 53
       // symbol, which may trigger type checking of the underlying tree, so we
54 54
       // wrap it in 'ask'
55  
-      if (originalSym.isEmpty) {
  55
+      if (originalSym.isEmpty && hasSourceCodeRepresentation(tpeTree)) {
56 56
         val tpeSym = global.askOption(() => Option(t.symbol)).flatten.toList
57 57
         tpeSym.zip(List(tpeTree.namePosition))
58 58
       } else originalSym
@@ -76,8 +76,33 @@ trait SafeSymbol extends CompilerAccess with PimpedTrees {
76 76
         else List((sym1, pos))
77 77
       }).flatten
78 78
 
  79
+    case AppliedTypeTree(tpe, args) if isTupleOrFunctionLiteral(tpe) =>
  80
+      args.flatMap(safeSymbol)
  81
+
79 82
     case AppliedTypeTree(tpe, args) =>
80  
-      tpe.symbol -> tpe.namePosition :: args.flatMap(safeSymbol)
  83
+      (tpe :: args).flatMap(safeSymbol)
  84
+
  85
+    case tpe @ SelectFromTypeTree(qualifier, _) =>
  86
+      global.askOption(() => tpe.symbol -> tpe.namePosition).toList ::: safeSymbol(qualifier)
  87
+
  88
+    case CompoundTypeTree(Template(parents, _, body)) =>
  89
+      (if (isStructuralType(parents)) body else parents).flatMap(safeSymbol)
  90
+
  91
+    case TypeBoundsTree(lo, hi) =>
  92
+      List(lo, hi).flatMap(safeSymbol)
  93
+
  94
+    case ValDef(_, name, tpt: TypeTree, _) if isProbableTypeBound(name) =>
  95
+      tpt.original match {
  96
+        case AppliedTypeTree(_, args) if isViewBound(args) =>
  97
+          safeSymbol(args(1))
  98
+        case AppliedTypeTree(tpe, args) if isContextBound(args) =>
  99
+          global.askOption(() => tpe.symbol -> tpe.namePosition).toList
  100
+        case tpt =>
  101
+          safeSymbol(tpt)
  102
+      }
  103
+
  104
+    case ExistentialTypeTree(tpt, whereClauses) =>
  105
+      (tpt :: whereClauses).flatMap(safeSymbol)
81 106
 
82 107
     case _ =>
83 108
       // the local variable backing a lazy value is called 'originalName$lzy'. We swap it here for its
@@ -87,6 +112,38 @@ trait SafeSymbol extends CompilerAccess with PimpedTrees {
87 112
         else sym
88 113
       }.toList
89 114
 
90  
-      sym1.zip(List(t.namePosition))
  115
+      if (!hasSourceCodeRepresentation(t)) Nil
  116
+      else sym1.zip(List(t.namePosition))
91 117
   }
  118
+
  119
+  private def isViewBound(args: List[Tree]): Boolean =
  120
+    args.size == 2
  121
+
  122
+  private def isProbableTypeBound(name: Name): Boolean =
  123
+    name.startsWith(nme.EVIDENCE_PARAM_PREFIX)
  124
+
  125
+  private def isStructuralType(ts: List[Tree]): Boolean =
  126
+    ts.size == 1
  127
+
  128
+  private def isTupleOrFunctionLiteral(tpt: Tree): Boolean =
  129
+    tpt.toString.matches(""".*scala\.(Tuple|Function).*""") && !hasSourceCodeRepresentation(tpt)
  130
+
  131
+  private def isContextBound(args: List[Tree]): Boolean =
  132
+    args.size == 1 && !hasSourceCodeRepresentation(args.head)
  133
+
  134
+  /*
  135
+   * Sometimes the compiler enrich the AST with some trees not having a source
  136
+   * code representation. This is true for tuple or function literals, for
  137
+   * view and context bounds and others.
  138
+   * 
  139
+   * The problem is that such trees don't have a `Range` because these trees are
  140
+   * generated by the compiler to have a representation for symbols not written
  141
+   * directly into the source code (but written in form of there corresponding
  142
+   * literals).
  143
+   * 
  144
+   * Thus, calling the position of such a tree results in an exception. To avoid
  145
+   * this exception one needs to call this method to be on the safe side.
  146
+   */
  147
+  private def hasSourceCodeRepresentation(tpt: Tree): Boolean =
  148
+    tpt.pos.isRange
92 149
 }
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.