git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Expose error suggestions in JSON
3ea5bf4Nicholas Wolverson
psc/JSON.hs+9
@@ -30,6 +30,8 @@ data ErrorPosition = ErrorPosition
30 , endColumn :: Int
30 , endColumn :: Int
31 }
31 }
32
32
33+data ErrorSuggestion = ErrorSuggestion { replacement :: String }
34+
33data JSONError = JSONError
35data JSONError = JSONError
34 { position :: Maybe ErrorPosition
36 { position :: Maybe ErrorPosition
35 , message :: String
37 , message :: String
@@ -37,6 +39,7 @@ data JSONError = JSONError
37 , errorLink :: String
39 , errorLink :: String
38 , filename :: Maybe String
40 , filename :: Maybe String
39 , moduleName :: Maybe String
41 , moduleName :: Maybe String
42+ , suggestion :: Maybe ErrorSuggestion
40 }
43 }
41
44
42data JSONResult = JSONResult
45data JSONResult = JSONResult
@@ -47,6 +50,8 @@ data JSONResult = JSONResult
47$(A.deriveJSON A.defaultOptions ''ErrorPosition)
50$(A.deriveJSON A.defaultOptions ''ErrorPosition)
48$(A.deriveJSON A.defaultOptions ''JSONError)
51$(A.deriveJSON A.defaultOptions ''JSONError)
49$(A.deriveJSON A.defaultOptions ''JSONResult)
52$(A.deriveJSON A.defaultOptions ''JSONResult)
53+$(A.deriveJSON A.defaultOptions ''ErrorSuggestion)
54+
50
55
51toJSONErrors :: Bool -> P.Level -> P.MultipleErrors -> [JSONError]
56toJSONErrors :: Bool -> P.Level -> P.MultipleErrors -> [JSONError]
52toJSONErrors verbose level = map (toJSONError verbose level) . P.runMultipleErrors
57toJSONErrors verbose level = map (toJSONError verbose level) . P.runMultipleErrors
@@ -59,6 +64,7 @@ toJSONError verbose level e =
59 (P.wikiUri e)
64 (P.wikiUri e)
60 (P.spanName <$> sspan)
65 (P.spanName <$> sspan)
61 (P.runModuleName <$> P.errorModule e)
66 (P.runModuleName <$> P.errorModule e)
67+ (toSuggestion <$> (P.errorSuggestion $ P.unwrapErrorMessage e))
62 where
68 where
63 sspan :: Maybe P.SourceSpan
69 sspan :: Maybe P.SourceSpan
64 sspan = P.errorSpan e
70 sspan = P.errorSpan e
@@ -69,3 +75,6 @@ toJSONError verbose level e =
69 (P.sourcePosColumn (P.spanStart ss))
75 (P.sourcePosColumn (P.spanStart ss))
70 (P.sourcePosLine (P.spanEnd ss))
76 (P.sourcePosLine (P.spanEnd ss))
71 (P.sourcePosColumn (P.spanEnd ss))
77 (P.sourcePosColumn (P.spanEnd ss))
78+ toSuggestion :: P.ErrorSuggestion -> ErrorSuggestion
79+-- TODO: Adding a newline because source spans chomp everything up to the next character
80+ toSuggestion (P.ErrorSuggestion s) = ErrorSuggestion $ if null s then s else s ++ "\n"
src/Language/PureScript/Errors.hs+70−32
@@ -124,7 +124,7 @@ data SimpleErrorMessage
124 | MisleadingEmptyTypeImport ModuleName (ProperName 'TypeName)
124 | MisleadingEmptyTypeImport ModuleName (ProperName 'TypeName)
125 | ImportHidingModule ModuleName
125 | ImportHidingModule ModuleName
126 | UnusedImport ModuleName
126 | UnusedImport ModuleName
127− | UnusedExplicitImport ModuleName [String]
127+ | UnusedExplicitImport ModuleName [String] (Maybe ModuleName) [DeclarationRef]
128 | UnusedDctorImport (ProperName 'TypeName)
128 | UnusedDctorImport (ProperName 'TypeName)
129 | UnusedDctorExplicitImport (ProperName 'TypeName) [ProperName 'ConstructorName]
129 | UnusedDctorExplicitImport (ProperName 'TypeName) [ProperName 'ConstructorName]
130 | DeprecatedOperatorDecl String
130 | DeprecatedOperatorDecl String
@@ -179,6 +179,8 @@ data HintCategory
179
179
180data ErrorMessage = ErrorMessage [ErrorMessageHint] SimpleErrorMessage deriving (Show)
180data ErrorMessage = ErrorMessage [ErrorMessageHint] SimpleErrorMessage deriving (Show)
181
181
182+newtype ErrorSuggestion = ErrorSuggestion String
183+
182-- | Get the source span for an error
184-- | Get the source span for an error
183errorSpan :: ErrorMessage -> Maybe SourceSpan
185errorSpan :: ErrorMessage -> Maybe SourceSpan
184errorSpan = findHint matchSpan
186errorSpan = findHint matchSpan
@@ -419,6 +421,40 @@ onTypesInErrorMessageM f (ErrorMessage hints simple) = ErrorMessage <$> traverse
419wikiUri :: ErrorMessage -> String
421wikiUri :: ErrorMessage -> String
420wikiUri e = "https://github.com/purescript/purescript/wiki/Error-Code-" ++ errorCode e
422wikiUri e = "https://github.com/purescript/purescript/wiki/Error-Code-" ++ errorCode e
421
423
424+-- TODO Other possible suggestions:
425+-- WildcardInferredType - source span not small enough
426+-- DuplicateSelectiveImport - would require 2 ranges to remove and 1 insert
427+-- DeprecatedClassExport, DeprecatedClassImport, would want to replace smaller span?
428+errorSuggestion :: SimpleErrorMessage -> Maybe ErrorSuggestion
429+errorSuggestion err = case err of
430+ UnusedImport{} -> emptySuggestion
431+ RedundantEmptyHidingImport{} -> emptySuggestion
432+ DuplicateImport{} -> emptySuggestion
433+ RedundantUnqualifiedImport{} -> emptySuggestion
434+ DeprecatedQualifiedSyntax name qualName -> suggest $
435+ "import " ++ runModuleName name ++ " as " ++ runModuleName qualName
436+ UnusedExplicitImport mn _ qual refs -> suggest $ importSuggestion mn refs qual
437+ ImplicitImport mn refs -> suggest $ importSuggestion mn refs Nothing
438+ ImplicitQualifiedImport mn asModule refs -> suggest $ importSuggestion mn refs (Just asModule)
439+ _ -> Nothing
440+
441+ where
442+ emptySuggestion = Just $ ErrorSuggestion ""
443+ suggest = Just . ErrorSuggestion
444+
445+ importSuggestion :: ModuleName -> [ DeclarationRef ] -> Maybe ModuleName -> String
446+ importSuggestion mn refs qual =
447+ "import " ++ runModuleName mn ++ " (" ++ intercalate ", " (map prettyPrintRef refs) ++ ")" ++ qstr qual
448+
449+ qstr :: Maybe ModuleName -> String
450+ qstr (Just mn) = " as " ++ runModuleName mn
451+ qstr Nothing = ""
452+
453+showSuggestion :: SimpleErrorMessage -> String
454+showSuggestion suggestion = case errorSuggestion suggestion of
455+ Just (ErrorSuggestion x) -> x
456+ _ -> ""
457+
422-- |
458-- |
423-- Pretty print a single error, simplifying if necessary
459-- Pretty print a single error, simplifying if necessary
424--
460--
@@ -796,9 +832,11 @@ prettyPrintSingleError full level showWiki e = flip evalState defaultUnknownMap
796 renderSimpleErrorMessage (UnusedImport name) =
832 renderSimpleErrorMessage (UnusedImport name) =
797 line $ "The import of module " ++ runModuleName name ++ " is redundant"
833 line $ "The import of module " ++ runModuleName name ++ " is redundant"
798
834
799− renderSimpleErrorMessage (UnusedExplicitImport name names) =
835+ renderSimpleErrorMessage msg@(UnusedExplicitImport mn names _ _) =
800− paras [ line $ "The import of module " ++ runModuleName name ++ " contains the following unused references:"
836+ paras [ line $ "The import of module " ++ runModuleName mn ++ " contains the following unused references:"
801− , indent $ paras $ map line names ]
837+ , indent $ paras $ map line names
838+ , line $ "It could be replaced with:"
839+ , indent $ line $ showSuggestion msg ]
802
840
803 renderSimpleErrorMessage (UnusedDctorImport name) =
841 renderSimpleErrorMessage (UnusedDctorImport name) =
804 line $ "The import of type " ++ runProperName name ++ " includes data constructors but only the type is used"
842 line $ "The import of type " ++ runProperName name ++ " includes data constructors but only the type is used"
@@ -860,15 +898,15 @@ prettyPrintSingleError full level showWiki e = flip evalState defaultUnknownMap
860 renderSimpleErrorMessage (RedundantEmptyHidingImport mn) =
898 renderSimpleErrorMessage (RedundantEmptyHidingImport mn) =
861 line $ "The import for module " ++ runModuleName mn ++ " is redundant as all members have been explicitly hidden."
899 line $ "The import for module " ++ runModuleName mn ++ " is redundant as all members have been explicitly hidden."
862
900
863− renderSimpleErrorMessage (ImplicitQualifiedImport importedModule asModule refs) =
901+ renderSimpleErrorMessage msg@(ImplicitQualifiedImport importedModule asModule _) =
864 paras [ line $ "Module " ++ runModuleName importedModule ++ " was imported as " ++ runModuleName asModule ++ " with unspecified imports."
902 paras [ line $ "Module " ++ runModuleName importedModule ++ " was imported as " ++ runModuleName asModule ++ " with unspecified imports."
865 , line $ "As there are multiple modules being imported as " ++ runModuleName asModule ++ ", consider using the explicit form:"
903 , line $ "As there are multiple modules being imported as " ++ runModuleName asModule ++ ", consider using the explicit form:"
866− , indent $ line $ "import " ++ runModuleName importedModule ++ " (" ++ intercalate ", " (map prettyPrintRef refs) ++ ") as " ++ runModuleName asModule
904+ , indent $ line $ showSuggestion msg
867 ]
905 ]
868
906
869− renderSimpleErrorMessage (ImplicitImport mn refs) =
907+ renderSimpleErrorMessage msg@(ImplicitImport mn _) =
870 paras [ line $ "Module " ++ runModuleName mn ++ " has unspecified imports, consider using the explicit form: "
908 paras [ line $ "Module " ++ runModuleName mn ++ " has unspecified imports, consider using the explicit form: "
871− , indent $ line $ "import " ++ runModuleName mn ++ " (" ++ intercalate ", " (map prettyPrintRef refs) ++ ")"
909+ , indent $ line $ showSuggestion msg
872 ]
910 ]
873
911
874 renderSimpleErrorMessage (HidingImport mn refs) =
912 renderSimpleErrorMessage (HidingImport mn refs) =
@@ -1009,30 +1047,6 @@ prettyPrintSingleError full level showWiki e = flip evalState defaultUnknownMap
1009 paras :: [Box.Box] -> Box.Box
1047 paras :: [Box.Box] -> Box.Box
1010 paras = Box.vcat Box.left
1048 paras = Box.vcat Box.left
1011
1049
1012− -- Pretty print and export declaration
1013− prettyPrintExport :: DeclarationRef -> String
1014− prettyPrintExport (TypeRef pn _) = runProperName pn
1015− prettyPrintExport ref = prettyPrintRef ref
1016−
1017− prettyPrintRef :: DeclarationRef -> String
1018− prettyPrintRef (TypeRef pn Nothing) = runProperName pn ++ "(..)"
1019− prettyPrintRef (TypeRef pn (Just [])) = runProperName pn
1020− prettyPrintRef (TypeRef pn (Just dctors)) = runProperName pn ++ "(" ++ intercalate ", " (map runProperName dctors) ++ ")"
1021− prettyPrintRef (ValueRef ident) = showIdent ident
1022− prettyPrintRef (TypeClassRef pn) = "class " ++ runProperName pn
1023− prettyPrintRef (ProperRef name) = name
1024− prettyPrintRef (TypeInstanceRef ident) = showIdent ident
1025− prettyPrintRef (ModuleRef name) = "module " ++ runModuleName name
1026− prettyPrintRef (PositionedDeclarationRef _ _ ref) = prettyPrintExport ref
1027−
1028− prettyPrintImport :: ModuleName -> ImportDeclarationType -> Maybe ModuleName -> String
1029− prettyPrintImport mn idt qual =
1030− let i = case idt of
1031− Implicit -> runModuleName mn
1032− Explicit refs -> runModuleName mn ++ " (" ++ intercalate ", " (map prettyPrintRef refs) ++ ")"
1033− Hiding refs -> runModuleName mn ++ " hiding (" ++ intercalate "," (map prettyPrintRef refs) ++ ")"
1034− in i ++ maybe "" (\q -> " as " ++ runModuleName q) qual
1035−
1036 -- | Simplify an error message
1050 -- | Simplify an error message
1037 simplifyErrorMessage :: ErrorMessage -> ErrorMessage
1051 simplifyErrorMessage :: ErrorMessage -> ErrorMessage
1038 simplifyErrorMessage (ErrorMessage hints simple) = ErrorMessage (simplifyHints hints) simple
1052 simplifyErrorMessage (ErrorMessage hints simple) = ErrorMessage (simplifyHints hints) simple
@@ -1084,6 +1098,30 @@ prettyPrintSingleError full level showWiki e = flip evalState defaultUnknownMap
1084 hintCategory PositionedError{} = PositionHint
1098 hintCategory PositionedError{} = PositionHint
1085 hintCategory _ = OtherHint
1099 hintCategory _ = OtherHint
1086
1100
1101+-- Pretty print and export declaration
1102+prettyPrintExport :: DeclarationRef -> String
1103+prettyPrintExport (TypeRef pn _) = runProperName pn
1104+prettyPrintExport ref = prettyPrintRef ref
1105+
1106+prettyPrintImport :: ModuleName -> ImportDeclarationType -> Maybe ModuleName -> String
1107+prettyPrintImport mn idt qual =
1108+ let i = case idt of
1109+ Implicit -> runModuleName mn
1110+ Explicit refs -> runModuleName mn ++ " (" ++ intercalate ", " (map prettyPrintRef refs) ++ ")"
1111+ Hiding refs -> runModuleName mn ++ " hiding (" ++ intercalate "," (map prettyPrintRef refs) ++ ")"
1112+ in i ++ maybe "" (\q -> " as " ++ runModuleName q) qual
1113+
1114+prettyPrintRef :: DeclarationRef -> String
1115+prettyPrintRef (TypeRef pn Nothing) = runProperName pn ++ "(..)"
1116+prettyPrintRef (TypeRef pn (Just [])) = runProperName pn
1117+prettyPrintRef (TypeRef pn (Just dctors)) = runProperName pn ++ "(" ++ intercalate ", " (map runProperName dctors) ++ ")"
1118+prettyPrintRef (ValueRef ident) = showIdent ident
1119+prettyPrintRef (TypeClassRef pn) = "class " ++ runProperName pn
1120+prettyPrintRef (ProperRef name) = name
1121+prettyPrintRef (TypeInstanceRef ident) = showIdent ident
1122+prettyPrintRef (ModuleRef name) = "module " ++ runModuleName name
1123+prettyPrintRef (PositionedDeclarationRef _ _ ref) = prettyPrintExport ref
1124+
1087-- |
1125-- |
1088-- Pretty print multiple errors
1126-- Pretty print multiple errors
1089--
1127--
src/Language/PureScript/Linter/Imports.hs+7−5
@@ -185,10 +185,9 @@ lintImportDecl env mni qualifierName names declType =
185 :: (ModuleName -> [DeclarationRef] -> SimpleErrorMessage)
185 :: (ModuleName -> [DeclarationRef] -> SimpleErrorMessage)
186 -> m ()
186 -> m ()
187 checkImplicit warning =
187 checkImplicit warning =
188− let allRefs = findUsedRefs env mni qualifierName names
189− in if null allRefs
188+ if null allRefs
190− then unused
189+ then unused
191− else tell $ errorMessage $ warning mni allRefs
190+ else tell $ errorMessage $ warning mni allRefs
192
191
193 checkExplicit
192 checkExplicit
194 :: [DeclarationRef]
193 :: [DeclarationRef]
@@ -201,7 +200,7 @@ lintImportDecl env mni qualifierName names declType =
201 case (length diff, length idents) of
200 case (length diff, length idents) of
202 (0, _) -> return ()
201 (0, _) -> return ()
203 (n, m) | n == m -> unused
202 (n, m) | n == m -> unused
204− _ -> tell $ errorMessage $ UnusedExplicitImport mni diff
203+ _ -> tell $ errorMessage $ UnusedExplicitImport mni diff qualifierName allRefs
205
204
206 -- If we've not already warned a type is unused, check its data constructors
205 -- If we've not already warned a type is unused, check its data constructors
207 forM_ (mapMaybe getTypeRef declrefs) $ \(tn, c) -> do
206 forM_ (mapMaybe getTypeRef declrefs) $ \(tn, c) -> do
@@ -218,6 +217,9 @@ lintImportDecl env mni qualifierName names declType =
218 unused :: m ()
217 unused :: m ()
219 unused = tell $ errorMessage $ UnusedImport mni
218 unused = tell $ errorMessage $ UnusedImport mni
220
219
220+ allRefs :: [DeclarationRef]
221+ allRefs = findUsedRefs env mni qualifierName names
222+
221 dtys
223 dtys
222 :: ModuleName
224 :: ModuleName
223 -> [((ProperName 'TypeName, [ProperName 'ConstructorName]), ModuleName)]
225 -> [((ProperName 'TypeName, [ProperName 'ConstructorName]), ModuleName)]