aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
blob: 87837fd82c0b1a817751017565adafadbb988d7f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12




                   
                  





                       
                          
                              


                                                   
                              
                          
                       
                                                         
 
                 
 
                                                                               
              
                      

                                               

                
                      

                                                 

             
                      

                                              

                                    
                      

                                 



                                                               
                      

                                            



                                                                    
                      

                                            



                                                                      
                      

                                            



                                                                        
                      

                                            


                                                                      

               





                                                                               

                                                         

          








                                                                                       
 
                                                                                

                                                                                                            






                                                



                                               















                                                                                                


                                                                                              
                                                                     


     
                                                                        
                                                               


                                                                         
                                                                            


                                                                                  
                                                                          


                                                                                  
                                                              


                                                            
                                             


                                                                               
                     

                                                                              
                                                                          
   
 
                                                                                  
                                                






                                                                                      
                                                                            


   
                                                                                
                                                                                        
                                    
















                                                            
                                                                          


                    
                                                                               


     
                                                                                                   
                                   
                                   


                                           



                                                                                    

     
 
                                                                                                                                    
                                   
                              


                                                                           


                         
                                                               





                                                                                        
                                 

                                 

                                                                                                           



                                             
                                                                                    


















































                                                                             


                        
 
                                                                  
                                                   
                       

                                                                                 
















                                                                             
 







                                                                        
 












                                                                                          

                                                                              
                                            
                       
                                                                 


                                                                          

                                                                                  

                                                             



                                                                                           
            
                                                        








                                                                                                       
                                            



                                                                          
                                        
                       
                                                                           


                                                                                
            


                                               


                                                                                   
                                               
                       
                                                                 

                       
                                            
                                           
                                                        



                                                                       

                                                       



                                                                      
                                   

                                    
                                                                                 




                                                                             
                                                                                                                
            
                                          

     
 
                                                                     
                                       
                       

                                                 


                                                                  









                                                                              

               

   
                                                            
                                              
                       
                                                                          











                                                                                               

   
                                                                
                                                  
                       
                                                                   
                     




























                                                                                
                                             












                                                                                          
                                                            

     
 

                                                      


                                   




                                                                              

   
                                                                         
                                                          

                                                
 
                     


                                                                                        


                                                                                
                                                      


                        
                                                        


                                                         
                                                                                          

                                                                                   
            
                                             
            
                             
            
                                                                         
            
               
   
 
                                                              
                                               
                                     
                                                                      

                       











                                                                                          
                                                                                      

                                                                                      


                                                               
                                                                             


                      
                                                                                           



                                                                   


                                                                                         


     
                                                                 
                                                  
                       

















                                                                                                    
               
   
 
                                                                                                                               
                                            
                       



                                                                                         
 
                                               



                                           
 
                                                                             
 
                                                     


                                          

             
                                                                                  



                                                     



                                                                       



                                                                      



                                                                                    
                                     

                                                          
                                                                                                 

     

                                                                         
                                                          
                       
                                                                 
                       












                                                      


                                                                                          
            


                                                                                 
                                     
     

   
                                                                          
                                         
                       
                                   
                       




                                                                                        






                                 
               

     
 
                                                                            
                                                              


                                                                       








                                                                                      
   
 

                                                                      























                                                                                
 

                                                    


                               
                                                                  







                                                                          

                                                                               
                                               










                                                          
                                           



                                                   
 
                                                           
                                            




















                                                                                 
 

                                                                 












































































                                                                                                 

                                                                        
                                         
                                 


                                                                        
 

                                                                       




















                                                                    

                                                         









































                                                                                
 
                                                                                                


                                                                                         








                            
                    

                                      

                  

                                        



                                                                                         


                                                                                   
            
                                                            

                         
                                               


                       

                                                                    
                                       










                                                                                                                               
                                                    
















                                                                                            
                                                                                                            
                                                            











                                                                                                              
                                                                



                                              
                                                                                                             
                                            























                                                                                                                 
                                                                                                                
                                                      


                                                                                                 
                                                                                                        
                     
                                                                                              
                                            

                                                                                           


                                                                                           
                                                                        


                                                                                                           

                                                                                                                









                                           
                                                                                                                 


                          




                                                                                                      





                                                                                         
            




                                                                     

   
                                                                                                
                                                                        











                                                                                                           
 

                                                                                                    
                       
                                                                              
                     






                                                                                            

   
                                                                                       
                                                    


                                                             

                                                                                          

   









                                                                                   
                                                                                      
                                                       



                                                                                                        
                                                                          


                           
















                                                                                                      











                                                                                                 
 
package dotty.tools
package dotc
package reporting
package diagnostic

import dotc.core._
import Contexts.Context
import Decorators._
import Symbols._
import Names._
import NameOps._
import Types._
import util.SourcePosition
import config.Settings.Setting
import interfaces.Diagnostic.{ERROR, INFO, WARNING}
import dotc.parsing.Scanners.Token
import dotc.parsing.Tokens
import printing.Highlighting._
import printing.Formatting
import ErrorMessageID._
import dotty.tools.dotc.core.SymDenotations.SymDenotation

object messages {

  // `MessageContainer`s to be consumed by `Reporter` ---------------------- //
  class Error(
    msgFn: => Message,
    pos: SourcePosition
  ) extends MessageContainer(msgFn, pos, ERROR)

  class Warning(
    msgFn: => Message,
    pos: SourcePosition
  ) extends MessageContainer(msgFn, pos, WARNING)

  class Info(
    msgFn: => Message,
    pos: SourcePosition
  ) extends MessageContainer(msgFn, pos, INFO)

  abstract class ConditionalWarning(
    msgFn: => Message,
    pos: SourcePosition
  ) extends Warning(msgFn, pos) {
    def enablingOption(implicit ctx: Context): Setting[Boolean]
  }

  class FeatureWarning(
    msgFn: => Message,
    pos: SourcePosition
  ) extends ConditionalWarning(msgFn, pos) {
    def enablingOption(implicit ctx: Context) = ctx.settings.feature
  }

  class UncheckedWarning(
    msgFn: => Message,
    pos: SourcePosition
  ) extends ConditionalWarning(msgFn, pos) {
    def enablingOption(implicit ctx: Context) = ctx.settings.unchecked
  }

  class DeprecationWarning(
    msgFn: => Message,
    pos: SourcePosition
  ) extends ConditionalWarning(msgFn, pos) {
    def enablingOption(implicit ctx: Context) = ctx.settings.deprecation
  }

  class MigrationWarning(
    msgFn: => Message,
    pos: SourcePosition
  ) extends ConditionalWarning(msgFn, pos) {
    def enablingOption(implicit ctx: Context) = ctx.settings.migration
  }

  /**  Messages
    *  ========
    *  The role of messages is to provide the necessary details for a simple to
    *  understand diagnostic event. Each message can be turned into a message
    *  container (one of the above) by calling the appropriate method on them.
    *  For instance:
    *
    *  ```scala
    *  EmptyCatchBlock(tree).error(pos)   // res: Error
    *  EmptyCatchBlock(tree).warning(pos) // res: Warning
    *  ```
    */
  import ast.Trees._
  import ast.untpd
  import ast.tpd

  /** Helper methods for messages */
  def implicitClassRestrictionsText(implicit ctx: Context) =
    hl"""|${NoColor("For a full list of restrictions on implicit classes visit")}
         |${Blue("http://docs.scala-lang.org/overviews/core/implicit-classes.html")}"""


  // Syntax Errors ---------------------------------------------------------- //
  abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(implicit ctx: Context)
  extends Message(EmptyCatchOrFinallyBlockID) {
    val explanation = {
      val tryString = tryBody match {
        case Block(Nil, untpd.EmptyTree) => "{}"
        case _ => tryBody.show
      }

      val code1 =
        s"""|import scala.util.control.NonFatal
            |
            |try $tryString catch {
            |  case NonFatal(e) => ???
            |}""".stripMargin

      val code2 =
        s"""|try $tryString finally {
            |  // perform your cleanup here!
            |}""".stripMargin

      hl"""|A ${"try"} expression should be followed by some mechanism to handle any exceptions
           |thrown. Typically a ${"catch"} expression follows the ${"try"} and pattern matches
           |on any expected exceptions. For example:
           |
           |$code1
           |
           |It is also possible to follow a ${"try"} immediately by a ${"finally"} - letting the
           |exception propagate - but still allowing for some clean up in ${"finally"}:
           |
           |$code2
           |
           |It is recommended to use the ${"NonFatal"} extractor to catch all exceptions as it
           |correctly handles transfer functions like ${"return"}."""
    }
  }

  case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context)
 extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) {
    val kind = "Syntax"
    val msg =
      hl"""|The ${"catch"} block does not contain a valid expression, try
           |adding a case like - `${"case e: Exception =>"}` to the block"""
  }

  case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context)
  extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchAndFinallyBlockID) {
    val kind = "Syntax"
    val msg =
      hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting
           |its body in a block; no exceptions are handled."""
  }

  case class DeprecatedWithOperator()(implicit ctx: Context)
  extends Message(DeprecatedWithOperatorID) {
    val kind = "Syntax"
    val msg =
      hl"""${"with"} as a type operator has been deprecated; use `&' instead"""
    val explanation =
      hl"""|Dotty introduces intersection types - `&' types. These replace the
           |use of the ${"with"} keyword. There are a few differences in
           |semantics between intersection types and using `${"with"}'."""
  }

  case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context)
  extends Message(CaseClassMissingParamListID) {
    val kind = "Syntax"
    val msg =
      hl"""|A ${"case class"} must have at least one parameter list"""

    val explanation =
      hl"""|${cdef.name} must have at least one parameter list, if you would rather
           |have a singleton representation of ${cdef.name}, use a "${"case object"}".
           |Or, add an explicit `()' as a parameter list to ${cdef.name}."""
  }


  // Type Errors ------------------------------------------------------------ //
  case class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context)
  extends Message(DuplicateBindID) {
    val kind = "Naming"
    val msg = em"duplicate pattern variable: `${bind.name}`"

    val explanation = {
      val pat = tree.pat.show
      val guard = tree.guard match {
        case untpd.EmptyTree => ""
        case guard => s"if ${guard.show}"
      }

      val body = tree.body match {
        case Block(Nil, untpd.EmptyTree) => ""
        case body => s" ${body.show}"
      }

      val caseDef = s"case $pat$guard => $body"

      hl"""|For each ${"case"} bound variable names have to be unique. In:
           |
           |$caseDef
           |
           |`${bind.name}` is not unique. Rename one of the bound variables!"""
    }
  }

  case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context)
  extends Message(MissingIdentID) {
    val kind = "Unbound Identifier"
    val msg = em"not found: $treeKind$name"

    val explanation = {
      hl"""|The identifier for `$treeKind$name` is not bound, that is,
           |no declaration for this identifier can be found.
           |That can happen for instance if $name or its declaration has either been
           |misspelt, or if you're forgetting an import"""
    }
  }

  case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "", implicitFailure: String = "")(implicit ctx: Context)
  extends Message(TypeMismatchID) {
    val kind = "Type Mismatch"
    val msg = {
      val (where, printCtx) = Formatting.disambiguateTypes(found, expected)
      val (fnd, exp) = Formatting.typeDiff(found, expected)(printCtx)
      s"""|found:    $fnd
          |required: $exp
          |
          |$where""".stripMargin + whyNoMatch + implicitFailure
    }

    val explanation = ""
  }

  case class NotAMember(site: Type, name: Name, selected: String)(implicit ctx: Context)
  extends Message(NotAMemberID) {
    val kind = "Member Not Found"

    //println(i"site = $site, decls = ${site.decls}, source = ${site.widen.typeSymbol.sourceFile}") //DEBUG

    val msg = {
      import core.Flags._
      val maxDist = 3
      val decls = site.decls.flatMap { sym =>
        if (sym.flagsUNSAFE.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil
        else List((sym.name.show, sym))
      }

      // Calculate Levenshtein distance
      def distance(n1: Iterable[_], n2: Iterable[_]) =
        n1.foldLeft(List.range(0, n2.size)) { (prev, x) =>
          (prev zip prev.tail zip n2).scanLeft(prev.head + 1) {
            case (h, ((d, v), y)) => math.min(
              math.min(h + 1, v + 1),
              if (x == y) d else d + 1
            )
          }
        }.last

      // Count number of wrong characters
      def incorrectChars(x: (String, Int, Symbol)): (String, Symbol, Int) = {
        val (currName, _, sym) = x
        val matching = name.show.zip(currName).foldLeft(0) {
          case (acc, (x,y)) => if (x != y) acc + 1 else acc
        }
        (currName, sym, matching)
      }

      // Get closest match in `site`
      val closest =
        decls
        .map { case (n, sym) => (n, distance(n, name.show), sym) }
        .collect { case (n, dist, sym) if dist <= maxDist => (n, dist, sym) }
        .groupBy(_._2).toList
        .sortBy(_._1)
        .headOption.map(_._2).getOrElse(Nil)
        .map(incorrectChars).toList
        .sortBy(_._3)
        .take(1).map { case (n, sym, _) => (n, sym) }

      val siteName = site match {
        case site: NamedType => site.name.show
        case site => i"$site"
      }

      val closeMember = closest match {
        case (n, sym) :: Nil => hl""" - did you mean `${s"$siteName.$n"}`?"""
        case Nil => ""
        case _ => assert(
          false,
          "Could not single out one distinct member to match on input with"
        )
      }

      ex"$selected `$name` is not a member of $site$closeMember"
    }

    val explanation = ""
  }

  case class EarlyDefinitionsNotSupported()(implicit ctx: Context)
  extends Message(EarlyDefinitionsNotSupportedID) {
    val kind = "Syntax"
    val msg = "early definitions are not supported; use trait parameters instead"

    val explanation = {
      val code1 =
        """|trait Logging {
           |  val f: File
           |  f.open()
           |  onExit(f.close())
           |  def log(msg: String) = f.write(msg)
           |}
           |
           |class B extends Logging {
           |  val f = new File("log.data") // triggers a NullPointerException
           |}
           |
           |// early definition gets around the NullPointerException
           |class C extends {
           |  val f = new File("log.data")
           |} with Logging""".stripMargin

      val code2 =
        """|trait Logging(f: File) {
           |  f.open()
           |  onExit(f.close())
           |  def log(msg: String) = f.write(msg)
           |}
           |
           |class C extends Logging(new File("log.data"))""".stripMargin

      hl"""|Earlier versions of Scala did not support trait parameters and "early
           |definitions" (also known as "early initializers") were used as an alternative.
           |
           |Example of old syntax:
           |
           |$code1
           |
           |The above code can now be written as:
           |
           |$code2
           |"""
    }
  }

  case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context)
  extends Message(TopLevelImplicitClassID) {
    val kind = "Syntax"
    val msg = hl"""An ${"implicit class"} may not be top-level"""

    val explanation = {
      val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
      val exampleArgs =
        constr0.vparamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ")
      def defHasBody[T] = impl.body.exists(!_.isEmpty)
      val exampleBody = if (defHasBody) "{\n ...\n }" else ""
      hl"""|There may not be any method, member or object in scope with the same name as
           |the implicit class and a case class automatically gets a companion object with
           |the same name created by the compiler which would cause a naming conflict if it
           |were allowed.
           |
           |""" + implicitClassRestrictionsText + hl"""|
           |
           |To resolve the conflict declare ${cdef.name} inside of an ${"object"} then import the class
           |from the object at the use site if needed, for example:
           |
           |object Implicits {
           |  implicit class ${cdef.name}($exampleArgs)$exampleBody
           |}
           |
           |// At the use site:
           |import Implicits.${cdef.name}"""
    }
  }

  case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context)
  extends Message(ImplicitCaseClassID) {
    val kind = "Syntax"
    val msg = hl"""A ${"case class"} may not be defined as ${"implicit"}"""

    val explanation =
      hl"""|implicit classes may not be case classes. Instead use a plain class:
           |
           |implicit class ${cdef.name}...
           |
           |""" + implicitClassRestrictionsText
  }

  case class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(implicit ctx: Context)
  extends Message(ObjectMayNotHaveSelfTypeID) {
    val kind = "Syntax"
    val msg = hl"""${"object"}s must not have a self ${"type"}"""

    val explanation = {
      val untpd.ModuleDef(name, tmpl) = mdef
      val ValDef(_, selfTpt, _) = tmpl.self
      hl"""|${"object"}s must not have a self ${"type"}:
           |
           |Consider these alternative solutions:
           |  - Create a trait or a class instead of an object
           |  - Let the object extend a trait containing the self type:
           |
           |    object $name extends ${selfTpt.show}"""
    }
  }

  case class TupleTooLong(ts: List[untpd.Tree])(implicit ctx: Context)
  extends Message(TupleTooLongID) {
    import Definitions.MaxTupleArity
    val kind = "Syntax"
    val msg = hl"""A ${"tuple"} cannot have more than ${MaxTupleArity} members"""

    val explanation = {
      val members = ts.map(_.showSummary).grouped(MaxTupleArity)
      val nestedRepresentation = members.map(_.mkString(", ")).mkString(")(")
      hl"""|This restriction will be removed in the future.
           |Currently it is possible to use nested tuples when more than $MaxTupleArity are needed, for example:
           |
           |((${nestedRepresentation}))"""
    }
  }

  case class RepeatedModifier(modifier: String)(implicit ctx:Context)
  extends Message(RepeatedModifierID) {
    val kind = "Syntax"
    val msg = hl"""repeated modifier $modifier"""

    val explanation = {
      val code1 = hl"""private private val Origin = Point(0, 0)"""
      val code2 = hl"""private final val Origin = Point(0, 0)"""
      hl"""This happens when you accidentally specify the same modifier twice.
           |
           |Example:
           |
           |$code1
           |
           |instead of
           |
           |$code2
           |
           |"""
    }
  }

  case class InterpolatedStringError()(implicit ctx:Context)
  extends Message(InterpolatedStringErrorID) {
    val kind = "Syntax"
    val msg = "error in interpolated string: identifier or block expected"
    val explanation = {
      val code1 = "s\"$new Point(0, 0)\""
      val code2 = "s\"${new Point(0, 0)}\""
      hl"""|This usually happens when you forget to place your expressions inside curly braces.
           |
           |$code1
           |
           |should be written as
           |
           |$code2
           |"""
    }
  }

  case class UnboundPlaceholderParameter()(implicit ctx:Context)
  extends Message(UnboundPlaceholderParameterID) {
    val kind = "Syntax"
    val msg = "unbound placeholder parameter; incorrect use of `_`"
    val explanation =
      hl"""|The `_` placeholder syntax was used where it could not be bound.
           |Consider explicitly writing the variable binding.
           |
           |This can be done by replacing `_` with a variable (eg. `x`)
           |and adding ${"x =>"} where applicable.
           |
           |Example before:
           |
           |${"{ _ }"}
           |
           |Example after:
           |
           |${"x => { x }"}
           |
           |Another common occurrence for this error is defining a val with `_`:
           |
           |${"val a = _"}
           |
           |But this val definition isn't very useful, it can never be assigned
           |another value. And thus will always remain uninitialized.
           |Consider replacing the ${"val"} with ${"var"}:
           |
           |${"var a = _"}
           |
           |Note that this use of `_` is not placeholder syntax,
           |but an uninitialized var definition"""
  }

  case class IllegalStartSimpleExpr(illegalToken: String)(implicit ctx: Context)
  extends Message(IllegalStartSimpleExprID) {
    val kind = "Syntax"
    val msg = "illegal start of simple expression"
    val explanation = {
      hl"""|An expression yields a value. In the case of the simple expression, this error
           |commonly occurs when there's a missing parenthesis or brace. The reason being
           |that a simple expression is one of the following:
           |
           |- Block
           |- Expression in parenthesis
           |- Identifier
           |- Object creation
           |- Literal
           |
           |which cannot start with ${Red(illegalToken)}."""
    }
  }

  case class MissingReturnType()(implicit ctx:Context)
  extends Message(MissingReturnTypeID) {
    val kind = "Syntax"
    val msg = "missing return type"
    val explanation =
      hl"""|An abstract declaration must have a return type. For example:
           |
           |trait Shape {
           |  def area: Double // abstract declaration returning a ${"Double"}
           |}"""
  }

  case class YieldOrDoExpectedInForComprehension()(implicit ctx: Context)
  extends Message(YieldOrDoExpectedInForComprehensionID) {
    val kind = "Syntax"
    val msg = hl"${"yield"} or ${"do"} expected"

    val explanation =
      hl"""|When the enumerators in a for comprehension are not placed in parentheses or
           |braces, a ${"do"} or ${"yield"} statement is required after the enumerators
           |section of the comprehension.
           |
           |You can save some keystrokes by omitting the parentheses and writing
           |
           |${"val numbers = for i <- 1 to 3 yield i"}
           |
           |  instead of
           |
           |${"val numbers = for (i <- 1 to 3) yield i"}
           |
           |but the ${"yield"} keyword is still required.
           |
           |For comprehensions that simply perform a side effect without yielding anything
           |can also be written without parentheses but a ${"do"} keyword has to be
           |included. For example,
           |
           |${"for (i <- 1 to 3) println(i)"}
           |
           |can be written as
           |
           |${"for i <- 1 to 3 do println(i) // notice the 'do' keyword"}
           |
           |"""
  }

  case class ProperDefinitionNotFound()(implicit ctx: Context)
  extends Message(ProperDefinitionNotFoundID) {
    val kind = "Definition Not Found"
    val msg = hl"""Proper definition was not found in ${"@usecase"}"""

    val explanation = {
      val noUsecase =
        "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That"

      val usecase =
        """|/** Map from List[A] => List[B]
           |  *
           |  * @usecase def map[B](f: A => B): List[B]
           |  */
           |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That
           |""".stripMargin

      hl"""|Usecases are only supported for ${"def"}s. They exist because with Scala's
           |advanced type-system, we sometimes end up with seemingly scary signatures.
           |The usage of these methods, however, needs not be - for instance the `map`
           |function
           |
           |${"List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)"}
           |
           |is easy to understand and use - but has a rather bulky signature:
           |
           |$noUsecase
           |
           |to mitigate this and ease the usage of such functions we have the ${"@usecase"}
           |annotation for docstrings. Which can be used like this:
           |
           |$usecase
           |
           |When creating the docs, the signature of the method is substituted by the
           |usecase and the compiler makes sure that it is valid. Because of this, you're
           |only allowed to use ${"def"}s when defining usecases."""
    }
  }

  case class ByNameParameterNotSupported()(implicit ctx: Context)
  extends Message(ByNameParameterNotSupportedID) {
    val kind = "Syntax"
    val msg = "By-name parameter type not allowed here."

    val explanation =
      hl"""|By-name parameters act like functions that are only evaluated when referenced,
           |allowing for lazy evaluation of a parameter.
           |
           |An example of using a by-name parameter would look like:
           |${"def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function"}
           |
           |An example of the syntax of passing an actual function as a parameter:
           |${"def func(f: (Boolean => Boolean)) = f(true)"}
           |
           |or:
           |
           |${"def func(f: Boolean => Boolean) = f(true)"}
           |
           |And the usage could be as such:
           |${"func(bool => // do something...)"}
           |"""
  }

  case class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree])(implicit ctx: Context)
  extends Message(WrongNumberOfTypeArgsID) {
    val kind = "Syntax"

    private val expectedCount = expectedArgs.length
    private val actualCount = actual.length
    private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough"

    //TODO add def simpleParamName to ParamInfo
    private val expectedArgString = fntpe
      .widen.typeParams
      .map(_.paramName.unexpandedName.show)
      .mkString("[", ", ", "]")

    private val actualArgString = actual.map(_.show).mkString("[", ", ", "]")

    private val prettyName = fntpe.termSymbol match {
      case NoSymbol => fntpe.show
      case symbol   => symbol.showFullName
    }

    val msg =
      hl"""|${NoColor(msgPrefix)} type arguments for $prettyName$expectedArgString
           |expected: $expectedArgString
           |actual:   $actualArgString""".stripMargin

    val explanation = {
      val tooManyTypeParams =
        """|val tuple2: (Int, String) = (1, "one")
           |val list: List[(Int, String)] = List(tuple2)""".stripMargin

      if (actualCount > expectedCount)
        hl"""|You have supplied too many type parameters
             |
             |For example List takes a single type parameter (List[A])
             |If you need to hold more types in a list then you need to combine them
             |into another data type that can contain the number of types you need,
             |In this example one solution would be to use a Tuple:
             |
             |${tooManyTypeParams}"""
      else
        hl"""|You have not supplied enough type parameters
             |If you specify one type parameter then you need to specify every type parameter."""
    }
  }

  case class IllegalVariableInPatternAlternative()(implicit ctx: Context)
  extends Message(IllegalVariableInPatternAlternativeID) {
    val kind = "Syntax"
    val msg = "Variables are not allowed in alternative patterns"
    val explanation = {
      val varInAlternative =
        """|def g(pair: (Int,Int)): Int = pair match {
           |  case (1, n) | (n, 1) => n
           |  case _ => 0
           |}""".stripMargin

      val fixedVarInAlternative =
        """|def g(pair: (Int,Int)): Int = pair match {
           |  case (1, n) => n
           |  case (n, 1) => n
           |  case _ => 0
           |}""".stripMargin

      hl"""|Variables are not allowed within alternate pattern matches. You can workaround
           |this issue by adding additional cases for each alternative. For example, the
           |illegal function:
           |
           |$varInAlternative
           |could be implemented by moving each alternative into a separate case:
           |
           |$fixedVarInAlternative"""
    }
  }

  case class IdentifierExpected(identifier: String)(implicit ctx: Context)
  extends Message(IdentifierExpectedID) {
    val kind = "Syntax"
    val msg = "identifier expected"
    val explanation = {
      val wrongIdentifier = s"def foo: $identifier = {...}"
      val validIdentifier = s"def foo = {...}"
      hl"""|An identifier expected, but `$identifier` found. This could be because
           |`$identifier` is not a valid identifier. As a workaround, the compiler could
           |infer the type for you. For example, instead of:
           |
           |$wrongIdentifier
           |
           |Write your code like:
           |
           |$validIdentifier
           |
           |"""
    }
  }

  case class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context)
  extends Message(AuxConstructorNeedsNonImplicitParameterID) {
    val kind = "Syntax"
    val msg = "auxiliary constructor needs non-implicit parameter list"
    val explanation =
      hl"""|Only the primary constructor is allowed an ${"implicit"} parameter list;
           |auxiliary constructors need non-implicit parameter lists. When a primary
           |constructor has an implicit argslist, auxiliary constructors that call the
           |primary constructor must specify the implicit value.
           |
           |To resolve this issue check for:
           | - forgotten parenthesis on ${"this"} (${"def this() = { ... }"})
           | - auxiliary constructors specify the implicit value
           |"""
  }

  case class IncorrectRepeatedParameterSyntax()(implicit ctx: Context)
  extends Message(IncorrectRepeatedParameterSyntaxID) {
    val kind = "Syntax"
    val msg = "'*' expected"
    val explanation =
      hl"""|Expected * in '_*' operator.
           |
           |The '_*' operator can be used to supply a sequence-based argument
           |to a method with a variable-length or repeated parameter. It is used
           |to expand the sequence to a variable number of arguments, such that:
           |func(args: _*) would expand to func(arg1, arg2 ... argN).
           |
           |Below is an example of how a method with a variable-length
           |parameter can be declared and used.
           |
           |Squares the arguments of a variable-length parameter:
           |${"def square(args: Int*) = args.map(a => a * a)"}
           |
           |Usage:
           |${"square(1, 2, 3) // res0: List[Int] = List(1, 4, 9)"}
           |
           |Secondary Usage with '_*':
           |${"val ints = List(2, 3, 4)  // ints: List[Int] = List(2, 3, 4)"}
           |${"square(ints: _*)          // res1: List[Int] = List(4, 9, 16)"}
           |""".stripMargin
  }

  case class IllegalLiteral()(implicit ctx: Context)
  extends Message(IllegalLiteralID) {
    val kind = "Syntax"
    val msg = "illegal literal"
    val explanation =
      hl"""|Available literals can be divided into several groups:
           | - Integer literals: 0, 21, 0xFFFFFFFF, -42L
           | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1
           | - Boolean Literals: true, false
           | - Character Literals: 'a', '\u0041', '\n'
           | - String Literals: "Hello, World!"
           | - null
           |"""
  }

  case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context)
  extends Message(PatternMatchExhaustivityID) {
    val kind = "Pattern Match Exhaustivity"
    val msg =
      hl"""|match may not be exhaustive.
           |
           |It would fail on: $uncovered"""


    val explanation = ""
  }

  case class MatchCaseUnreachable()(implicit ctx: Context)
  extends Message(MatchCaseUnreachableID) {
    val kind = s"""Match ${hl"case"} Unreachable"""
    val msg = "unreachable code"
    val explanation = ""
  }

  case class SeqWildcardPatternPos()(implicit ctx: Context)
  extends Message(SeqWildcardPatternPosID) {
    val kind = "Syntax"
    val msg = "`_*' can be used only for last argument"
    val explanation = {
      val code =
        """def sumOfTheFirstTwo(list: List[Int]): Int = list match {
          |  case List(first, second, x:_*) => first + second
          |  case _ => 0
          |}"""
      hl"""|Sequence wildcard pattern is expected at the end of an argument list.
           |This pattern matches any remaining elements in a sequence.
           |Consider the following example:
           |
           |$code
           |
           |Calling:
           |
           |${"sumOfTheFirstTwo(List(1, 2, 10))"}
           |
           |would give 3 as a result"""
    }
  }

  case class IllegalStartOfSimplePattern()(implicit ctx: Context)
  extends Message(IllegalStartOfSimplePatternID) {
    val kind = "Syntax"
    val msg = "illegal start of simple pattern"
    val explanation = {
      val sipCode =
        """def f(x: Int, y: Int) = x match {
          |  case `y` => ...
          |}
        """
      val constructorPatternsCode =
        """case class Person(name: String, age: Int)
          |
          |def test(p: Person) = p match {
          |  case Person(name, age) => ...
          |}
        """
      val tupplePatternsCode =
        """def swap(tuple: (String, Int)): (Int, String) = tuple match {
          |  case (text, number) => (number, text)
          |}
        """
      val patternSequencesCode =
        """def getSecondValue(list: List[Int]): Int = list match {
          |  case List(_, second, x:_*) => second
          |  case _ => 0
          |}"""
      hl"""|Simple patterns can be divided into several groups:
           |- Variable Patterns: ${"case x => ..."}.
           |  It matches any value, and binds the variable name to that value.
           |  A special case is the wild-card pattern _ which is treated as if it was a fresh
           |  variable on each occurrence.
           |
           |- Typed Patterns: ${"case x: Int => ..."} or ${"case _: Int => ..."}.
           |  This pattern matches any value matched by the specified type; it binds the variable
           |  name to that value.
           |
           |- Literal Patterns: ${"case 123 => ..."} or ${"case 'A' => ..."}.
           |  This type of pattern matches any value that is equal to the specified literal.
           |
           |- Stable Identifier Patterns:
           |
           |  $sipCode
           |
           |  the match succeeds only if the x argument and the y argument of f are equal.
           |
           |- Constructor Patterns:
           |
           |  $constructorPatternsCode
           |
           |  The pattern binds all object's fields to the variable names (name and age, in this
           |  case).
           |
           |- Tuple Patterns:
           |
           |  $tupplePatternsCode
           |
           |  Calling:
           |
           |  ${"""swap(("Luftballons", 99)"""}
           |
           |  would give ${"""(99, "Luftballons")"""} as a result.
           |
           |- Pattern Sequences:
           |
           |  $patternSequencesCode
           |
           |  Calling:
           |
           |  ${"getSecondValue(List(1, 10, 2))"}
           |
           |  would give 10 as a result.
           |  This pattern is possible because a companion object for the List class has a method
           |  with the following signature:
           |
           |  ${"def unapplySeq[A](x: List[A]): Some[List[A]]"}
           |"""
    }
  }

  case class PkgDuplicateSymbol(existing: Symbol)(implicit ctx: Context)
  extends Message(PkgDuplicateSymbolID) {
    val kind = "Duplicate Symbol"
    val msg = hl"trying to define package with same name as `$existing`"
    val explanation = ""
  }

  case class ExistentialTypesNoLongerSupported()(implicit ctx: Context)
  extends Message(ExistentialTypesNoLongerSupportedID) {
    val kind = "Syntax"
    val msg =
      hl"""|Existential types are no longer supported -
           |use a wildcard or dependent type instead"""
    val explanation =
      hl"""|The use of existential types is no longer supported.
           |
           |You should use a wildcard or dependent type instead.
           |
           |For example:
           |
           |Instead of using ${"forSome"} to specify a type variable
           |
           |${"List[T forSome { type T }]"}
           |
           |Try using a wildcard type variable
           |
           |${"List[_]"}
           |"""
  }

  case class UnboundWildcardType()(implicit ctx: Context)
  extends Message(UnboundWildcardTypeID) {
    val kind = "Syntax"
    val msg = "Unbound wildcard type"
    val explanation =
      hl"""|The wildcard type syntax (`_`) was used where it could not be bound.
           |Replace `_` with a non-wildcard type. If the type doesn't matter,
           |try replacing `_` with ${"Any"}.
           |
           |Examples:
           |
           |- Parameter lists
           |
           |  Instead of:
           |    ${"def foo(x: _) = ..."}
           |
           |  Use ${"Any"} if the type doesn't matter:
           |    ${"def foo(x: Any) = ..."}
           |
           |- Type arguments
           |
           |  Instead of:
           |    ${"val foo = List[_](1, 2)"}
           |
           |  Use:
           |    ${"val foo = List[Int](1, 2)"}
           |
           |- Type bounds
           |
           |  Instead of:
           |    ${"def foo[T <: _](x: T) = ..."}
           |
           |  Remove the bounds if the type doesn't matter:
           |    ${"def foo[T](x: T) = ..."}
           |
           |- ${"val"} and ${"def"} types
           |
           |  Instead of:
           |    ${"val foo: _ = 3"}
           |
           |  Use:
           |    ${"val foo: Int = 3"}
           |"""
  }

  case class DanglingThisInPath()(implicit ctx: Context) extends Message(DanglingThisInPathID) {
    val kind = "Syntax"
    val msg = hl"""Expected an additional member selection after the keyword ${"this"}"""

    val contextCode =
      """  trait Outer {
        |    val member: Int
        |    type Member
        |    trait Inner {
        |      ...
        |    }
        |  }"""

    val importCode =
      """  import Outer.this.member
        |  //               ^^^^^^^"""

    val typeCode =
      """  type T = Outer.this.Member
        |  //                 ^^^^^^^"""

    val explanation =
      hl"""|Paths of imports and type selections must not end with the keyword ${"this"}.
           |
           |Maybe you forgot to select a member of ${"this"}? As an example, in the
           |following context:
           |${contextCode}
           |
           |- this is a valid import expression using a path
           |${importCode}
           |
           |- this is a valid type using a path
           |${typeCode}
           |"""
  }

  case class OverridesNothing(member: Symbol)(implicit ctx: Context)
  extends Message(OverridesNothingID) {
    val kind = "Reference"
    val msg = hl"""${member} overrides nothing"""

    val explanation =
      hl"""|There must be a field or method with the name `${member.name}` in a super
           |class of `${member.owner}` to override it. Did you misspell it?
           |Are you extending the right classes?
           |"""
  }

  case class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(implicit ctx: Context)
  extends Message(OverridesNothingButNameExistsID) {
    val kind = "Reference"
    val msg = hl"""${member} has a different signature than the overridden declaration"""

    val existingDecl = existing.map(_.showDcl).mkString("  \n")

    val explanation =
      hl"""|There must be a non-final field or method with the name `${member.name}` and the
           |same parameter list in a super class of `${member.owner}` to override it.
           |
           |  ${member.showDcl}
           |
           |The super classes of `${member.owner}` contain the following members
           |named `${member.name}`:
           |  ${existingDecl}
           |"""
  }

  case class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(implicit ctx: Context)
  extends Message(ForwardReferenceExtendsOverDefinitionID) {
    val kind = "Reference"
    val msg = hl"`${definition.name}` is a forward reference extending over the definition of `${value.name}`"

    val explanation =
      hl"""|`${definition.name}` is used before you define it, and the definition of `${value.name}`
           |appears between that use and the definition of `${definition.name}`.
           |
           |Forward references are allowed only, if there are no value definitions between
           |the reference and the referred method definition.
           |
           |Define `${definition.name}` before it is used,
           |or move the definition of `${value.name}` so it does not appear between
           |the declaration of `${definition.name}` and its use,
           |or define `${value.name}` as lazy.
           |""".stripMargin
  }

  case class ExpectedTokenButFound(expected: Token, found: Token, foundName: TermName)(implicit ctx: Context)
  extends Message(ExpectedTokenButFoundID) {
    val kind = "Syntax"

    private val expectedText =
      if (Tokens.isIdentifier(expected)) "an identifier"
      else Tokens.showToken(expected)

    private val foundText =
      if (foundName != null) hl"`${foundName.show}`"
      else Tokens.showToken(found)

    val msg = hl"""${expectedText} expected, but ${foundText} found"""

    private val ifKeyword =
      if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found))
        s"""
           |If you necessarily want to use $foundText as identifier, you may put it in backticks.""".stripMargin
      else
        ""

    val explanation =
      s"""|The text ${foundText} may not occur at that position, the compiler expected ${expectedText}.$ifKeyword
          |""".stripMargin
  }

  case class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(implicit ctx: Context)
  extends Message(MixedLeftAndRightAssociativeOpsID) {
    val kind = "Syntax"
    val op1Asso = if (op2LeftAssoc) "which is right-associative" else "which is left-associative"
    val op2Asso = if (op2LeftAssoc) "which is left-associative" else "which is right-associative"
    val msg = s"`${op1}` (${op1Asso}) and `${op2}` ($op2Asso) have same precedence and may not be mixed"
    val explanation =
      s"""|The operators ${op1} and ${op2} are used as infix operators in the same expression,
          |but they bind to different sides:
          |${op1} is applied to the operand to its ${if (op2LeftAssoc) "right" else "left"}
          |${op2} is applied to the operand to its ${if (op2LeftAssoc) "left" else "right"}
          |As both have the same precedence the compiler can't decide which to apply first.
          |
          |You may use parenthesis to make the application order explicit,
          |or use method application syntax `operand1.${op1}(operand2)`.
          |
          |Operators ending in a colon `:` are right-associative. All other operators are left-associative.
          |
          |Infix operator precedence is determined by the operator's first character. Characters are listed
          |below in increasing order of precedence, with characters on the same line having the same precedence.
          |  (all letters)
          |  |
          |  ^
          |  &
          |  = !
          |  < >
          |  :
          |  + -
          |  * / %
          |  (all other special characters)
          |Operators starting with a letter have lowest precedence, followed by operators starting with `|`, etc.
          |""".stripMargin
  }

  case class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(implicit ctx: Context)
  extends Message(CantInstantiateAbstractClassOrTraitID) {
    val kind = "Usage"
    private val traitOrAbstract = if (isTrait) hl"a trait" else hl"abstract"
    val msg = hl"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated"""
    val explanation =
      hl"""|Abstract classes and traits need to be extended by a concrete class or object
           |to make their functionality accessible.
           |
           |You may want to create an anonymous class extending ${cls.name} with
           |  ${s"class ${cls.name} { }"}
           |
           |or add a companion object with
           |  ${s"object ${cls.name} extends ${cls.name}"}
           |
           |You need to implement any abstract members in both cases.
           |""".stripMargin
  }

  case class AnnotatedPrimaryConstructorRequiresModifierOrThis(cls: Name)(implicit ctx: Context)
  extends Message(AnnotatedPrimaryConstructorRequiresModifierOrThisID) {
    val kind = "Syntax"
    val msg = hl"""${"private"}, ${"protected"}, or ${"this"} expected for annotated primary constructor"""
    val explanation =
      hl"""|When using annotations with a primary constructor of a class,
           |the annotation must be followed by an access modifier
           |(${"private"} or ${"protected"}) or ${"this"}.
           |
           |For example:
           |  ${"class Sample @deprecated this(param: Parameter) { ..."}
           |                           ^^^^
           |""".stripMargin
  }

  case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
  extends Message(OverloadedOrRecursiveMethodNeedsResultTypeID) {
    val kind = "Syntax"
    val msg = hl"""overloaded or recursive method ${tree} needs return type"""
    val explanation =
      hl"""Case 1: ${tree} is overloaded
          |If there are multiple methods named `${tree.name}` and at least one definition of
          |it calls another, you need to specify the calling method's return type.
          |
          |Case 2: ${tree} is recursive
          |If `${tree.name}` calls itself on any path, you need to specify its return type.
          |""".stripMargin
  }

  case class RecursiveValueNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
  extends Message(RecursiveValueNeedsResultTypeID) {
    val kind = "Syntax"
    val msg = hl"""recursive value ${tree.name} needs type"""
    val explanation =
      hl"""The definition of `${tree.name}` is recursive and you need to specify its type.
          |""".stripMargin
  }

  case class CyclicReferenceInvolving(denot: SymDenotation)(implicit ctx: Context)
  extends Message(CyclicReferenceInvolvingID) {
    val kind = "Syntax"
    val msg = hl"""cyclic reference involving $denot"""
    val explanation =
      hl"""|$denot is declared as part of a cycle which makes it impossible for the
           |compiler to decide upon ${denot.name}'s type.
           |""".stripMargin
  }

  case class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(implicit ctx: Context)
  extends Message(CyclicReferenceInvolvingImplicitID) {
    val kind = "Syntax"
    val msg = hl"""cyclic reference involving implicit $cycleSym"""
    val explanation =
      hl"""|This happens when the right hand-side of $cycleSym's definition involves an implicit search.
           |To avoid this error, give `${cycleSym.name}` an explicit type.
           |""".stripMargin
  }

  case class SuperQualMustBeParent(qual: untpd.Ident, cls: Symbols.ClassSymbol)(implicit ctx: Context)
  extends Message(SuperQualMustBeParentID) {

    val msg = hl"""|$qual does not name a parent of $cls"""

    val kind = "Reference"

    private val parents: Seq[String] = (cls.info.parents map (_.name.show)).sorted

    val explanation =
      hl"""|When a qualifier ${"T"} is used in a ${"super"} prefix of the form ${"C.super[T]"},
           |${"T"} must be a parent type of ${"C"}.
           |
           |In this case, the parents of $cls are:
           |${parents.mkString("  - ", "\n  - ", "")}
           |""".stripMargin
  }

  case class VarArgsParamMustComeLast()(implicit ctx: Context)
    extends Message(IncorrectRepeatedParameterSyntaxID) {
    override def msg: String = "varargs parameter must come last"

    override def kind: String = "Syntax"

    override def explanation: String =
      hl"""|The varargs field must be the last field in the method signature.
           |Attempting to define a field in a method signature after a varargs field is an error.
           |""".stripMargin
  }
}