imp: errors: more error prettification

This commit is contained in:
Simon Michael 2022-07-13 02:29:36 +01:00
parent 84f951e020
commit 1c67d0860e
15 changed files with 110 additions and 49 deletions

View File

@ -115,11 +115,11 @@ transactionCheckBalanced BalancingOpts{commodity_styles_} t = errs
where where
rmsg rmsg
| rsumok = "" | rsumok = ""
| not rsignsok = "The real postings all have the same sign." | not rsignsok = "The real postings all have the same sign. Consider negating some of them."
| otherwise = "The real postings' sum should be 0 but is: " ++ showMixedAmountOneLine rsumcost | otherwise = "The real postings' sum should be 0 but is: " ++ showMixedAmountOneLine rsumcost
bvmsg bvmsg
| bvsumok = "" | bvsumok = ""
| not bvsignsok = "The balanced virtual postings all have the same sign." | not bvsignsok = "The balanced virtual postings all have the same sign. Consider negating some of them."
| otherwise = "The balanced virtual postings' sum should be 0 but is: " ++ showMixedAmountOneLine bvsumcost | otherwise = "The balanced virtual postings' sum should be 0 but is: " ++ showMixedAmountOneLine bvsumcost
-- | Legacy form of transactionCheckBalanced. -- | Legacy form of transactionCheckBalanced.
@ -159,22 +159,34 @@ balanceTransactionHelper bopts t = do
[] -> Right (txnTieKnot t', inferredamtsandaccts) [] -> Right (txnTieKnot t', inferredamtsandaccts)
errs -> Left $ transactionBalanceError t' errs' errs -> Left $ transactionBalanceError t' errs'
where where
errs' = errs ++ ismulticommodity = (length $ transactionCommodities t') > 1
[ "Inference of conversion costs has been disallowed." errs' =
[ "Automatic commodity conversion is not enabled."
| ismulticommodity && not (infer_transaction_prices_ bopts) | ismulticommodity && not (infer_transaction_prices_ bopts)
] ++
errs ++
if ismulticommodity
then
[ "Consider adjusting this entry's amounts, adding missing postings,"
, "or recording conversion price(s) with @, @@ or equity postings."
] ]
where else
ismulticommodity = (length $ mconcat $ map (maCommodities . pamount) $ tpostings t') > 1 [ "Consider adjusting this entry's amounts, or adding missing postings."
]
transactionCommodities :: Transaction -> S.Set CommoditySymbol
transactionCommodities t = mconcat $ map (maCommodities . pamount) $ tpostings t
-- | Generate a transaction balancing error message, given the transaction -- | Generate a transaction balancing error message, given the transaction
-- and one or more suberror messages. -- and one or more suberror messages.
transactionBalanceError :: Transaction -> [String] -> String transactionBalanceError :: Transaction -> [String] -> String
transactionBalanceError t errs = printf "%s:\n%s\n\n%s\n%s" transactionBalanceError t errs = printf "%s:\n%s\n\nThis %stransaction is unbalanced.\n%s"
(sourcePosPairPretty $ tsourcepos t) (sourcePosPairPretty $ tsourcepos t)
(textChomp ex) (textChomp ex)
("This transaction is unbalanced."::String) (if ismulticommodity then "multi-commodity " else "" :: String)
(chomp $ unlines errs) (chomp $ unlines errs)
where where
ismulticommodity = (length $ transactionCommodities t) > 1
(_f,_l,_mcols,ex) = makeTransactionErrorExcerpt t finderrcols (_f,_l,_mcols,ex) = makeTransactionErrorExcerpt t finderrcols
where where
finderrcols _ = Nothing finderrcols _ = Nothing
@ -591,12 +603,16 @@ checkBalanceAssertionOneCommodityB p@Posting{paccount=assertedacct} assertedamt
-- "display precision: %d", -- "display precision: %d",
"this balance was asserted: %s", -- (at display precision: %s)", "this balance was asserted: %s", -- (at display precision: %s)",
"but the actual balance is: %s", -- (at display precision: %s)", "but the actual balance is: %s", -- (at display precision: %s)",
"a difference of: %s" "a difference of: %s",
"",
"Consider viewing this account's register to troubleshoot. Eg:",
"",
"hledger reg -I '%s'%s"
]) ])
(sourcePosPretty pos) (sourcePosPretty pos)
(textChomp ex) (textChomp ex)
-- (showDate $ postingDate p) -- (showDate $ postingDate p)
(T.unpack $ paccount p) -- XXX pack acct
(if isinclusive then " (including subaccounts)" else "" :: String) (if isinclusive then " (including subaccounts)" else "" :: String)
assertedcomm assertedcomm
(if istotal then " (no other commodity balance allowed)" else "" :: String) (if istotal then " (no other commodity balance allowed)" else "" :: String)
@ -606,7 +622,10 @@ checkBalanceAssertionOneCommodityB p@Posting{paccount=assertedacct} assertedamt
(show $ aquantity actualbalincomm) (show $ aquantity actualbalincomm)
-- (showAmount actualbalincommodity) -- (showAmount actualbalincommodity)
(show $ aquantity assertedamt - aquantity actualbalincomm) (show $ aquantity assertedamt - aquantity actualbalincomm)
(acct ++ if isinclusive then "" else "$")
(if istotal then "" else (" cur:'"++T.unpack assertedcomm++"'"))
where where
acct = T.unpack $ paccount p
ass = fromJust $ pbalanceassertion p -- PARTIAL: fromJust won't fail, there is a balance assertion ass = fromJust $ pbalanceassertion p -- PARTIAL: fromJust won't fail, there is a balance assertion
pos = baposition ass pos = baposition ass
(_,_,_,ex) = makePostingErrorExcerpt p finderrcols (_,_,_,ex) = makePostingErrorExcerpt p finderrcols
@ -648,7 +667,7 @@ checkBalanceAssignmentPostingDateB :: Posting -> Balancing s ()
checkBalanceAssignmentPostingDateB p = checkBalanceAssignmentPostingDateB p =
when (hasBalanceAssignment p && isJust (pdate p)) $ when (hasBalanceAssignment p && isJust (pdate p)) $
throwError $ chomp $ unlines [ throwError $ chomp $ unlines [
"can't use balance assignment with custom posting date" "Balance assignments and custom posting dates may not be combined."
,"" ,""
,chomp1 $ T.unpack $ maybe (T.unlines $ showPostingLines p) showTransaction $ ptransaction p ,chomp1 $ T.unpack $ maybe (T.unlines $ showPostingLines p) showTransaction $ ptransaction p
,"Balance assignments may not be used on postings with a custom posting date" ,"Balance assignments may not be used on postings with a custom posting date"
@ -664,7 +683,7 @@ checkBalanceAssignmentUnassignableAccountB p = do
unassignable <- R.asks bsUnassignable unassignable <- R.asks bsUnassignable
when (hasBalanceAssignment p && paccount p `S.member` unassignable) $ when (hasBalanceAssignment p && paccount p `S.member` unassignable) $
throwError $ chomp $ unlines [ throwError $ chomp $ unlines [
"can't use balance assignment with auto postings" "Balance assignments and auto postings may not be combined."
,"" ,""
,chomp1 $ T.unpack $ maybe (T.unlines $ showPostingLines p) (showTransaction) $ ptransaction p ,chomp1 $ T.unpack $ maybe (T.unlines $ showPostingLines p) (showTransaction) $ ptransaction p
,"Balance assignments may not be used on accounts affected by auto posting rules" ,"Balance assignments may not be used on accounts affected by auto posting rules"

View File

@ -40,8 +40,16 @@ journalCheckAccounts j = mapM_ checkacct (journalPostings j)
where where
checkacct p@Posting{paccount=a} checkacct p@Posting{paccount=a}
| a `elem` journalAccountNamesDeclared j = Right () | a `elem` journalAccountNamesDeclared j = Right ()
| otherwise = Left $ | otherwise = Left $ printf (unlines [
printf "%s:%d:\n%sundeclared account \"%s\"\n" f l ex a "%s:%d:"
,"%s"
,"Strict account checking is enabled, and"
,"account %s has not been declared."
,"Consider adding an account directive. Examples:"
,""
,"account %s"
,"account %s ; type:A ; (L,E,R,X,C,V)"
]) f l ex (show a) a a
where where
(f,l,_mcols,ex) = makePostingErrorExcerpt p finderrcols (f,l,_mcols,ex) = makePostingErrorExcerpt p finderrcols
-- Calculate columns suitable for highlighting the excerpt. -- Calculate columns suitable for highlighting the excerpt.
@ -61,7 +69,16 @@ journalCheckCommodities j = mapM_ checkcommodities (journalPostings j)
case findundeclaredcomm p of case findundeclaredcomm p of
Nothing -> Right () Nothing -> Right ()
Just (comm, _) -> Just (comm, _) ->
Left $ printf "%s:%d:\n%sundeclared commodity \"%s\"\n" f l ex comm Left $ printf (unlines [
"%s:%d:"
,"%s"
,"Strict commodity checking is enabled, and"
,"commodity %s has not been declared."
,"Consider adding a commodity directive. Examples:"
,""
,"commodity %s1000.00"
,"commodity 1.000,00 %s"
]) f l ex (show comm) comm comm
where where
(f,l,_mcols,ex) = makePostingErrorExcerpt p finderrcols (f,l,_mcols,ex) = makePostingErrorExcerpt p finderrcols
where where
@ -122,7 +139,15 @@ journalCheckPayees j = mapM_ checkpayee (jtxns j)
checkpayee t checkpayee t
| payee `elem` journalPayeesDeclared j = Right () | payee `elem` journalPayeesDeclared j = Right ()
| otherwise = Left $ | otherwise = Left $
printf "%s:%d:\n%sundeclared payee \"%s\"\n" f l ex payee printf (unlines [
"%s:%d:"
,"%s"
,"Strict payee checking is enabled, and"
,"payee %s has not been declared."
,"Consider adding a payee directive. Examples:"
,""
,"payee %s"
]) f l ex (show payee) payee
where where
payee = transactionPayee t payee = transactionPayee t
(f,l,_mcols,ex) = makeTransactionErrorExcerpt t finderrcols (f,l,_mcols,ex) = makeTransactionErrorExcerpt t finderrcols

View File

@ -6,10 +6,12 @@ where
import Control.Monad (forM) import Control.Monad (forM)
import Data.List (groupBy) import Data.List (groupBy)
import Text.Printf (printf) import Text.Printf (printf)
import qualified Data.Text as T (pack, unlines)
import Hledger.Data.Errors (makeTransactionErrorExcerpt) import Hledger.Data.Errors (makeTransactionErrorExcerpt)
import Hledger.Data.Transaction (transactionFile, transactionDateOrDate2) import Hledger.Data.Transaction (transactionFile, transactionDateOrDate2)
import Hledger.Data.Types import Hledger.Data.Types
import Hledger.Utils (textChomp)
journalCheckOrdereddates :: WhichDate -> Journal -> Either String () journalCheckOrdereddates :: WhichDate -> Journal -> Either String ()
journalCheckOrdereddates whichdate j = do journalCheckOrdereddates whichdate j = do
@ -25,13 +27,17 @@ journalCheckOrdereddates whichdate j = do
FoldAcc{fa_previous=Nothing} -> Right () FoldAcc{fa_previous=Nothing} -> Right ()
FoldAcc{fa_error=Nothing} -> Right () FoldAcc{fa_error=Nothing} -> Right ()
FoldAcc{fa_error=Just t, fa_previous=Just tprev} -> Left $ printf FoldAcc{fa_error=Just t, fa_previous=Just tprev} -> Left $ printf
"%s:%d:\n%stransaction date%s is out of order with previous transaction date %s" ("%s:%d:\n%s\nOrdered dates checking is enabled, and this transaction's\n"
f l ex datenum tprevdate ++ "date%s (%s) is out of order with the previous transaction.\n"
++ "Consider moving this entry into date order, or adjusting its date.")
f l ex datenum (show $ getdate t)
where where
(f,l,_mcols,ex) = makeTransactionErrorExcerpt t finderrcols (_,_,_,ex1) = makeTransactionErrorExcerpt tprev (const Nothing)
(f,l,_,ex2) = makeTransactionErrorExcerpt t finderrcols
-- separate the two excerpts by a space-beginning line to help flycheck-hledger parse them
ex = T.unlines [textChomp ex1, T.pack " ", textChomp ex2]
finderrcols _t = Just (1, Just 10) finderrcols _t = Just (1, Just 10)
datenum = if whichdate==SecondaryDate then "2" else "" datenum = if whichdate==SecondaryDate then "2" else ""
tprevdate = show $ getdate tprev
data FoldAcc a b = FoldAcc data FoldAcc a b = FoldAcc
{ fa_error :: Maybe a { fa_error :: Maybe a

View File

@ -17,6 +17,7 @@ import Hledger.Data.Errors (makePostingErrorExcerpt)
import Hledger.Data.Journal (journalPostings, journalAccountNamesUsed) import Hledger.Data.Journal (journalPostings, journalAccountNamesUsed)
import Hledger.Data.Posting (isVirtual) import Hledger.Data.Posting (isVirtual)
import Hledger.Data.Types import Hledger.Data.Types
import Hledger.Utils (chomp)
-- | Check that all the journal's postings are to accounts with a unique leaf name. -- | Check that all the journal's postings are to accounts with a unique leaf name.
-- Otherwise, return an error message for the first offending posting. -- Otherwise, return an error message for the first offending posting.
@ -46,9 +47,13 @@ checkposting :: [(Text,[AccountName])] -> Posting -> Either String ()
checkposting leafandfullnames p@Posting{paccount=a} = checkposting leafandfullnames p@Posting{paccount=a} =
case [lf | lf@(_,fs) <- leafandfullnames, a `elem` fs] of case [lf | lf@(_,fs) <- leafandfullnames, a `elem` fs] of
[] -> Right () [] -> Right ()
(leaf,fulls):_ -> Left $ printf (leaf,fulls):_ -> Left $ chomp $ printf
"%s:%d:\n%saccount leaf name \"%s\" is not unique\nit is used in account names: %s" ("%s:%d:\n%s\nChecking for unique account leaf names is enabled, and\n"
f l ex leaf accts ++"account leaf name %s is not unique.\n"
++"It appears in these account names:\n%s"
++"\nConsider changing these account names so their last parts are different."
)
f l ex (show leaf) accts
where where
-- t = fromMaybe nulltransaction ptransaction -- XXX sloppy -- t = fromMaybe nulltransaction ptransaction -- XXX sloppy
(f,l,_mcols,ex) = makePostingErrorExcerpt p finderrcols (f,l,_mcols,ex) = makePostingErrorExcerpt p finderrcols
@ -59,4 +64,4 @@ checkposting leafandfullnames p@Posting{paccount=a} =
llen = T.length $ accountLeafName a llen = T.length $ accountLeafName a
col = 5 + (if isVirtual p then 1 else 0) + alen - llen col = 5 + (if isVirtual p then 1 else 0) + alen - llen
col2 = col + llen - 1 col2 = col + llen - 1
accts = T.intercalate ", " $ map (("\""<>).(<>"\"")) fulls accts = T.unlines fulls -- $ map (("\""<>).(<>"\"")) fulls

View File

@ -498,11 +498,12 @@ datep' mYear = do
let dateStr = show year ++ [sep1] ++ show month ++ [sep2] ++ show day let dateStr = show year ++ [sep1] ++ show month ++ [sep2] ++ show day
when (sep1 /= sep2) $ customFailure $ parseErrorAtRegion startOffset endOffset $ when (sep1 /= sep2) $ customFailure $ parseErrorAtRegion startOffset endOffset $
"invalid date: separators are different, should be the same" "This date is malformed because the separators are different.\n"
++"Please use consistent separators."
case fromGregorianValid year month day of case fromGregorianValid year month day of
Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $ Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $
"well-formed but invalid date: " ++ dateStr "This date is invalid, please correct it: " ++ dateStr
Just date -> pure $! date Just date -> pure $! date
partialDate :: Int -> Maybe Year -> Month -> Char -> MonthDay -> TextParser m Day partialDate :: Int -> Maybe Year -> Month -> Char -> MonthDay -> TextParser m Day
@ -512,12 +513,13 @@ datep' mYear = do
Just year -> Just year ->
case fromGregorianValid year month day of case fromGregorianValid year month day of
Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $ Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $
"well-formed but invalid date: " ++ dateStr "This date is invalid, please correct it: " ++ dateStr
Just date -> pure $! date Just date -> pure $! date
where dateStr = show year ++ [sep] ++ show month ++ [sep] ++ show day where dateStr = show year ++ [sep] ++ show month ++ [sep] ++ show day
Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $ Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $
"partial date "++dateStr++" found, but the current year is unknown" "The partial date "++dateStr++" can not be parsed because the current year is unknown.\n"
++"Consider making it a full date, or add a default year directive.\n"
where dateStr = show month ++ [sep] ++ show day where dateStr = show month ++ [sep] ++ show day
{-# INLINABLE datep' #-} {-# INLINABLE datep' #-}

View File

@ -134,7 +134,7 @@ toRegexCI = memo $ \s -> mkRegexErr s (RegexpCI s <$> makeRegexOptsM defaultComp
-- | Make a nice error message for a regexp error. -- | Make a nice error message for a regexp error.
mkRegexErr :: Text -> Maybe a -> Either RegexError a mkRegexErr :: Text -> Maybe a -> Either RegexError a
mkRegexErr s = maybe (Left errmsg) Right mkRegexErr s = maybe (Left errmsg) Right
where errmsg = T.unpack $ "this regular expression could not be compiled: " <> s where errmsg = T.unpack $ "This regular expression is malformed, please correct it:\n" <> s
-- Convert a Regexp string to a compiled Regex, throw an error -- Convert a Regexp string to a compiled Regex, throw an error
toRegex' :: Text -> Regexp toRegex' :: Text -> Regexp

View File

@ -120,8 +120,10 @@ parseErrorAtRegion
-> HledgerParseErrorData -> HledgerParseErrorData
parseErrorAtRegion startOffset endOffset msg = parseErrorAtRegion startOffset endOffset msg =
if startOffset < endOffset if startOffset < endOffset
then ErrorFailAt startOffset endOffset msg then ErrorFailAt startOffset endOffset msg'
else ErrorFailAt startOffset (startOffset+1) msg else ErrorFailAt startOffset (startOffset+1) msg'
where
msg' = "\n" ++ msg
--- * Re-parsing --- * Re-parsing

View File

@ -10,7 +10,7 @@ $ hledger -f- check accounts
2020-01-01 2020-01-01
(a) 1 (a) 1
$ hledger -f- check accounts $ hledger -f- check accounts
>2 /undeclared account "a"/ >2 /account "a" has not been declared/
>=1 >=1
# 3. also fails for forecast accounts # 3. also fails for forecast accounts
@ -20,12 +20,12 @@ account a
a $1 a $1
b b
$ hledger -f- --today 2022-01-01 --forecast check accounts $ hledger -f- --today 2022-01-01 --forecast check accounts
>2 /undeclared account "b"/ >2 /account "b" has not been declared/
>=1 >=1
# 4. also fails in --strict mode # 4. also fails in --strict mode
$ hledger -f- --today 2022-01-01 --forecast --strict bal $ hledger -f- --today 2022-01-01 --forecast --strict bal
>2 /undeclared account "b"/ >2 /account "b" has not been declared/
>=1 >=1
# 5. also fails for auto accounts # 5. also fails for auto accounts
@ -40,10 +40,10 @@ account a
2022-02-01 2022-02-01
$ hledger -f- --auto check accounts $ hledger -f- --auto check accounts
>2 /undeclared account "b"/ >2 /account "b" has not been declared/
>=1 >=1
# 6. also fails in --strict mode # 6. also fails in --strict mode
$ hledger -f- --auto --strict bal $ hledger -f- --auto --strict bal
>2 /undeclared account "b"/ >2 /account "b" has not been declared/
>=1 >=1

View File

@ -10,7 +10,7 @@ $ hledger -f- check commodities
2020-01-01 2020-01-01
(a) $1 (a) $1
$ hledger -f- check commodities $ hledger -f- check commodities
>2 /undeclared commodity "\$"/ >2 /commodity "\$" has not been declared/
>=1 >=1
# 3. But commodityless zero amounts will not fail # 3. But commodityless zero amounts will not fail
@ -27,5 +27,5 @@ $ hledger -f- check commodities
(a) $0 (a) $0
$ hledger -f- check commodities $ hledger -f- check commodities
>2 /undeclared commodity "\$"/ >2 /commodity "\$" has not been declared/
>=1 >=1

View File

@ -12,7 +12,7 @@ $ hledger -f- check ordereddates
2020-01-01 2020-01-01
(a) 1 (a) 1
$ hledger -f- check ordereddates $ hledger -f- check ordereddates
>2 /transaction date is out of order/ >2 /date .*is out of order/
>=1 >=1
# With --date2, it checks secondary dates instead # With --date2, it checks secondary dates instead
@ -26,7 +26,7 @@ $ hledger -f- check ordereddates --date2
2020-01-01=2020-01-03 2020-01-01=2020-01-03
2020-01-02 2020-01-02
$ hledger -f- check ordereddates --date2 $ hledger -f- check ordereddates --date2
>2 /transaction date2 is out of order/ >2 /date2 .*is out of order/
>=1 >=1
# XXX not supported: With a query, only matched transactions' dates are checked. # XXX not supported: With a query, only matched transactions' dates are checked.

View File

@ -9,7 +9,7 @@ $ hledger -f - check payees
< <
2020-01-01 foo 2020-01-01 foo
$ hledger -f - check payees $ hledger -f - check payees
>2 /undeclared payee "foo"/ >2 /payee "foo" has not been declared/
>=1 >=1
# or: # or:
@ -17,5 +17,5 @@ $ hledger -f - check payees
payee foo payee foo
2020-01-01 the payee | foo 2020-01-01 the payee | foo
$ hledger -f - check payees $ hledger -f - check payees
>2 /undeclared payee "the payee"/ >2 /payee "the payee" has not been declared/
>=1 >=1

View File

@ -160,7 +160,7 @@ $ hledger -f- print --auto -x
# 9. # 9.
$ hledger print -f- --auto $ hledger print -f- --auto
>2 /can't use balance assignment with auto postings/ >2 /Balance assignments and auto postings may not be combined/
>=1 >=1

View File

@ -225,7 +225,7 @@ $ hledger -f - stats
b =$-1 ; date:2012/1/1 b =$-1 ; date:2012/1/1
$ hledger -f - stats $ hledger -f - stats
>2 /can't use balance assignment with custom posting date/ >2 /Balance assignments and custom posting dates may not be combined/
>=1 >=1
# 13. Posting Date # 13. Posting Date

View File

@ -5,7 +5,7 @@ hledger -f- print
2010/31/12 x 2010/31/12 x
a 1 a 1
b b
>>>2 /invalid date/ >>>2 /date is invalid/
>>>= 1 >>>= 1
# 2. too-large day # 2. too-large day
hledger -f- print hledger -f- print
@ -13,7 +13,7 @@ hledger -f- print
2010/12/32 x 2010/12/32 x
a 1 a 1
b b
>>>2 /invalid date/ >>>2 /date is invalid/
>>>= 1 >>>= 1
# 3. 29th feb on leap year should be ok # 3. 29th feb on leap year should be ok
hledger -f- print hledger -f- print
@ -33,7 +33,7 @@ hledger -f- print
2001/2/29 x 2001/2/29 x
a 1 a 1
b b
>>>2 /invalid date/ >>>2 /date is invalid/
>>>= 1 >>>= 1
# 5. dates must be followed by whitespace or newline # 5. dates must be followed by whitespace or newline
hledger -f- print hledger -f- print

View File

@ -128,8 +128,10 @@ hledger: Error: -:1-3:
| a 1A | a 1A
| b 1B | b 1B
This transaction is unbalanced. This multi-commodity transaction is unbalanced.
The real postings all have the same sign. The real postings all have the same sign. Consider negating some of them.
Consider adjusting this entry's amounts, adding missing postings,
or recording conversion price(s) with @, @@ or equity postings.
>=1 >=1
# 12. Typical "hledger equity --close" transaction does not trigger sign error. # 12. Typical "hledger equity --close" transaction does not trigger sign error.