Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

implemented an extendible quick fix functionality for type mismatch

errors
  • Loading branch information...
commit cb2e25c629c0da8d6d6784dd7289d168d08dfd3a 1 parent f95b2ce
@kaptoxic authored
View
72 org.scala-ide.sdt.core/src/scala/tools/eclipse/quickfix/ScalaQuickFixProcessor.scala
@@ -1,10 +1,12 @@
package scala.tools.eclipse.quickfix
+// Eclipse
+import org.eclipse.core.runtime.CoreException
+import org.eclipse.core.runtime.NullProgressMonitor
import org.eclipse.ui.IEditorPart
-import org.eclipse.jface.text.source.Annotation
+import org.eclipse.ui.texteditor.ITextEditor
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
import org.eclipse.jdt.ui.JavaUI
-import org.eclipse.core.runtime.CoreException
import org.eclipse.jdt.core.ICompilationUnit
import org.eclipse.jdt.core.search.IJavaSearchConstants
import org.eclipse.jdt.internal.codeassist.ISearchRequestor
@@ -12,22 +14,32 @@ import org.eclipse.jdt.internal.compiler.env.{ AccessRestriction }
import org.eclipse.jdt.internal.core.{ DefaultWorkingCopyOwner, JavaProject }
import org.eclipse.jdt.internal.ui.text.correction.{ SimilarElement, SimilarElementsRequestor }
import org.eclipse.jdt.ui.text.java._
-import org.eclipse.core.resources.IMarker
+import org.eclipse.jdt.core.search.{ TypeNameMatch, SearchEngine }
+import org.eclipse.jdt.core.IJavaElement
+import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector
+import org.eclipse.jface.text.source.Annotation
+import org.eclipse.jface.text.Position
+import org.eclipse.jface.text.IDocument
+
+// Scala IDE
import scala.tools.eclipse.javaelements.ScalaSourceFile
import scala.tools.eclipse.util.FileUtils
+import scala.tools.eclipse.util.EditorUtils.getAnnotationsAtOffset
+import scala.tools.eclipse.semantichighlighting.implicits.ImplicitHighlightingPresenter
+import scala.tools.eclipse.logging.HasLogger
+
+// Scala
import scala.util.matching.Regex
-import org.eclipse.jdt.core.search.TypeNameMatch
-import org.eclipse.jdt.core.search.SearchEngine
-import org.eclipse.jdt.core.IJavaElement
-import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector
-import org.eclipse.core.runtime.NullProgressMonitor
import collection.JavaConversions._
-class ScalaQuickFixProcessor extends IQuickFixProcessor {
+class ScalaQuickFixProcessor extends IQuickFixProcessor with HasLogger {
private val typeNotFoundError = new Regex("not found: type (.*)")
private val valueNotFoundError = new Regex("not found: value (.*)")
private val xxxxxNotFoundError = new Regex("not found: (.*)")
+ // regex for extracting expected and required type on type mismatch
+ private val typeMismatchError = new Regex("type mismatch;\\s*found\\s*: (\\S*)\\s*required: (.*)")
+
/**
* Checks if the processor has any corrections.
*
@@ -36,7 +48,6 @@ class ScalaQuickFixProcessor extends IQuickFixProcessor {
* slightly more responsive.
*/
def hasCorrections(unit : ICompilationUnit, problemId : Int) : Boolean = true
-
/**
* Collects corrections or code manipulations for the given context.
@@ -53,9 +64,15 @@ class ScalaQuickFixProcessor extends IQuickFixProcessor {
val editor = JavaUI.openInEditor(context.getCompilationUnit)
var corrections : List[IJavaCompletionProposal] = Nil
for (location <- locations)
- for (ann <- getAnnotationsAtOffset(editor, location.getOffset)) {
- val fix = suggestFix(context.getCompilationUnit(), ann.getText)
- corrections = corrections ++ fix
+ for ((ann, pos) <- getAnnotationsAtOffset(editor, location.getOffset)) {
+ val importFix = suggestImportFix(context.getCompilationUnit(), ann.getText)
+
+ // compute all possible type mismatch quick fixes
+ val document = (editor.asInstanceOf[ITextEditor]).getDocumentProvider().getDocument(editor.getEditorInput())
+ val typeMismatchFix = suggestTypeMismatchFix(document, ann.getText, pos)
+
+ // concatenate lists of found quick fixes
+ corrections = corrections ++ importFix ++ typeMismatchFix
}
corrections match {
case Nil => null
@@ -65,7 +82,8 @@ class ScalaQuickFixProcessor extends IQuickFixProcessor {
case _ => null
}
- private def getAnnotationsAtOffset(part: IEditorPart, offset: Int): List[Annotation] = {
+ // XXX is this code duplication? -- check scala.tools.eclipse.util.EditorUtils.getAnnotationsAtOffset
+ private def getAnnotationsAtOffsetXXX(part: IEditorPart, offset: Int): List[Annotation] = {
import ScalaQuickFixProcessor._
var ret = List[Annotation]()
@@ -81,7 +99,7 @@ class ScalaQuickFixProcessor extends IQuickFixProcessor {
}
private
- def suggestFix(compilationUnit : ICompilationUnit, problemMessage : String) : List[IJavaCompletionProposal] = {
+ def suggestImportFix(compilationUnit : ICompilationUnit, problemMessage : String) : List[IJavaCompletionProposal] = {
/**
* Import a type could solve several error message :
*
@@ -106,6 +124,30 @@ class ScalaQuickFixProcessor extends IQuickFixProcessor {
case _ => Nil
}
}
+ private
+ def suggestTypeMismatchFix(document : IDocument, problemMessage : String, location: Position) : List[IJavaCompletionProposal] = {
+ // get the annotation string
+ val annotationString = document.get(location.getOffset, location.getLength)
+ // match problem message
+ return problemMessage match {
+ // extract found and required type
+ case typeMismatchError(foundType, requiredType) =>
+ // utilize type mismatch computer to find quick fixes
+ val replacementStringList = TypeMismatchQuickFixProcessor(foundType, requiredType, annotationString)
+
+ // map replacements strings into expanding proposals
+ replacementStringList map {
+ replacementString =>
+ // make markers message in form: "... =>replacement"
+ val markersMessage = annotationString + " " + ImplicitHighlightingPresenter.DisplayStringSeparator + replacementString
+ // construct a proposal with the appropriate location
+ new ExpandingProposalBase(markersMessage, "Transform your code expression: ", location)
+ }
+ // no match found for the problem message
+ case _ => Nil
+ }
+ }
+
}
object ScalaQuickFixProcessor {
View
114 org.scala-ide.sdt.core/src/scala/tools/eclipse/quickfix/TypeMismatchQuickFixProcessor.scala
@@ -0,0 +1,114 @@
+package scala.tools.eclipse.quickfix
+
+// Java imports
+import java.util.regex.Pattern
+
+/**
+ * @author ivcha
+ * This object is used for applying code transformations based on the found and required type
+ * extract from the annotation message (such as quick fix message) and the expression in the source code.
+ * The object arguments are: found type string, required type string and annotation string, respective and
+ * the result is a list of strings which should replace the annotation string
+ */
+object TypeMismatchQuickFixProcessor extends
+ ((String, String, String) => List[String]) {
+
+ /** list containing all type mismatch quick fix cases that this object should go through */
+ val cases: List[TypeMismatchQuickFixCase] =
+ List(
+ // "type mismatch: List[T] but found List[List[T]]
+ FoundToRequiredTypeCase(
+ List("%s.flatten", "%s.head", "%s.last"),
+ Pattern.compile("List\\[List\\[(.*)\\]\\]"), Pattern.compile("List\\[(.*)\\]"), Pattern.compile("^(.*)$")
+ ),
+ // "type mismatch: Array[T] but found List[T]
+ FoundToRequiredTypeCase(
+ List("%s.toArray"),
+ Pattern.compile("List\\[(.*)\\]"), Pattern.compile("Array\\[(.*)\\]"), Pattern.compile("^(.*)$")
+ ),
+ // "type mismatch: found T; required Option[T]" -> suggest to wrap the result in Some()
+ FoundToRequiredTypeCase(
+ List("Some(%s)"),
+ Pattern.compile("(.*)"), Pattern.compile("Option\\[(.*)\\]"), Pattern.compile("^(.*)$")
+ )
+ )
+
+ /**
+ * apply method for getting list of replacement strings
+ * @param foundType extracted found type string
+ * @param requiredType extracted required type string
+ * @param annotationString extracted expression string from the source code
+ * @return list of strings which should replace the annotation string
+ */
+ def apply(foundType: String, requiredType: String, annotationString: String): List[String] =
+ // go through all cases and collect lists of replacement strings
+ (List[String]() /: cases) {
+ case (list, ftrtc:FoundToRequiredTypeCase) => {
+ list ++ ftrtc.apply(foundType, requiredType, annotationString)
+ }
+ }
+
+}
+
+/** trait marking all type mismatch quick fix cases */
+trait TypeMismatchQuickFixCase
+
+/**
+ * class which is to be inherited if quick fix simply injects a sequence of strings into a format strings of
+ * form "... %s... %s..."
+ */
+abstract class SimpleFormatQuickFixCase(formatStrings: List[String]) extends TypeMismatchQuickFixCase {
+ def apply(listOfInjectStrings: Seq[String]*) =
+ for (
+ // iterate through all sequences of strings to inject
+ injectString <- listOfInjectStrings;
+ // iterate through all given format
+ formatString <- formatStrings
+ // yield a string when inject strings are applied to the format string
+ ) yield { formatString.format( injectString:_* ) }
+}
+
+/**
+ * class which checks whether found type string and required type string match - it does by
+ * capturing all groups according to the found and required patterns and compares them for match -
+ * if all match, the replacement is proceeded by extracting inject strings from the annotation pattern
+ * and applying them to SimpleFormatQuickFixCase
+ * found and required patterns should extract the same number of groups
+ * annotation string should extract required number of groups to feed the given format string
+ */
+case class FoundToRequiredTypeCase(formatStrings: List[String],
+ found: Pattern, required: Pattern, annotationExtract: Pattern) extends SimpleFormatQuickFixCase(formatStrings) {
+ def apply(foundType: String, requiredType: String, annotationString: String): Seq[String] = {
+ // get matchers
+ val foundMatcher = found.matcher(foundType)
+ val requiredMatcher = required.matcher(requiredType)
+
+ // if both matched
+ // NOTE (we expect only a single match)
+ if (foundMatcher.find && requiredMatcher.find) {
+ // check if all groups match
+ if (
+ // fold all groups and compare them - capturing group count must be the same for both patterns
+ (true /: (1 to foundMatcher.groupCount()) ) {
+ case (false, _) => false
+ case (result, ind) => foundMatcher.group(ind) == requiredMatcher.group(ind)
+ }
+ ) {
+ // get annotation matcher
+ val annotationMatcher = annotationExtract.matcher(annotationString)
+ // check if find can pass (only single match is expected)
+ if (annotationMatcher.find) {
+ // get injection strings
+ val injectStrings =
+ for (ind <- 1 to annotationMatcher.groupCount()) yield { annotationMatcher.group(ind) }
+ // apply them to the format string
+ super.apply(injectStrings)
+ // in case annotation matcher cannot find
+ } else Nil
+ }
+ // in case groups don't match
+ else Nil
+ // in case matchers fail
+ } else Nil
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.