From 84edbae503cb5f9b86792d8064048c70ab74898a Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Wed, 6 Oct 2021 22:17:41 -0600 Subject: [PATCH] preliminary scoped errors with approximate source location - scoped traversals can now raise errors which contain the path of the current scope and an approximate source location based on preceding trace comments, if available - initially, this new error messaging has only been added for the illegal size cast checks in the TypeOf and Cast conversions - error suite tests can provide a verbose mode expected source location --- CHANGELOG.md | 5 ++++ src/Convert/Cast.hs | 3 +- src/Convert/Scoper.hs | 47 +++++++++++++++++++++++++++++-- src/Convert/TypeOf.hs | 3 +- test/error/run.sh | 4 +++ test/error/size_cast_neg_lit_1.sv | 1 + test/error/size_cast_neg_lit_2.sv | 1 + test/error/size_cast_neg_var_1.sv | 1 + test/error/size_cast_neg_var_2.sv | 1 + test/error/size_cast_x_lit.sv | 1 + test/error/size_cast_x_var.sv | 1 + test/error/size_cast_zero_lit.sv | 1 + test/error/size_cast_zero_var.sv | 1 + 13 files changed, 65 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 457ff36..2a755df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ * Added support for excluding the conversion of unbased unsized literals (e.g., `'1`, `'x`) via `--exclude UnbasedUniszed` +### Other Enhancements + +* Certain errors raised during conversion now also provide hierarchical and + approximate source location information to help locate the error + ## v0.0.9 ### Breaking Changes diff --git a/src/Convert/Cast.hs b/src/Convert/Cast.hs index e7ec996..da1c23e 100644 --- a/src/Convert/Cast.hs +++ b/src/Convert/Cast.hs @@ -115,7 +115,8 @@ convertCastM (Number size) _ _ where maybeInt = numberToInteger size Just int = maybeInt - illegal s = error $ "size cast width " ++ show size ++ " is not " ++ s + illegal = scopedErrorM . msg + msg s = "size cast width " ++ show size ++ " is not " ++ s convertCastM (Number size) (Number value) signed = return $ Number $ numberCast signed (fromIntegral size') value diff --git a/src/Convert/Scoper.hs b/src/Convert/Scoper.hs index 9ffa97b..299e300 100644 --- a/src/Convert/Scoper.hs +++ b/src/Convert/Scoper.hs @@ -57,6 +57,8 @@ module Convert.Scoper , withinProcedureM , procedureLoc , procedureLocM + , scopedError + , scopedErrorM , isLoopVar , isLoopVarM , loopVarDepth @@ -68,7 +70,7 @@ module Convert.Scoper ) where import Control.Monad.State.Strict -import Data.List (findIndices, partition) +import Data.List (findIndices, intercalate, isPrefixOf, partition) import Data.Maybe (isNothing) import qualified Data.Map.Strict as Map @@ -105,6 +107,7 @@ data Scopes a = Scopes , sProcedureLoc :: [Access] , sInjectedItems :: [(Bool, ModuleItem)] , sInjectedDecls :: [Decl] + , sLatestTrace :: String } deriving Show extractMapping :: Scopes a -> Map.Map Identifier a @@ -353,6 +356,26 @@ procedureLocM = gets procedureLoc procedureLoc :: Scopes a -> [Access] procedureLoc = sProcedureLoc +debugLocation :: Scopes a -> String +debugLocation s = + hierarchy ++ + if null latestTrace + then " (use -v to get approximate source location)" + else ", near " ++ latestTrace + where + hierarchy = intercalate "." $ map tierToStr $ sCurrent s + latestTrace = sLatestTrace s + tierToStr :: Tier -> String + tierToStr (Tier "" _) = "" + tierToStr (Tier x "") = x + tierToStr (Tier x y) = x ++ '[' : y ++ "]" + +scopedErrorM :: Monad m => String -> ScoperT a m x +scopedErrorM msg = get >>= flip scopedError msg + +scopedError :: Scopes a -> String -> x +scopedError scopes = error . (++ ", within scope " ++ debugLocation scopes) + isLoopVar :: Scopes a -> Identifier -> Bool isLoopVar scopes x = any matches $ sCurrent scopes where matches = (== x) . tierIndex @@ -411,7 +434,10 @@ runScoperT :: Monad m => ScoperT a m x -> m (x, Scopes a) runScoperT = flip runStateT initialState initialState :: Scopes a -initialState = Scopes [] Map.empty [] [] [] +initialState = Scopes [] Map.empty [] [] [] "" + +tracePrefix :: String +tracePrefix = "Trace: " scopeModuleItem :: forall a m. Monad m @@ -420,7 +446,7 @@ scopeModuleItem -> MapperM (ScoperT a m) GenItem -> MapperM (ScoperT a m) Stmt -> MapperM (ScoperT a m) ModuleItem -scopeModuleItem declMapper moduleItemMapper genItemMapper stmtMapper = +scopeModuleItem declMapperRaw moduleItemMapper genItemMapper stmtMapperRaw = wrappedModuleItemMapper where fullStmtMapper :: Stmt -> ScoperT a m Stmt @@ -438,6 +464,21 @@ scopeModuleItem declMapper moduleItemMapper genItemMapper stmtMapper = then traverseSinglyNestedStmtsM fullStmtMapper stmt' else fullStmtMapper $ Block Seq "" injected [stmt'] + declMapper :: Decl -> ScoperT a m Decl + declMapper decl@(CommentDecl c) = + consumeComment c >> return decl + declMapper decl = declMapperRaw decl + + stmtMapper :: Stmt -> ScoperT a m Stmt + stmtMapper stmt@(CommentStmt c) = + consumeComment c >> return stmt + stmtMapper stmt = stmtMapperRaw stmt + + consumeComment :: String -> ScoperT a m () + consumeComment c = + when (tracePrefix `isPrefixOf` c) $ + modify' $ \s -> s { sLatestTrace = drop (length tracePrefix) c } + -- converts a decl and adds decls injected during conversion declMapper' :: Decl -> ScoperT a m [Decl] declMapper' decl = do diff --git a/src/Convert/TypeOf.hs b/src/Convert/TypeOf.hs index e591436..54fbdd1 100644 --- a/src/Convert/TypeOf.hs +++ b/src/Convert/TypeOf.hs @@ -130,7 +130,8 @@ traverseExprM other = elaborateSizeCast :: Expr -> Expr -> ST Expr elaborateSizeCast (Number size) _ | Just 0 == numberToInteger size = -- special case because zero-width ranges cannot be represented - error $ "size cast width " ++ show size ++ " is not a positive integer" + scopedErrorM $ "size cast width " ++ show size + ++ " is not a positive integer" elaborateSizeCast size value = do t <- typeof value force <- isStringParam value diff --git a/test/error/run.sh b/test/error/run.sh index ff510b0..8b6e476 100755 --- a/test/error/run.sh +++ b/test/error/run.sh @@ -3,6 +3,9 @@ runErrorTest() { extractFlag pattern $1.sv pattern="${flag:-.}" + extractFlag location $1.sv + location="${flag//./\.}" + location="${location:-.}" runAndCapture $1.sv assertFalse "regular conversion should have failed" $result @@ -15,6 +18,7 @@ runErrorTest() { assertNull "verbose stdout should be empty" "$stdout" assertNotNull "verbose stderr should not be empty" "$stderr" assertMatch "verbose error message" "$stderr" "$pattern" + assertMatch "verbose location" "$stderr" "$location[^0-9]" } addTest() { diff --git a/test/error/size_cast_neg_lit_1.sv b/test/error/size_cast_neg_lit_1.sv index ba47846..00117cc 100644 --- a/test/error/size_cast_neg_lit_1.sv +++ b/test/error/size_cast_neg_lit_1.sv @@ -1,4 +1,5 @@ // pattern: size cast width 1'sb1 is not a positive integer +// location: size_cast_neg_lit_1.sv:4:13 module top; initial $display((1'sb1)'(2)); endmodule diff --git a/test/error/size_cast_neg_lit_2.sv b/test/error/size_cast_neg_lit_2.sv index 8e9d3b5..c8b40bd 100644 --- a/test/error/size_cast_neg_lit_2.sv +++ b/test/error/size_cast_neg_lit_2.sv @@ -1,4 +1,5 @@ // pattern: size cast width 2'sb11 is not a positive integer +// location: size_cast_neg_lit_2.sv:4:13 module top; initial $display((2'sb11)'(2)); endmodule diff --git a/test/error/size_cast_neg_var_1.sv b/test/error/size_cast_neg_var_1.sv index 55348b5..8b4d188 100644 --- a/test/error/size_cast_neg_var_1.sv +++ b/test/error/size_cast_neg_var_1.sv @@ -1,4 +1,5 @@ // pattern: size cast width 1'sb1 is not a positive integer +// location: size_cast_neg_var_1.sv:5:13 module top; wire x = 0; initial $display((1'sb1)'(x)); diff --git a/test/error/size_cast_neg_var_2.sv b/test/error/size_cast_neg_var_2.sv index 2aff050..71815ee 100644 --- a/test/error/size_cast_neg_var_2.sv +++ b/test/error/size_cast_neg_var_2.sv @@ -1,4 +1,5 @@ // pattern: size cast width 2'sb11 is not a positive integer +// location: size_cast_neg_var_2.sv:5:13 module top; wire x = 0; initial $display((2'sb11)'(x)); diff --git a/test/error/size_cast_x_lit.sv b/test/error/size_cast_x_lit.sv index da144ae..adc7e10 100644 --- a/test/error/size_cast_x_lit.sv +++ b/test/error/size_cast_x_lit.sv @@ -1,4 +1,5 @@ // pattern: size cast width 1'bx is not an integer +// location: size_cast_x_lit.sv:4:13 module top; initial $display((1'bx)'(2)); endmodule diff --git a/test/error/size_cast_x_var.sv b/test/error/size_cast_x_var.sv index 0ee5464..4662747 100644 --- a/test/error/size_cast_x_var.sv +++ b/test/error/size_cast_x_var.sv @@ -1,4 +1,5 @@ // pattern: size cast width 1'bx is not an integer +// location: size_cast_x_var.sv:5:13 module top; wire x = 0; initial $display((1'bx)'(x)); diff --git a/test/error/size_cast_zero_lit.sv b/test/error/size_cast_zero_lit.sv index d81e911..3d62756 100644 --- a/test/error/size_cast_zero_lit.sv +++ b/test/error/size_cast_zero_lit.sv @@ -1,4 +1,5 @@ // pattern: size cast width 0 is not a positive integer +// location: size_cast_zero_lit.sv:4:13 module top; initial $display((0)'(2)); endmodule diff --git a/test/error/size_cast_zero_var.sv b/test/error/size_cast_zero_var.sv index 840dff7..b7ee219 100644 --- a/test/error/size_cast_zero_var.sv +++ b/test/error/size_cast_zero_var.sv @@ -1,4 +1,5 @@ // pattern: size cast width 0 is not a positive integer +// location: size_cast_zero_var.sv:5:13 module top; wire x = 0; initial $display((0)'(x));