on master help sign in
Improve messages for documentation errors
c0c53dbEvan Czaplicki
src/Docs/Check.hs+48−28
@@ -5,15 +5,16 @@ import qualified Data.List as List
5import qualified Data.Map as Map
5import qualified Data.Map as Map
6import qualified Data.Set as Set
6import qualified Data.Set as Set
7import qualified Data.Traversable as T
7import qualified Data.Traversable as T
8−import Text.Parsec ((<|>), choice, many, string)
8+import Text.Parsec ((<|>), choice, many, setPosition, string)
9+import Text.Parsec.Pos (newPos)
9
10
10import qualified AST.Variable as Var
11import qualified AST.Variable as Var
11import qualified Docs.AST as Docs
12import qualified Docs.AST as Docs
12import qualified Elm.Compiler.Type as Type
13import qualified Elm.Compiler.Type as Type
13import Elm.Utils ((|>))
14import Elm.Utils ((|>))
14import Parse.Helpers
15import Parse.Helpers
15− ( anyUntil, commaSep1, iParse, parens, simpleNewline, symOp, var
16+ ( addLocation, anyUntil, commaSep1, iParse, parens, simpleNewline, symOp
16− , whitespace
17+ , var, whitespace
17 )
18 )
18import qualified Reporting.Annotation as A
19import qualified Reporting.Annotation as A
19import qualified Reporting.Error.Docs as Error
20import qualified Reporting.Error.Docs as Error
@@ -40,11 +41,14 @@ checkHelp :: R.Region -> [Var.Value] -> Docs.Centralized -> Result w Docs.Checke
40checkHelp region exports (Docs.Docs comment aliases types values) =
41checkHelp region exports (Docs.Docs comment aliases types values) =
41 let
42 let
42 docNames =
43 docNames =
43− Set.fromList (commentedNames comment)
44+ commentedNames region comment
45+
46+ docSet =
47+ Set.fromList (map A.drop docNames)
44
48
45 checkCategory checkValue dict =
49 checkCategory checkValue dict =
46 dict
50 dict
47− |> Map.filterWithKey (\key _ -> Set.member key docNames)
51+ |> Map.filterWithKey (\key _ -> Set.member key docSet)
48 |> Map.mapWithKey checkValue
52 |> Map.mapWithKey checkValue
49 |> T.traverse id
53 |> T.traverse id
50
54
@@ -77,15 +81,14 @@ checkUnion exportedUnions name value =
77
81
78filterUnionTags :: [(String, [String])] -> String -> A.Located Docs.Union -> Result w (A.Located Docs.Union)
82filterUnionTags :: [(String, [String])] -> String -> A.Located Docs.Union -> Result w (A.Located Docs.Union)
79filterUnionTags exportedUnions name (A.A region union@(Docs.Union _ _ ctors)) =
83filterUnionTags exportedUnions name (A.A region union@(Docs.Union _ _ ctors)) =
84+ let
85+ exportedTags =
80− case List.lookup name exportedUnions of
86+ maybe [] id (List.lookup name exportedUnions)
81− Nothing ->
87+ in
82− R.throw region (Error.OnlyInExports name)
83−
84− Just tags ->
85− R.ok $ A.A region $ union
88+ R.ok $ A.A region $ union
86− { Docs.unionCases =
89+ { Docs.unionCases =
87− filter (\(tag, _) -> elem tag tags) ctors
90+ filter (\(tag, _) -> elem tag exportedTags) ctors
88− }
91+ }
89
92
90
93
91
94
@@ -108,36 +111,53 @@ hasType name (A.A region value) =
108
111
109-- CHECK MODULE COMMENT
112-- CHECK MODULE COMMENT
110
113
111−checkModuleComment :: R.Region -> [Var.Value] -> Set.Set String -> Result w ()
114+checkModuleComment :: R.Region -> [Var.Value] -> [A.Located String] -> Result w ()
112−checkModuleComment region exports docNames =
115+checkModuleComment docRegion exports locatedDocNames =
113 let
116 let
114 exportNames =
117 exportNames =
115− Set.fromList (map valueName exports)
118+ Map.fromList (map (\v -> (valueName v, ())) exports)
116−
117− onlyInDocs =
118− Set.toList (Set.difference docNames exportNames)
119
119
120− onlyInExports =
120+ docNames =
121+ Map.fromList (map (\(A.A region name) -> (name, region)) locatedDocNames)
122+
123+ docErrors =
124+ Map.difference docNames exportNames
125+ |> Map.toList
126+ |> T.traverse (\(name, region) -> R.throw region (Error.OnlyInDocs name))
127+
128+ namesOnlyInExports =
121− Set.toList (Set.difference exportNames docNames)
129+ Map.difference exportNames docNames
130+ |> Map.toList
131+ |> map fst
132+
133+ exportErrors =
134+ if null namesOnlyInExports then
135+ R.ok ()
136+ else
137+ R.throw docRegion (Error.OnlyInExports namesOnlyInExports)
122 in
138 in
123 (\_ _ -> ())
139 (\_ _ -> ())
124− <$> T.traverse (R.throw region . Error.OnlyInDocs) onlyInDocs
140+ <$> docErrors
125− <*> T.traverse (R.throw region . Error.OnlyInExports) onlyInExports
141+ <*> exportErrors
126
142
127
143
128−commentedNames :: String -> [String]
144+commentedNames :: R.Region -> String -> [A.Located String]
129−commentedNames comment =
145+commentedNames (R.Region start _) comment =
130 let
146 let
131 nameParser =
147 nameParser =
132 choice
148 choice
133 [ do string "@docs"
149 [ do string "@docs"
134 whitespace
150 whitespace
135− commaSep1 (var <|> parens symOp)
151+ commaSep1 (addLocation (var <|> parens symOp))
136 , do anyUntil simpleNewline
152 , do anyUntil simpleNewline
137 return []
153 return []
138 ]
154 ]
155+
156+ docCommentParser =
157+ do setPosition (newPos "docs" (R.line start) (R.column start + 3))
158+ concat <$> many nameParser
139 in
159 in
140− case iParse (concat <$> many nameParser) comment of
160+ case iParse docCommentParser comment of
141 Left _ ->
161 Left _ ->
142 []
162 []
143
163
src/Reporting/Error/Docs.hs+11−8
@@ -7,7 +7,7 @@ import qualified Reporting.Report as Report
7data Error
7data Error
8 = NoDocs
8 = NoDocs
9 | OnlyInDocs String
9 | OnlyInDocs String
10− | OnlyInExports String
10+ | OnlyInExports [String]
11 | NoComment String
11 | NoComment String
12 | NoType String
12 | NoType String
13
13
@@ -18,26 +18,29 @@ toReport :: Error -> Report.Report
18toReport err =
18toReport err =
19 case err of
19 case err of
20 NoDocs ->
20 NoDocs ->
21− Report.simple "NO MODULE DOCUMENTATION"
21+ Report.simple "DOCUMENTATION ERROR"
22 ( "You must have a documentation comment between the module declaration and the\n"
22 ( "You must have a documentation comment between the module declaration and the\n"
23 ++ "imports."
23 ++ "imports."
24 )
24 )
25 "Learn how at <http://package.elm-lang.org/help/documentation-format>"
25 "Learn how at <http://package.elm-lang.org/help/documentation-format>"
26
26
27 OnlyInDocs name ->
27 OnlyInDocs name ->
28− Report.simple "UNDEFINED DOCUMENTATION"
28+ Report.simple "DOCUMENTATION ERROR"
29 ("Your module documentation includes `" ++ name ++ "` which is not exported.")
29 ("Your module documentation includes `" ++ name ++ "` which is not exported.")
30 "Is it misspelled? Should it be exported?"
30 "Is it misspelled? Should it be exported?"
31
31
32− OnlyInExports name ->
32+ OnlyInExports names ->
33− Report.simple "MISSING DOCUMENTATION"
33+ Report.simple
34+ "DOCUMENTATION ERROR"
34− ("Your module exports `" ++ name ++ "` but it is not in the module documentation.")
35+ ( "The following exports do not appear in your module documentation:\n"
36+ ++ concatMap ("\n " ++) names
37+ )
35− ( "It needs to be listed in the module documentation.\n"
38+ ( "All exports must be listed in the module documentation after a @docs keyword.\n"
36 ++ "Learn how at <http://package.elm-lang.org/help/documentation-format>"
39 ++ "Learn how at <http://package.elm-lang.org/help/documentation-format>"
37 )
40 )
38
41
39 NoComment name ->
42 NoComment name ->
40− Report.simple "MISSING COMMENT"
43+ Report.simple "DOCUMENTATION ERROR"
41 ("The value `" ++ name ++ "` does not have a documentation comment.")
44 ("The value `" ++ name ++ "` does not have a documentation comment.")
42 ( "Documentation comments start with {-| and end with -}. They should provide a\n"
45 ( "Documentation comments start with {-| and end with -}. They should provide a\n"
43 ++ "clear description of how they work, and ideally a small code example. This is\n"
46 ++ "clear description of how they work, and ideally a small code example. This is\n"