1{-# OPTIONS_GHC -Wall #-} 2{-# LANGUAGE OverloadedStrings #-} 3module Reporting.Error.Type 4 ( Error(..) 5 -- expectations 6 , Expected(..) 7 , Context(..) 8 , SubContext(..) 9 , MaybeName(..) 10 , Category(..) 11 , PExpected(..) 12 , PContext(..) 13 , PCategory(..) 14 , typeReplace 15 , ptypeReplace 16 -- make reports 17 , toReport 18 ) 19 where 20 21 22import Prelude hiding (round) 23import qualified Data.Map as Map 24import Data.Monoid ((<>)) 25import qualified Data.Name as Name 26 27import qualified AST.Canonical as Can 28import qualified Data.Index as Index 29import qualified Reporting.Annotation as A 30import qualified Reporting.Doc as D 31import qualified Reporting.Render.Code as Code 32import qualified Reporting.Render.Type as RT 33import qualified Reporting.Render.Type.Localizer as L 34import qualified Reporting.Report as Report 35import qualified Reporting.Suggest as Suggest 36import qualified Type.Error as T 37 38 39 40-- ERRORS 41 42 43data Error 44 = BadExpr A.Region Category T.Type (Expected T.Type) 45 | BadPattern A.Region PCategory T.Type (PExpected T.Type) 46 | InfiniteType A.Region Name.Name T.Type 47 48 49 50-- EXPRESSION EXPECTATIONS 51 52 53data Expected tipe 54 = NoExpectation tipe 55 | FromContext A.Region Context tipe 56 | FromAnnotation Name.Name Int SubContext tipe 57 58 59data Context 60 = ListEntry Index.ZeroBased 61 | Negate 62 | OpLeft Name.Name 63 | OpRight Name.Name 64 | IfCondition 65 | IfBranch Index.ZeroBased 66 | CaseBranch Index.ZeroBased 67 | CallArity MaybeName Int 68 | CallArg MaybeName Index.ZeroBased 69 | RecordAccess A.Region (Maybe Name.Name) A.Region Name.Name 70 | RecordUpdateKeys Name.Name (Map.Map Name.Name Can.FieldUpdate) 71 | RecordUpdateValue Name.Name 72 | Destructure 73 74 75data SubContext 76 = TypedIfBranch Index.ZeroBased 77 | TypedCaseBranch Index.ZeroBased 78 | TypedBody 79 80 81data MaybeName 82 = FuncName Name.Name 83 | CtorName Name.Name 84 | OpName Name.Name 85 | NoName 86 87 88data Category 89 = List 90 | Number 91 | Float 92 | String 93 | Char 94 | If 95 | Case 96 | CallResult MaybeName 97 | Lambda 98 | Accessor Name.Name 99 | Access Name.Name 100 | Record 101 | Tuple 102 | Unit 103 | Shader 104 | Effects 105 | Local Name.Name 106 | Foreign Name.Name 107 108 109 110-- PATTERN EXPECTATIONS 111 112 113data PExpected tipe 114 = PNoExpectation tipe 115 | PFromContext A.Region PContext tipe 116 117 118data PContext 119 = PTypedArg Name.Name Index.ZeroBased 120 | PCaseMatch Index.ZeroBased 121 | PCtorArg Name.Name Index.ZeroBased 122 | PListEntry Index.ZeroBased 123 | PTail 124 125 126data PCategory 127 = PRecord 128 | PUnit 129 | PTuple 130 | PList 131 | PCtor Name.Name 132 | PInt 133 | PStr 134 | PChr 135 | PBool 136 137 138 139-- HELPERS 140 141 142typeReplace :: Expected a -> b -> Expected b 143typeReplace expectation tipe = 144 case expectation of 145 NoExpectation _ -> 146 NoExpectation tipe 147 148 FromContext region context _ -> 149 FromContext region context tipe 150 151 FromAnnotation name arity context _ -> 152 FromAnnotation name arity context tipe 153 154 155ptypeReplace :: PExpected a -> b -> PExpected b 156ptypeReplace expectation tipe = 157 case expectation of 158 PNoExpectation _ -> 159 PNoExpectation tipe 160 161 PFromContext region context _ -> 162 PFromContext region context tipe 163 164 165 166-- TO REPORT 167 168 169toReport :: Code.Source -> L.Localizer -> Error -> Report.Report 170toReport source localizer err = 171 case err of 172 BadExpr region category actualType expected -> 173 toExprReport source localizer region category actualType expected 174 175 BadPattern region category tipe expected -> 176 toPatternReport source localizer region category tipe expected 177 178 InfiniteType region name overallType -> 179 toInfiniteReport source localizer region name overallType 180 181 182 183-- TO PATTERN REPORT 184 185 186toPatternReport :: Code.Source -> L.Localizer -> A.Region -> PCategory -> T.Type -> PExpected T.Type -> Report.Report 187toPatternReport source localizer patternRegion category tipe expected = 188 Report.Report "TYPE MISMATCH" patternRegion [] $ 189 case expected of 190 PNoExpectation expectedType -> 191 Code.toSnippet source patternRegion Nothing $ 192 ( "This pattern is being used in an unexpected way:" 193 , patternTypeComparison localizer tipe expectedType 194 (addPatternCategory "It is" category) 195 "But it needs to match:" 196 [] 197 ) 198 199 PFromContext region context expectedType -> 200 Code.toSnippet source region (Just patternRegion) $ 201 case context of 202 PTypedArg name index -> 203 ( D.reflow $ 204 "The " <> D.ordinal index <> " argument to `" <> Name.toChars name <> "` is weird." 205 , patternTypeComparison localizer tipe expectedType 206 (addPatternCategory "The argument is a pattern that matches" category) 207 ( "But the type annotation on `" <> Name.toChars name 208 <> "` says the " <> D.ordinal index <> " argument should be:" 209 ) 210 [] 211 ) 212 213 PCaseMatch index -> 214 if index == Index.first then 215 ( 216 D.reflow $ 217 "The 1st pattern in this `case` causing a mismatch:" 218 , 219 patternTypeComparison localizer tipe expectedType 220 (addPatternCategory "The first pattern is trying to match" category) 221 "But the expression between `case` and `of` is:" 222 [ D.reflow $ 223 "These can never match! Is the pattern the problem? Or is it the expression?" 224 ] 225 ) 226 else 227 ( D.reflow $ 228 "The " <> D.ordinal index <> " pattern in this `case` does not match the previous ones." 229 , patternTypeComparison localizer tipe expectedType 230 (addPatternCategory ("The " <> D.ordinal index <> " pattern is trying to match") category) 231 "But all the previous patterns match:" 232 [ D.link "Note" 233 "A `case` expression can only handle one type of value, so you may want to use" 234 "custom-types" 235 "to handle “mixing” types." 236 ] 237 ) 238 239 PCtorArg name index -> 240 ( D.reflow $ 241 "The " <> D.ordinal index <> " argument to `" <> Name.toChars name <> "` is weird." 242 , patternTypeComparison localizer tipe expectedType 243 (addPatternCategory "It is trying to match" category) 244 ( "But `" <> Name.toChars name <> "` needs its " 245 <> D.ordinal index <> " argument to be:" 246 ) 247 [] 248 ) 249 250 PListEntry index -> 251 ( D.reflow $ 252 "The " <> D.ordinal index <> " pattern in this list does not match all the previous ones:" 253 , patternTypeComparison localizer tipe expectedType 254 (addPatternCategory ("The " <> D.ordinal index <> " pattern is trying to match") category) 255 "But all the previous patterns in the list are:" 256 [ D.link "Hint" 257 "Everything in a list must be the same type of value. This way, we never\ 258 \ run into unexpected values partway through a List.map, List.foldl, etc. Read" 259 "custom-types" 260 "to learn how to “mix” types." 261 ] 262 ) 263 264 PTail -> 265 ( D.reflow $ 266 "The pattern after (::) is causing issues." 267 , patternTypeComparison localizer tipe expectedType 268 (addPatternCategory "The pattern after (::) is trying to match" category) 269 "But it needs to match lists like this:" 270 [] 271 ) 272 273 274 275-- PATTERN HELPERS 276 277 278patternTypeComparison :: L.Localizer -> T.Type -> T.Type -> String -> String -> [D.Doc] -> D.Doc 279patternTypeComparison localizer actual expected iAmSeeing insteadOf contextHints = 280 let 281 (actualDoc, expectedDoc, problems) = 282 T.toComparison localizer actual expected 283 in 284 D.stack $ 285 [ D.reflow iAmSeeing 286 , D.indent 4 actualDoc 287 , D.reflow insteadOf 288 , D.indent 4 expectedDoc 289 ] 290 ++ problemsToHint problems 291 ++ contextHints 292 293 294addPatternCategory :: String -> PCategory -> String 295addPatternCategory iAmTryingToMatch category = 296 iAmTryingToMatch <> 297 case category of 298 PRecord -> " record values of type:" 299 PUnit -> " unit values:" 300 PTuple -> " tuples of type:" 301 PList -> " lists of type:" 302 PCtor name -> " `" <> Name.toChars name <> "` values of type:" 303 PInt -> " integers:" 304 PStr -> " strings:" 305 PChr -> " characters:" 306 PBool -> " booleans:" 307 308 309 310-- EXPR HELPERS 311 312 313typeComparison :: L.Localizer -> T.Type -> T.Type -> String -> String -> [D.Doc] -> D.Doc 314typeComparison localizer actual expected iAmSeeing insteadOf contextHints = 315 let 316 (actualDoc, expectedDoc, problems) = 317 T.toComparison localizer actual expected 318 in 319 D.stack $ 320 [ D.reflow iAmSeeing 321 , D.indent 4 actualDoc 322 , D.reflow insteadOf 323 , D.indent 4 expectedDoc 324 ] 325 ++ contextHints 326 ++ problemsToHint problems 327 328 329loneType :: L.Localizer -> T.Type -> T.Type -> D.Doc -> [D.Doc] -> D.Doc 330loneType localizer actual expected iAmSeeing furtherDetails = 331 let 332 (actualDoc, _, problems) = 333 T.toComparison localizer actual expected 334 in 335 D.stack $ 336 [ iAmSeeing 337 , D.indent 4 actualDoc 338 ] 339 ++ furtherDetails 340 ++ problemsToHint problems 341 342 343addCategory :: String -> Category -> String 344addCategory thisIs category = 345 case category of 346 Local name -> "This `" <> Name.toChars name <> "` value is a:" 347 Foreign name -> "This `" <> Name.toChars name <> "` value is a:" 348 Access field -> "The value at ." <> Name.toChars field <> " is a:" 349 Accessor field -> "This ." <> Name.toChars field <> " field access function has type:" 350 If -> "This `if` expression produces:" 351 Case -> "This `case` expression produces:" 352 List -> thisIs <> " a list of type:" 353 Number -> thisIs <> " a number of type:" 354 Float -> thisIs <> " a float of type:" 355 String -> thisIs <> " a string of type:" 356 Char -> thisIs <> " a character of type:" 357 Lambda -> thisIs <> " an anonymous function of type:" 358 Record -> thisIs <> " a record of type:" 359 Tuple -> thisIs <> " a tuple of type:" 360 Unit -> thisIs <> " a unit value:" 361 Shader -> thisIs <> " a GLSL shader of type:" 362 Effects -> thisIs <> " a thing for CORE LIBRARIES ONLY." 363 CallResult maybeName -> 364 case maybeName of 365 NoName -> thisIs <> ":" 366 FuncName name -> "This `" <> Name.toChars name <> "` call produces:" 367 CtorName name -> "This `" <> Name.toChars name <> "` call produces:" 368 OpName _ -> thisIs <> ":" 369 370 371problemsToHint :: [T.Problem] -> [D.Doc] 372problemsToHint problems = 373 case problems of 374 [] -> 375 [] 376 377 problem : _ -> 378 problemToHint problem 379 380 381problemToHint :: T.Problem -> [D.Doc] 382problemToHint problem = 383 case problem of 384 T.IntFloat -> 385 [ D.fancyLink "Note" ["Read"] "implicit-casts" 386 ["to","learn","why","Elm","does","not","implicitly","convert" 387 ,"Ints","to","Floats.","Use",D.green "toFloat","and" 388 ,D.green "round","to","do","explicit","conversions." 389 ] 390 ] 391 392 T.StringFromInt -> 393 [ D.toFancyHint 394 ["Want","to","convert","an","Int","into","a","String?" 395 ,"Use","the",D.green "String.fromInt","function!" 396 ] 397 ] 398 399 T.StringFromFloat -> 400 [ D.toFancyHint 401 ["Want","to","convert","a","Float","into","a","String?" 402 ,"Use","the",D.green "String.fromFloat","function!" 403 ] 404 ] 405 406 T.StringToInt -> 407 [ D.toFancyHint 408 ["Want","to","convert","a","String","into","an","Int?" 409 ,"Use","the",D.green "String.toInt","function!" 410 ] 411 ] 412 413 T.StringToFloat -> 414 [ D.toFancyHint 415 ["Want","to","convert","a","String","into","a","Float?" 416 ,"Use","the",D.green "String.toFloat","function!" 417 ] 418 ] 419 420 T.AnythingToBool -> 421 [ D.toSimpleHint $ 422 "Elm does not have “truthiness” such that ints and strings and lists\ 423 \ are automatically converted to booleans. Do that conversion explicitly!" 424 ] 425 426 T.AnythingFromMaybe -> 427 [ D.toFancyHint 428 ["Use",D.green "Maybe.withDefault","to","handle","possible","errors." 429 ,"Longer","term,","it","is","usually","better","to","write","out","the" 430 ,"full","`case`","though!" 431 ] 432 ] 433 434 T.ArityMismatch x y -> 435 [ D.toSimpleHint $ 436 if x < y then 437 "It looks like it takes too few arguments. I was expecting " ++ show (y - x) ++ " more." 438 else 439 "It looks like it takes too many arguments. I see " ++ show (x - y) ++ " extra." 440 ] 441 442 T.BadFlexSuper direction super _ tipe -> 443 case tipe of 444 T.Lambda _ _ _ -> badFlexSuper direction super tipe 445 T.Infinite -> [] 446 T.Error -> [] 447 T.FlexVar _ -> [] 448 T.FlexSuper s _ -> badFlexFlexSuper super s 449 T.RigidVar y -> badRigidVar y (toASuperThing super) 450 T.RigidSuper s _ -> badRigidSuper s (toASuperThing super) 451 T.Type _ _ _ -> badFlexSuper direction super tipe 452 T.Record _ _ -> badFlexSuper direction super tipe 453 T.Unit -> badFlexSuper direction super tipe 454 T.Tuple _ _ _ -> badFlexSuper direction super tipe 455 T.Alias _ _ _ _ -> badFlexSuper direction super tipe 456 457 T.BadRigidVar x tipe -> 458 case tipe of 459 T.Lambda _ _ _ -> badRigidVar x "a function" 460 T.Infinite -> [] 461 T.Error -> [] 462 T.FlexVar _ -> [] 463 T.FlexSuper s _ -> badRigidVar x (toASuperThing s) 464 T.RigidVar y -> badDoubleRigid x y 465 T.RigidSuper _ y -> badDoubleRigid x y 466 T.Type _ n _ -> badRigidVar x ("a `" ++ Name.toChars n ++ "` value") 467 T.Record _ _ -> badRigidVar x "a record" 468 T.Unit -> badRigidVar x "a unit value" 469 T.Tuple _ _ _ -> badRigidVar x "a tuple" 470 T.Alias _ n _ _ -> badRigidVar x ("a `" ++ Name.toChars n ++ "` value") 471 472 T.BadRigidSuper super x tipe -> 473 case tipe of 474 T.Lambda _ _ _ -> badRigidSuper super "a function" 475 T.Infinite -> [] 476 T.Error -> [] 477 T.FlexVar _ -> [] 478 T.FlexSuper s _ -> badRigidSuper super (toASuperThing s) 479 T.RigidVar y -> badDoubleRigid x y 480 T.RigidSuper _ y -> badDoubleRigid x y 481 T.Type _ n _ -> badRigidSuper super ("a `" ++ Name.toChars n ++ "` value") 482 T.Record _ _ -> badRigidSuper super "a record" 483 T.Unit -> badRigidSuper super "a unit value" 484 T.Tuple _ _ _ -> badRigidSuper super "a tuple" 485 T.Alias _ n _ _ -> badRigidSuper super ("a `" ++ Name.toChars n ++ "` value") 486 487 T.FieldsMissing fields -> 488 case map (D.green . D.fromName) fields of 489 [] -> 490 [] 491 492 [f1] -> 493 [ D.toFancyHint ["Looks","like","the",f1,"field","is","missing."] 494 ] 495 496 fieldDocs -> 497 [ D.toFancyHint $ 498 ["Looks","like","fields"] ++ D.commaSep "and" id fieldDocs ++ ["are","missing."] 499 ] 500 501 502 T.FieldTypo typo possibilities -> 503 case Suggest.sort (Name.toChars typo) Name.toChars possibilities of 504 [] -> 505 [] 506 507 nearest:_ -> 508 [ D.toFancyHint $ 509 ["Seems","like","a","record","field","typo.","Maybe" 510 ,D.dullyellow (D.fromName typo),"should","be" 511 ,D.green (D.fromName nearest) <> "?" 512 ] 513 , D.toSimpleHint 514 "Can more type annotations be added? Type annotations always help me give\ 515 \ more specific messages, and I think they could help a lot in this case!" 516 ] 517 518 519 520-- BAD RIGID HINTS 521 522 523badRigidVar :: Name.Name -> String -> [D.Doc] 524badRigidVar name aThing = 525 [ D.toSimpleHint $ 526 "Your type annotation uses type variable `" ++ Name.toChars name ++ 527 "` which means ANY type of value can flow through, but your code is saying it specifically wants " 528 ++ aThing ++ ". Maybe change your type annotation to\ 529 \ be more specific? Maybe change the code to be more general?" 530 , D.reflowLink "Read" "type-annotations" "for more advice!" 531 ] 532 533 534badDoubleRigid :: Name.Name -> Name.Name -> [D.Doc] 535badDoubleRigid x y = 536 [ D.toSimpleHint $ 537 "Your type annotation uses `" ++ Name.toChars x ++ "` and `" ++ Name.toChars y ++ 538 "` as separate type variables. Your code seems to be saying they are the\ 539 \ same though. Maybe they should be the same in your type annotation?\ 540 \ Maybe your code uses them in a weird way?" 541 , D.reflowLink "Read" "type-annotations" "for more advice!" 542 ] 543 544 545toASuperThing :: T.Super -> String 546toASuperThing super = 547 case super of 548 T.Number -> "a `number` value" 549 T.Comparable -> "a `comparable` value" 550 T.CompAppend -> "a `compappend` value" 551 T.Appendable -> "an `appendable` value" 552 553 554 555-- BAD SUPER HINTS 556 557 558badFlexSuper :: T.Direction -> T.Super -> T.Type -> [D.Doc] 559badFlexSuper direction super tipe = 560 case super of 561 T.Comparable -> 562 case tipe of 563 T.Record _ _ -> 564 [ D.link "Hint" 565 "I do not know how to compare records. I can only compare ints, floats,\ 566 \ chars, strings, lists of comparable values, and tuples of comparable values.\ 567 \ Check out" "comparing-records" "for ideas on how to proceed." 568 ] 569 570 T.Type _ name _ -> 571 [ D.toSimpleHint $ 572 "I do not know how to compare `" ++ Name.toChars name ++ "` values. I can only\ 573 \ compare ints, floats, chars, strings, lists of comparable values, and tuples\ 574 \ of comparable values." 575 , D.reflowLink 576 "Check out" "comparing-custom-types" "for ideas on how to proceed." 577 ] 578 579 _ -> 580 [ D.toSimpleHint $ 581 "I only know how to compare ints, floats, chars, strings, lists of\ 582 \ comparable values, and tuples of comparable values." 583 ] 584 585 T.Appendable -> 586 [ D.toSimpleHint "I only know how to append strings and lists." 587 ] 588 589 T.CompAppend -> 590 [ D.toSimpleHint "Only strings and lists are both comparable and appendable." 591 ] 592 593 T.Number -> 594 case tipe of 595 T.Type home name _ | T.isString home name -> 596 case direction of 597 T.Have -> 598 [ D.toFancyHint ["Try","using",D.green "String.fromInt","to","convert","it","to","a","string?"] 599 ] 600 601 T.Need -> 602 [ D.toFancyHint ["Try","using",D.green "String.toInt","to","convert","it","to","an","integer?"] 603 ] 604 605 _ -> 606 [ D.toFancyHint ["Only",D.green "Int","and",D.green "Float","values","work","as","numbers."] 607 ] 608 609 610badRigidSuper :: T.Super -> String -> [D.Doc] 611badRigidSuper super aThing = 612 let 613 (superType, manyThings) = 614 case super of 615 T.Number -> ("number", "ints AND floats") 616 T.Comparable -> ("comparable", "ints, floats, chars, strings, lists, and tuples") 617 T.Appendable -> ("appendable", "strings AND lists") 618 T.CompAppend -> ("compappend", "strings AND lists") 619 in 620 [ D.toSimpleHint $ 621 "The `" ++ superType ++ "` in your type annotation is saying that " 622 ++ manyThings ++ " can flow through, but your code is saying it specifically wants " 623 ++ aThing ++ ". Maybe change your type annotation to\ 624 \ be more specific? Maybe change the code to be more general?" 625 , D.reflowLink "Read" "type-annotations" "for more advice!" 626 ] 627 628 629badFlexFlexSuper :: T.Super -> T.Super -> [D.Doc] 630badFlexFlexSuper s1 s2 = 631 let 632 likeThis super = 633 case super of 634 T.Number -> "a number" 635 T.Comparable -> "comparable" 636 T.CompAppend -> "a compappend" 637 T.Appendable -> "appendable" 638 in 639 [ D.toSimpleHint $ 640 "There are no values in Elm that are both " 641 ++ likeThis s1 ++ " and " ++ likeThis s2 ++ "." 642 ] 643 644 645 646-- TO EXPR REPORT 647 648 649toExprReport :: Code.Source -> L.Localizer -> A.Region -> Category -> T.Type -> Expected T.Type -> Report.Report 650toExprReport source localizer exprRegion category tipe expected = 651 case expected of 652 NoExpectation expectedType -> 653 Report.Report "TYPE MISMATCH" exprRegion [] $ 654 Code.toSnippet source exprRegion Nothing 655 ( "This expression is being used in an unexpected way:" 656 , typeComparison localizer tipe expectedType 657 (addCategory "It is" category) 658 "But you are trying to use it as:" 659 [] 660 ) 661 662 FromAnnotation name _arity subContext expectedType -> 663 let 664 thing = 665 case subContext of 666 TypedIfBranch index -> D.ordinal index <> " branch of this `if` expression:" 667 TypedCaseBranch index -> D.ordinal index <> " branch of this `case` expression:" 668 TypedBody -> "body of the `" <> Name.toChars name <> "` definition:" 669 670 itIs = 671 case subContext of 672 TypedIfBranch index -> "The " <> D.ordinal index <> " branch is" 673 TypedCaseBranch index -> "The " <> D.ordinal index <> " branch is" 674 TypedBody -> "The body is" 675 in 676 Report.Report "TYPE MISMATCH" exprRegion [] $ 677 Code.toSnippet source exprRegion Nothing $ 678 ( D.reflow ("Something is off with the " <> thing) 679 , typeComparison localizer tipe expectedType 680 (addCategory itIs category) 681 ("But the type annotation on `" <> Name.toChars name <> "` says it should be:") 682 [] 683 ) 684 685 FromContext region context expectedType -> 686 let 687 mismatch (maybeHighlight, problem, thisIs, insteadOf, furtherDetails) = 688 Report.Report "TYPE MISMATCH" exprRegion [] $ 689 Code.toSnippet source region maybeHighlight 690 ( D.reflow problem 691 , typeComparison localizer tipe expectedType (addCategory thisIs category) insteadOf furtherDetails 692 ) 693 694 badType (maybeHighlight, problem, thisIs, furtherDetails) = 695 Report.Report "TYPE MISMATCH" exprRegion [] $ 696 Code.toSnippet source region maybeHighlight 697 ( D.reflow problem 698 , loneType localizer tipe expectedType (D.reflow (addCategory thisIs category)) furtherDetails 699 ) 700 701 custom maybeHighlight docPair = 702 Report.Report "TYPE MISMATCH" exprRegion [] $ 703 Code.toSnippet source region maybeHighlight docPair 704 in 705 case context of 706 ListEntry index -> 707 let ith = D.ordinal index in 708 mismatch 709 ( Just exprRegion 710 , "The " <> ith <> " element of this list does not match all the previous elements:" 711 , "The " <> ith <> " element is" 712 , "But all the previous elements in the list are:" 713 , [ D.link "Hint" 714 "Everything in a list must be the same type of value. This way, we never\ 715 \ run into unexpected values partway through a List.map, List.foldl, etc. Read" 716 "custom-types" 717 "to learn how to “mix” types." 718 ] 719 ) 720 721 Negate -> 722 badType 723 ( Just exprRegion 724 , "I do not know how to negate this type of value:" 725 , "It is" 726 , [ D.fillSep 727 ["But","I","only","now","how","to","negate" 728 ,D.dullyellow "Int","and",D.dullyellow "Float","values." 729 ] 730 ] 731 ) 732 733 OpLeft op -> 734 custom (Just exprRegion) $ 735 opLeftToDocs localizer category op tipe expectedType 736 737 OpRight op -> 738 case opRightToDocs localizer category op tipe expectedType of 739 EmphBoth details -> 740 custom Nothing details 741 742 EmphRight details -> 743 custom (Just exprRegion) details 744 745 IfCondition -> 746 badType 747 ( Just exprRegion 748 , "This `if` condition does not evaluate to a boolean value, True or False." 749 , "It is" 750 , [ D.fillSep ["But","I","need","this","`if`","condition","to","be","a",D.dullyellow "Bool","value."] 751 ] 752 ) 753 754 IfBranch index -> 755 let ith = D.ordinal index in 756 mismatch 757 ( Just exprRegion 758 , "The " <> ith <> " branch of this `if` does not match all the previous branches:" 759 , "The " <> ith <> " branch is" 760 , "But all the previous branches result in:" 761 , [ D.link "Hint" 762 "All branches in an `if` must produce the same type of values. This way, no\ 763 \ matter which branch we take, the result is always a consistent shape. Read" 764 "custom-types" 765 "to learn how to “mix” types." 766 ] 767 ) 768 769 CaseBranch index -> 770 let ith = D.ordinal index in 771 mismatch 772 ( Just exprRegion 773 , "The " <> ith <> " branch of this `case` does not match all the previous branches:" 774 , "The " <> ith <> " branch is" 775 , "But all the previous branches result in:" 776 , [ D.link "Hint" 777 "All branches in a `case` must produce the same type of values. This way, no\ 778 \ matter which branch we take, the result is always a consistent shape. Read" 779 "custom-types" 780 "to learn how to “mix” types." 781 ] 782 ) 783 784 CallArity maybeFuncName numGivenArgs -> 785 Report.Report "TOO MANY ARGS" exprRegion [] $ 786 Code.toSnippet source region (Just exprRegion) $ 787 case countArgs tipe of 788 0 -> 789 let 790 thisValue = 791 case maybeFuncName of 792 NoName -> "This value" 793 FuncName name -> "The `" <> Name.toChars name <> "` value" 794 CtorName name -> "The `" <> Name.toChars name <> "` value" 795 OpName op -> "The (" <> Name.toChars op <> ") operator" 796 in 797 ( D.reflow $ thisValue <> " is not a function, but it was given " <> D.args numGivenArgs <> "." 798 , D.reflow $ "Are there any missing commas? Or missing parentheses?" 799 ) 800 801 n -> 802 let 803 thisFunction = 804 case maybeFuncName of 805 NoName -> "This function" 806 FuncName name -> "The `" <> Name.toChars name <> "` function" 807 CtorName name -> "The `" <> Name.toChars name <> "` constructor" 808 OpName op -> "The (" <> Name.toChars op <> ") operator" 809 in 810 ( D.reflow $ thisFunction <> " expects " <> D.args n <> ", but it got " <> show numGivenArgs <> " instead." 811 , D.reflow $ "Are there any missing commas? Or missing parentheses?" 812 ) 813 814 CallArg maybeFuncName index -> 815 let 816 ith = D.ordinal index 817 818 thisFunction = 819 case maybeFuncName of 820 NoName -> "this function" 821 FuncName name -> "`" <> Name.toChars name <> "`" 822 CtorName name -> "`" <> Name.toChars name <> "`" 823 OpName op -> "(" <> Name.toChars op <> ")" 824 in 825 mismatch 826 ( Just exprRegion 827 , "The " <> ith <> " argument to " <> thisFunction <> " is not what I expect:" 828 , "This argument is" 829 , "But " <> thisFunction <> " needs the " <> ith <> " argument to be:" 830 , 831 if Index.toHuman index == 1 then 832 [] 833 else 834 [ D.toSimpleHint $ 835 "I always figure out the argument types from left to right. If an argument\ 836 \ is acceptable, I assume it is “correct” and move on. So the problem may\ 837 \ actually be in one of the previous arguments!" 838 ] 839 ) 840 841 RecordAccess recordRegion maybeName fieldRegion field -> 842 case T.iteratedDealias tipe of 843 T.Record fields ext -> 844 custom (Just fieldRegion) 845 ( D.reflow $ 846 "This " 847 <> maybe "" (\n -> "`" <> Name.toChars n <> "`") maybeName 848 <> " record does not have a `" <> Name.toChars field <> "` field:" 849 , case Suggest.sort (Name.toChars field) (Name.toChars . fst) (Map.toList fields) of 850 [] -> 851 D.reflow "In fact, it is a record with NO fields!" 852 853 f:fs -> 854 D.stack 855 [ D.reflow $ 856 "This is usually a typo. Here are the " 857 <> maybe "" (\n -> "`" <> Name.toChars n <> "`") maybeName 858 <> " fields that are most similar:" 859 , toNearbyRecord localizer f fs ext 860 , D.fillSep 861 ["So","maybe",D.dullyellow (D.fromName field) 862 ,"should","be",D.green (D.fromName (fst f)) <> "?" 863 ] 864 ] 865 ) 866 867 _ -> 868 badType 869 ( Just recordRegion 870 , "This is not a record, so it has no fields to access!" 871 , "It is" 872 , [ D.fillSep 873 ["But","I","need","a","record","with","a" 874 ,D.dullyellow (D.fromName field),"field!" 875 ] 876 ] 877 ) 878 879 RecordUpdateKeys record expectedFields -> 880 case T.iteratedDealias tipe of 881 T.Record actualFields ext -> 882 case Map.lookupMin (Map.difference expectedFields actualFields) of 883 Nothing -> 884 mismatch 885 ( Nothing 886 , "Something is off with this record update:" 887 , "The `" <> Name.toChars record <> "` record is" 888 , "But this update needs it to be compatable with:" 889 , [ D.reflow 890 "Do you mind creating an <http://sscce.org/> that produces this error message and\ 891 \ sharing it at <https://github.com/elm/error-message-catalog/issues> so we\ 892 \ can try to give better advice here?" 893 ] 894 ) 895 896 Just (field, Can.FieldUpdate fieldRegion _) -> 897 let 898 rStr = "`" <> Name.toChars record <> "`" 899 fStr = "`" <> Name.toChars field <> "`" 900 in 901 custom (Just fieldRegion) 902 ( D.reflow $ 903 "The " <> rStr <> " record does not have a " <> fStr <> " field:" 904 , case Suggest.sort (Name.toChars field) (Name.toChars . fst) (Map.toList actualFields) of 905 [] -> 906 D.reflow $ "In fact, " <> rStr <> " is a record with NO fields!" 907 908 f:fs -> 909 D.stack 910 [ D.reflow $ 911 "This is usually a typo. Here are the " <> rStr <> " fields that are most similar:" 912 , toNearbyRecord localizer f fs ext 913 , D.fillSep 914 ["So","maybe",D.dullyellow (D.fromName field) 915 ,"should","be",D.green (D.fromName (fst f)) <> "?" 916 ] 917 ] 918 ) 919 920 _ -> 921 badType 922 ( Just exprRegion 923 , "This is not a record, so it has no fields to update!" 924 , "It is" 925 , [ D.reflow $ "But I need a record!" 926 ] 927 ) 928 929 RecordUpdateValue field -> 930 mismatch 931 ( Just exprRegion 932 , "I cannot update the `" <> Name.toChars field <> "` field like this:" 933 , "You are trying to update `" <> Name.toChars field <> "` to be" 934 , "But it should be:" 935 , [ D.toSimpleNote 936 "The record update syntax does not allow you to change the type of fields.\ 937 \ You can achieve that with record constructors or the record literal syntax." 938 ] 939 ) 940 941 Destructure -> 942 mismatch 943 ( Nothing 944 , "This definition is causing issues:" 945 , "You are defining" 946 , "But then trying to destructure it as:" 947 , [] 948 ) 949 950 951 952-- HELPERS 953 954 955countArgs :: T.Type -> Int 956countArgs tipe = 957 case tipe of 958 T.Lambda _ _ stuff -> 959 1 + length stuff 960 961 _ -> 962 0 963 964 965 966-- FIELD NAME HELPERS 967 968 969toNearbyRecord :: L.Localizer -> (Name.Name, T.Type) -> [(Name.Name, T.Type)] -> T.Extension -> D.Doc 970toNearbyRecord localizer f fs ext = 971 D.indent 4 $ 972 if length fs <= 3 then 973 RT.vrecord (map (fieldToDocs localizer) (f:fs)) (extToDoc ext) 974 else 975 RT.vrecordSnippet (fieldToDocs localizer f) (map (fieldToDocs localizer) (take 3 fs)) 976 977 978fieldToDocs :: L.Localizer -> (Name.Name, T.Type) -> (D.Doc, D.Doc) 979fieldToDocs localizer (name, tipe) = 980 ( D.fromName name 981 , T.toDoc localizer RT.None tipe 982 ) 983 984 985extToDoc :: T.Extension -> Maybe D.Doc 986extToDoc ext = 987 case ext of 988 T.Closed -> Nothing 989 T.FlexOpen x -> Just (D.fromName x) 990 T.RigidOpen x -> Just (D.fromName x) 991 992 993 994-- OP LEFT 995 996 997opLeftToDocs :: L.Localizer -> Category -> Name.Name -> T.Type -> T.Type -> (D.Doc, D.Doc) 998opLeftToDocs localizer category op tipe expected = 999 case op of 1000 "+" 1001 | isString tipe -> badStringAdd 1002 | isList tipe -> badListAdd localizer category "left" tipe expected 1003 | otherwise -> badMath localizer category "Addition" "left" "+" tipe expected [] 1004 1005 "*" 1006 | isList tipe -> badListMul localizer category "left" tipe expected 1007 | otherwise -> badMath localizer category "Multiplication" "left" "*" tipe expected [] 1008 1009 "-" -> badMath localizer category "Subtraction" "left" "-" tipe expected [] 1010 "^" -> badMath localizer category "Exponentiation" "left" "^" tipe expected [] 1011 "/" -> badFDiv localizer "left" tipe expected 1012 "//" -> badIDiv localizer "left" tipe expected 1013 "&&" -> badBool localizer "&&" "left" tipe expected 1014 "||" -> badBool localizer "||" "left" tipe expected 1015 "<" -> badCompLeft localizer category "<" "left" tipe expected 1016 ">" -> badCompLeft localizer category ">" "left" tipe expected 1017 "<=" -> badCompLeft localizer category "<=" "left" tipe expected 1018 ">=" -> badCompLeft localizer category ">=" "left" tipe expected 1019 1020 "++" -> badAppendLeft localizer category tipe expected 1021 1022 "<|" -> 1023 ( "The left side of (<|) needs to be a function so I can pipe arguments to it!" 1024 , loneType localizer tipe expected 1025 (D.reflow (addCategory "I am seeing" category)) 1026 [ D.reflow $ "This needs to be some kind of function though!" 1027 ] 1028 ) 1029 1030 _ -> 1031 ( D.reflow $ 1032 "The left argument of (" <> Name.toChars op <> ") is causing problems:" 1033 , typeComparison localizer tipe expected 1034 (addCategory "The left argument is" category) 1035 ("But (" <> Name.toChars op <> ") needs the left argument to be:") 1036 [] 1037 ) 1038 1039 1040 1041-- OP RIGHT 1042 1043 1044data RightDocs 1045 = EmphBoth (D.Doc, D.Doc) 1046 | EmphRight (D.Doc, D.Doc) 1047 1048 1049opRightToDocs :: L.Localizer -> Category -> Name.Name -> T.Type -> T.Type -> RightDocs 1050opRightToDocs localizer category op tipe expected = 1051 case op of 1052 "+" 1053 | isFloat expected && isInt tipe -> badCast op FloatInt 1054 | isInt expected && isFloat tipe -> badCast op IntFloat 1055 | isString tipe -> EmphRight $ badStringAdd 1056 | isList tipe -> EmphRight $ badListAdd localizer category "right" tipe expected 1057 | otherwise -> EmphRight $ badMath localizer category "Addition" "right" "+" tipe expected [] 1058 1059 "*" 1060 | isFloat expected && isInt tipe -> badCast op FloatInt 1061 | isInt expected && isFloat tipe -> badCast op IntFloat 1062 | isList tipe -> EmphRight $ badListMul localizer category "right" tipe expected 1063 | otherwise -> EmphRight $ badMath localizer category "Multiplication" "right" "*" tipe expected [] 1064 1065 "-" 1066 | isFloat expected && isInt tipe -> badCast op FloatInt 1067 | isInt expected && isFloat tipe -> badCast op IntFloat 1068 | otherwise -> 1069 EmphRight $ badMath localizer category "Subtraction" "right" "-" tipe expected [] 1070 1071 "^" 1072 | isFloat expected && isInt tipe -> badCast op FloatInt 1073 | isInt expected && isFloat tipe -> badCast op IntFloat 1074 | otherwise -> 1075 EmphRight $ badMath localizer category "Exponentiation" "right" "^" tipe expected [] 1076 1077 "/" -> EmphRight $ badFDiv localizer "right" tipe expected 1078 "//" -> EmphRight $ badIDiv localizer "right" tipe expected 1079 "&&" -> EmphRight $ badBool localizer "&&" "right" tipe expected 1080 "||" -> EmphRight $ badBool localizer "||" "right" tipe expected 1081 "<" -> badCompRight localizer "<" tipe expected 1082 ">" -> badCompRight localizer ">" tipe expected 1083 "<=" -> badCompRight localizer "<=" tipe expected 1084 ">=" -> badCompRight localizer ">=" tipe expected 1085 "==" -> badEquality localizer "==" tipe expected 1086 "/=" -> badEquality localizer "/=" tipe expected 1087 1088 "::" -> badConsRight localizer category tipe expected 1089 "++" -> badAppendRight localizer category tipe expected 1090 1091 "<|" -> 1092 EmphRight 1093 ( D.reflow $ "I cannot send this through the (<|) pipe:" 1094 , typeComparison localizer tipe expected 1095 "The argument is:" 1096 "But (<|) is piping it to a function that expects:" 1097 [] 1098 ) 1099 1100 "|>" -> 1101 case (tipe, expected) of 1102 (T.Lambda expectedArgType _ _, T.Lambda argType _ _) -> 1103 EmphRight 1104 ( D.reflow $ "This function cannot handle the argument sent through the (|>) pipe:" 1105 , typeComparison localizer argType expectedArgType 1106 "The argument is:" 1107 "But (|>) is piping it to a function that expects:" 1108 [] 1109 ) 1110 1111 _ -> 1112 EmphRight 1113 ( D.reflow $ "The right side of (|>) needs to be a function so I can pipe arguments to it!" 1114 , loneType localizer tipe expected 1115 (D.reflow (addCategory "But instead of a function, I am seeing" category)) 1116 [] 1117 ) 1118 1119 _ -> 1120 badOpRightFallback localizer category op tipe expected 1121 1122 1123badOpRightFallback :: L.Localizer -> Category -> Name.Name -> T.Type -> T.Type -> RightDocs 1124badOpRightFallback localizer category op tipe expected = 1125 EmphRight 1126 ( D.reflow $ 1127 "The right argument of (" <> Name.toChars op <> ") is causing problems." 1128 , typeComparison localizer tipe expected 1129 (addCategory "The right argument is" category) 1130 ("But (" <> Name.toChars op <> ") needs the right argument to be:") 1131 [ D.toSimpleHint $ 1132 "With operators like (" ++ Name.toChars op ++ ") I always check the left\ 1133 \ side first. If it seems fine, I assume it is correct and check the right\ 1134 \ side. So the problem may be in how the left and right arguments interact!" 1135 ] 1136 ) 1137 1138 1139isInt :: T.Type -> Bool 1140isInt tipe = 1141 case tipe of 1142 T.Type home name [] -> 1143 T.isInt home name 1144 1145 _ -> 1146 False 1147 1148 1149isFloat :: T.Type -> Bool 1150isFloat tipe = 1151 case tipe of 1152 T.Type home name [] -> 1153 T.isFloat home name 1154 1155 _ -> 1156 False 1157 1158 1159isString :: T.Type -> Bool 1160isString tipe = 1161 case tipe of 1162 T.Type home name [] -> 1163 T.isString home name 1164 1165 _ -> 1166 False 1167 1168 1169isList :: T.Type -> Bool 1170isList tipe = 1171 case tipe of 1172 T.Type home name [_] -> 1173 T.isList home name 1174 1175 _ -> 1176 False 1177 1178 1179 1180-- BAD CONS 1181 1182 1183badConsRight :: L.Localizer -> Category -> T.Type -> T.Type -> RightDocs 1184badConsRight localizer category tipe expected = 1185 case tipe of 1186 T.Type home1 name1 [actualElement] | T.isList home1 name1 -> 1187 case expected of 1188 T.Type home2 name2 [expectedElement] | T.isList home2 name2 -> 1189 EmphBoth 1190 ( D.reflow "I am having trouble with this (::) operator:" 1191 , typeComparison localizer expectedElement actualElement 1192 "The left side of (::) is:" 1193 "But you are trying to put that into a list filled with:" 1194 ( case expectedElement of 1195 T.Type home name [_] | T.isList home name -> 1196 [ D.toSimpleHint 1197 "Are you trying to append two lists? The (++) operator\ 1198 \ appends lists, whereas the (::) operator is only for\ 1199 \ adding ONE element to a list." 1200 ] 1201 1202 _ -> 1203 [ D.reflow 1204 "Lists need ALL elements to be the same type though." 1205 ] 1206 ) 1207 ) 1208 1209 _ -> 1210 badOpRightFallback localizer category "::" tipe expected 1211 1212 _ -> 1213 EmphRight 1214 ( D.reflow "The (::) operator can only add elements onto lists." 1215 , loneType localizer tipe expected 1216 (D.reflow (addCategory "The right side is" category)) 1217 [D.fillSep ["But","(::)","needs","a",D.dullyellow "List","on","the","right."] 1218 ] 1219 ) 1220 1221 1222 1223-- BAD APPEND 1224 1225 1226data AppendType 1227 = ANumber D.Doc D.Doc 1228 | AString 1229 | AList 1230 | AOther 1231 1232 1233toAppendType :: T.Type -> AppendType 1234toAppendType tipe = 1235 case tipe of 1236 T.Type home name _ 1237 | T.isInt home name -> ANumber "Int" "String.fromInt" 1238 | T.isFloat home name -> ANumber "Float" "String.fromFloat" 1239 | T.isString home name -> AString 1240 | T.isList home name -> AList 1241 1242 T.FlexSuper T.Number _ -> ANumber "number" "String.fromInt" 1243 1244 _ -> AOther 1245 1246 1247badAppendLeft :: L.Localizer -> Category -> T.Type -> T.Type -> (D.Doc, D.Doc) 1248badAppendLeft localizer category tipe expected = 1249 case toAppendType tipe of 1250 ANumber thing stringFromThing -> 1251 ( D.fillSep 1252 ["The","(++)","operator","can","append","List","and","String" 1253 ,"values,","but","not",D.dullyellow thing,"values","like","this:" 1254 ] 1255 , D.fillSep 1256 ["Try","using",D.green stringFromThing,"to","turn","it","into","a","string?" 1257 ,"Or","put","it","in","[]","to","make","it","a","list?" 1258 ,"Or","switch","to","the","(::)","operator?" 1259 ] 1260 ) 1261 1262 _ -> 1263 ( D.reflow $ 1264 "The (++) operator cannot append this type of value:" 1265 , loneType localizer tipe expected 1266 (D.reflow (addCategory "I am seeing" category)) 1267 [ D.fillSep 1268 ["But","the","(++)","operator","is","only","for","appending" 1269 ,D.dullyellow "List","and",D.dullyellow "String","values." 1270 ,"Maybe","put","this","value","in","[]","to","make","it","a","list?" 1271 ] 1272 ] 1273 ) 1274 1275 1276badAppendRight :: L.Localizer -> Category -> T.Type -> T.Type -> RightDocs 1277badAppendRight localizer category tipe expected = 1278 case (toAppendType expected, toAppendType tipe) of 1279 (AString, ANumber thing stringFromThing) -> 1280 EmphRight 1281 ( D.fillSep 1282 ["I","thought","I","was","appending",D.dullyellow "String","values","here," 1283 ,"not",D.dullyellow thing,"values","like","this:" 1284 ] 1285 , D.fillSep 1286 ["Try","using",D.green stringFromThing,"to","turn","it","into","a","string?"] 1287 ) 1288 1289 (AList, ANumber thing _) -> 1290 EmphRight 1291 ( D.fillSep 1292 ["I","thought","I","was","appending",D.dullyellow "List","values","here," 1293 ,"not",D.dullyellow thing,"values","like","this:" 1294 ] 1295 , D.reflow "Try putting it in [] to make it a list?" 1296 ) 1297 1298 (AString, AList) -> 1299 EmphBoth 1300 ( D.reflow $ 1301 "The (++) operator needs the same type of value on both sides:" 1302 , D.fillSep 1303 ["I","see","a",D.dullyellow "String","on","the","left","and","a" 1304 ,D.dullyellow "List","on","the","right.","Which","should","it","be?" 1305 ,"Does","the","string","need","[]","around","it","to","become","a","list?" 1306 ] 1307 ) 1308 1309 (AList, AString) -> 1310 EmphBoth 1311 ( D.reflow $ 1312 "The (++) operator needs the same type of value on both sides:" 1313 , D.fillSep 1314 ["I","see","a",D.dullyellow "List","on","the","left","and","a" 1315 ,D.dullyellow "String","on","the","right.","Which","should","it","be?" 1316 ,"Does","the","string","need","[]","around","it","to","become","a","list?" 1317 ] 1318 ) 1319 1320 (_,_) -> 1321 EmphBoth 1322 ( D.reflow $ 1323 "The (++) operator cannot append these two values:" 1324 , typeComparison localizer expected tipe 1325 "I already figured out that the left side of (++) is:" 1326 (addCategory "But this clashes with the right side, which is" category) 1327 [] 1328 ) 1329 1330 1331 1332-- BAD MATH 1333 1334 1335data ThisThenThat = FloatInt | IntFloat 1336 1337 1338badCast :: Name.Name -> ThisThenThat -> RightDocs 1339badCast op thisThenThat = 1340 EmphBoth 1341 ( D.reflow $ 1342 "I need both sides of (" <> Name.toChars op <> ") to be the exact same type. Both Int or both Float." 1343 , let 1344 anInt = ["an", D.dullyellow "Int"] 1345 aFloat = ["a", D.dullyellow "Float"] 1346 toFloat = D.green "toFloat" 1347 round = D.green "round" 1348 in 1349 case thisThenThat of 1350 FloatInt -> 1351 badCastHelp aFloat anInt round toFloat 1352 1353 IntFloat -> 1354 badCastHelp anInt aFloat toFloat round 1355 ) 1356 1357 1358badCastHelp :: [D.Doc] -> [D.Doc] -> D.Doc -> D.Doc -> D.Doc 1359badCastHelp anInt aFloat toFloat round = 1360 D.stack 1361 [ D.fillSep $ 1362 ["But","I","see"] 1363 ++ anInt 1364 ++ ["on","the","left","and"] 1365 ++ aFloat 1366 ++ ["on","the","right."] 1367 , D.fillSep 1368 ["Use",toFloat,"on","the","left","(or",round,"on" 1369 ,"the","right)","to","make","both","sides","match!" 1370 ] 1371 , D.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats." 1372 ] 1373 1374 1375badStringAdd :: (D.Doc, D.Doc) 1376badStringAdd = 1377 ( 1378 D.fillSep ["I","cannot","do","addition","with",D.dullyellow "String","values","like","this","one:"] 1379 , 1380 D.stack 1381 [ D.fillSep 1382 ["The","(+)","operator","only","works","with",D.dullyellow "Int","and",D.dullyellow "Float","values." 1383 ] 1384 , D.toFancyHint 1385 ["Switch","to","the",D.green "(++)","operator","to","append","strings!" 1386 ] 1387 ] 1388 ) 1389 1390 1391badListAdd :: L.Localizer -> Category -> String -> T.Type -> T.Type -> (D.Doc, D.Doc) 1392badListAdd localizer category direction tipe expected = 1393 ( 1394 "I cannot do addition with lists:" 1395 , 1396 loneType localizer tipe expected 1397 (D.reflow (addCategory ("The " <> direction <> " side of (+) is") category)) 1398 [ D.fillSep 1399 ["But","(+)","only","works","with",D.dullyellow "Int","and",D.dullyellow "Float","values." 1400 ] 1401 , D.toFancyHint 1402 ["Switch","to","the",D.green "(++)","operator","to","append","lists!" 1403 ] 1404 ] 1405 ) 1406 1407 1408badListMul :: L.Localizer -> Category -> String -> T.Type -> T.Type -> (D.Doc, D.Doc) 1409badListMul localizer category direction tipe expected = 1410 badMath localizer category "Multiplication" direction "*" tipe expected 1411 [ 1412 D.toFancyHint 1413 [ "Maybe", "you", "want" 1414 , D.green "List.repeat" 1415 , "to", "build","a","list","of","repeated","values?" 1416 ] 1417 ] 1418 1419 1420badMath :: L.Localizer -> Category -> String -> String -> String -> T.Type -> T.Type -> [D.Doc] -> (D.Doc, D.Doc) 1421badMath localizer category operation direction op tipe expected otherHints = 1422 ( 1423 D.reflow $ 1424 operation ++ " does not work with this value:" 1425 , 1426 loneType localizer tipe expected 1427 (D.reflow (addCategory ("The " <> direction <> " side of (" <> op <> ") is") category)) 1428 ( [ D.fillSep 1429 ["But","(" <> D.fromChars op <> ")","only","works","with" 1430 ,D.dullyellow "Int","and",D.dullyellow "Float","values." 1431 ] 1432 ] 1433 ++ otherHints 1434 ) 1435 ) 1436 1437 1438badFDiv :: L.Localizer -> D.Doc -> T.Type -> T.Type -> (D.Doc, D.Doc) 1439badFDiv localizer direction tipe expected = 1440 ( 1441 D.reflow $ 1442 "The (/) operator is specifically for floating-point division:" 1443 , 1444 if isInt tipe then 1445 D.stack 1446 [ D.fillSep 1447 ["The",direction,"side","of","(/)","must","be","a" 1448 ,D.dullyellow "Float" <> "," 1449 ,"but","I","am","seeing","an",D.dullyellow "Int" <> "." 1450 ,"I","recommend:" 1451 ] 1452 , D.vcat 1453 [ D.green "toFloat" <> " for explicit conversions " <> D.black "(toFloat 5 / 2) == 2.5" 1454 , D.green "(//) " <> " for integer division " <> D.black "(5 // 2) == 2" 1455 ] 1456 , D.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats." 1457 ] 1458 1459 else 1460 loneType localizer tipe expected 1461 (D.fillSep 1462 ["The",direction,"side","of","(/)","must","be","a" 1463 ,D.dullyellow "Float" <> ",","but","instead","I","am","seeing:" 1464 ] 1465 ) 1466 [] 1467 ) 1468 1469 1470badIDiv :: L.Localizer -> D.Doc -> T.Type -> T.Type -> (D.Doc, D.Doc) 1471badIDiv localizer direction tipe expected = 1472 ( 1473 D.reflow $ 1474 "The (//) operator is specifically for integer division:" 1475 , 1476 if isFloat tipe then 1477 D.stack 1478 [ D.fillSep 1479 ["The",direction,"side","of","(//)","must","be","an" 1480 ,D.dullyellow "Int" <> "," 1481 ,"but","I","am","seeing","a",D.dullyellow "Float" <> "." 1482 ,"I","recommend","doing","the","conversion","explicitly" 1483 ,"with","one","of","these","functions:" 1484 ] 1485 , D.vcat 1486 [ D.green "round" <> " 3.5 == 4" 1487 , D.green "floor" <> " 3.5 == 3" 1488 , D.green "ceiling" <> " 3.5 == 4" 1489 , D.green "truncate" <> " 3.5 == 3" 1490 ] 1491 , D.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats." 1492 ] 1493 else 1494 loneType localizer tipe expected 1495 ( D.fillSep 1496 ["The",direction,"side","of","(//)","must","be","an" 1497 ,D.dullyellow "Int" <> ",","but","instead","I","am","seeing:" 1498 ] 1499 ) 1500 [] 1501 ) 1502 1503 1504 1505-- BAD BOOLS 1506 1507 1508badBool :: L.Localizer -> D.Doc -> D.Doc -> T.Type -> T.Type -> (D.Doc, D.Doc) 1509badBool localizer op direction tipe expected = 1510 ( 1511 D.reflow $ 1512 "I am struggling with this boolean operation:" 1513 , 1514 loneType localizer tipe expected 1515 ( D.fillSep 1516 ["Both","sides","of","(" <> op <> ")","must","be" 1517 ,D.dullyellow "Bool","values,","but","the",direction,"side","is:" 1518 ] 1519 ) 1520 [] 1521 ) 1522 1523 1524 1525-- BAD COMPARISON 1526 1527 1528badCompLeft :: L.Localizer -> Category -> String -> String -> T.Type -> T.Type -> (D.Doc, D.Doc) 1529badCompLeft localizer category op direction tipe expected = 1530 ( 1531 D.reflow $ 1532 "I cannot do a comparison with this value:" 1533 , 1534 loneType localizer tipe expected 1535 (D.reflow (addCategory ("The " <> direction <> " side of (" <> op <> ") is") category)) 1536 [ D.fillSep 1537 ["But","(" <> D.fromChars op <> ")","only","works","on" 1538 ,D.dullyellow "Int" <> "," 1539 ,D.dullyellow "Float" <> "," 1540 ,D.dullyellow "Char" <> "," 1541 ,"and" 1542 ,D.dullyellow "String" 1543 ,"values.","It","can","work","on","lists","and","tuples" 1544 ,"of","comparable","values","as","well,","but","it","is" 1545 ,"usually","better","to","find","a","different","path." 1546 ] 1547 ] 1548 ) 1549 1550 1551badCompRight :: L.Localizer -> String -> T.Type -> T.Type -> RightDocs 1552badCompRight localizer op tipe expected = 1553 EmphBoth 1554 ( 1555 D.reflow $ 1556 "I need both sides of (" <> op <> ") to be the same type:" 1557 , 1558 typeComparison localizer expected tipe 1559 ("The left side of (" <> op <> ") is:") 1560 "But the right side is:" 1561 [ D.reflow $ 1562 "I cannot compare different types though! Which side of (" <> op <> ") is the problem?" 1563 ] 1564 ) 1565 1566 1567 1568-- BAD EQUALITY 1569 1570 1571badEquality :: L.Localizer -> String -> T.Type -> T.Type -> RightDocs 1572badEquality localizer op tipe expected = 1573 EmphBoth 1574 ( 1575 D.reflow $ 1576 "I need both sides of (" <> op <> ") to be the same type:" 1577 , 1578 typeComparison localizer expected tipe 1579 ("The left side of (" <> op <> ") is:") 1580 "But the right side is:" 1581 [ if isFloat tipe || isFloat expected then 1582 D.toSimpleNote $ 1583 "Equality on floats is not 100% reliable due to the design of IEEE 754. I\ 1584 \ recommend a check like (abs (x - y) < 0.0001) instead." 1585 else 1586 D.reflow "Different types can never be equal though! Which side is messed up?" 1587 ] 1588 ) 1589 1590 1591 1592-- INFINITE TYPES 1593 1594 1595toInfiniteReport :: Code.Source -> L.Localizer -> A.Region -> Name.Name -> T.Type -> Report.Report 1596toInfiniteReport source localizer region name overallType = 1597 Report.Report "INFINITE TYPE" region [] $ 1598 Code.toSnippet source region Nothing 1599 ( 1600 D.reflow $ 1601 "I am inferring a weird self-referential type for " <> Name.toChars name <> ":" 1602 , 1603 D.stack 1604 [ D.reflow $ 1605 "Here is my best effort at writing down the type. You will see ∞ for\ 1606 \ parts of the type that repeat something already printed out infinitely." 1607 , D.indent 4 (D.dullyellow (T.toDoc localizer RT.None overallType)) 1608 , D.reflowLink 1609 "Staring at this type is usually not so helpful, so I recommend reading the hints at" 1610 "infinite-type" 1611 "to get unstuck!" 1612 ] 1613 ) 1614