mirror of https://github.com/zachjs/sv2v.git
warn on duplicate modules
This commit is contained in:
parent
576a804d90
commit
aa0a885699
|
|
@ -30,6 +30,7 @@
|
||||||
### Other Enhancements
|
### Other Enhancements
|
||||||
|
|
||||||
* Improved error messages for invalid port or parameter bindings
|
* Improved error messages for invalid port or parameter bindings
|
||||||
|
* Added warning for modules or interfaces defined more than once
|
||||||
* `--write path/to/dir/` can now also be used with `--pass-through`
|
* `--write path/to/dir/` can now also be used with `--pass-through`
|
||||||
|
|
||||||
## v0.0.12
|
## v0.0.12
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,11 @@ import Language.SystemVerilog.AST (AST)
|
||||||
import Language.SystemVerilog.Parser.Lex (lexStr)
|
import Language.SystemVerilog.Parser.Lex (lexStr)
|
||||||
import Language.SystemVerilog.Parser.Parse (parse)
|
import Language.SystemVerilog.Parser.Parse (parse)
|
||||||
import Language.SystemVerilog.Parser.Preprocess (preprocess, annotate, Env, Contents)
|
import Language.SystemVerilog.Parser.Preprocess (preprocess, annotate, Env, Contents)
|
||||||
|
import Language.SystemVerilog.Parser.Tokens (Position)
|
||||||
|
|
||||||
type Output = (FilePath, AST)
|
type Output = (FilePath, AST)
|
||||||
type Strings = Set.Set String
|
type Strings = Set.Set String
|
||||||
|
type Positions = Map.Map String Position
|
||||||
|
|
||||||
data Config = Config
|
data Config = Config
|
||||||
{ cfDefines :: [String]
|
{ cfDefines :: [String]
|
||||||
|
|
@ -36,7 +38,7 @@ data Context = Context
|
||||||
{ ctConfig :: Config
|
{ ctConfig :: Config
|
||||||
, ctEnv :: Env
|
, ctEnv :: Env
|
||||||
, ctUsed :: Strings
|
, ctUsed :: Strings
|
||||||
, ctHave :: Strings
|
, ctHave :: Positions
|
||||||
}
|
}
|
||||||
|
|
||||||
-- parse CLI macro definitions into the internal macro environment format
|
-- parse CLI macro definitions into the internal macro environment format
|
||||||
|
|
@ -70,7 +72,8 @@ parseFiles' context []
|
||||||
then return []
|
then return []
|
||||||
else parseFiles' context possibleFiles
|
else parseFiles' context possibleFiles
|
||||||
where
|
where
|
||||||
missingParts = Set.toList $ ctUsed context Set.\\ ctHave context
|
missingParts = Set.toList $ ctUsed context Set.\\
|
||||||
|
(Map.keysSet $ ctHave context)
|
||||||
libdirs = cfLibraryPaths $ ctConfig context
|
libdirs = cfLibraryPaths $ ctConfig context
|
||||||
lookupLibrary partName = ((partName, ) <$>) <$> lookupLibFile partName
|
lookupLibrary partName = ((partName, ) <$>) <$> lookupLibFile partName
|
||||||
lookupLibFile = liftIO . findFile libdirs . (++ ".sv")
|
lookupLibFile = liftIO . findFile libdirs . (++ ".sv")
|
||||||
|
|
@ -78,7 +81,7 @@ parseFiles' context []
|
||||||
-- load the files, but complain if an expected part is missing
|
-- load the files, but complain if an expected part is missing
|
||||||
parseFiles' context ((part, path) : files) = do
|
parseFiles' context ((part, path) : files) = do
|
||||||
(context', ast) <- parseFile context path
|
(context', ast) <- parseFile context path
|
||||||
let misdirected = not $ null part || Set.member part (ctHave context')
|
let misdirected = not $ null part || Map.member part (ctHave context')
|
||||||
when misdirected $ throwError $
|
when misdirected $ throwError $
|
||||||
"Expected to find module or interface " ++ show part ++ " in file "
|
"Expected to find module or interface " ++ show part ++ " in file "
|
||||||
++ show path ++ " selected from the library path."
|
++ show path ++ " selected from the library path."
|
||||||
|
|
@ -89,9 +92,10 @@ parseFile :: Context -> FilePath -> ExceptT String IO (Context, AST)
|
||||||
parseFile context path = do
|
parseFile context path = do
|
||||||
(context', contents) <- preprocessFile context path
|
(context', contents) <- preprocessFile context path
|
||||||
tokens <- liftEither $ runExcept $ lexStr contents
|
tokens <- liftEither $ runExcept $ lexStr contents
|
||||||
(ast, used, have) <- parse (cfOversizedNumbers config) tokens
|
(ast, used, have') <-
|
||||||
|
parse (cfOversizedNumbers config) (ctHave context) tokens
|
||||||
let context'' = context' { ctUsed = used <> ctUsed context
|
let context'' = context' { ctUsed = used <> ctUsed context
|
||||||
, ctHave = have <> ctHave context }
|
, ctHave = have' }
|
||||||
return (context'', ast)
|
return (context'', ast)
|
||||||
where config = ctConfig context
|
where config = ctConfig context
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import Control.Monad.Except
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
import Data.Maybe (catMaybes, fromMaybe)
|
import Data.Maybe (catMaybes, fromMaybe)
|
||||||
import System.IO (hPutStrLn, stderr)
|
import System.IO (hPutStrLn, stderr)
|
||||||
|
import qualified Data.Map.Strict as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import Language.SystemVerilog.AST
|
import Language.SystemVerilog.AST
|
||||||
import Language.SystemVerilog.Parser.ParseDecl
|
import Language.SystemVerilog.Parser.ParseDecl
|
||||||
|
|
@ -1579,20 +1580,22 @@ data ParseData = ParseData
|
||||||
, pTokens :: [Token]
|
, pTokens :: [Token]
|
||||||
, pOversizedNumbers :: Bool
|
, pOversizedNumbers :: Bool
|
||||||
, pPartsUsed :: Strings
|
, pPartsUsed :: Strings
|
||||||
, pPartsHave :: Strings
|
, pPartsHave :: Positions
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParseState = StateT ParseData (ExceptT String IO)
|
type ParseState = StateT ParseData (ExceptT String IO)
|
||||||
type Strings = Set.Set String
|
type Strings = Set.Set String
|
||||||
|
type Positions = Map.Map String Position
|
||||||
|
|
||||||
parse :: Bool -> [Token] -> ExceptT String IO (AST, Strings, Strings)
|
parse :: Bool -> Positions -> [Token]
|
||||||
parse _ [] = return mempty
|
-> ExceptT String IO (AST, Strings, Positions)
|
||||||
parse oversizedNumbers tokens = do
|
parse _ partsHave [] = return ([], Set.empty, partsHave)
|
||||||
|
parse oversizedNumbers partsHave tokens = do
|
||||||
(ast, finalState) <- runStateT parseMain initialState
|
(ast, finalState) <- runStateT parseMain initialState
|
||||||
return (ast, pPartsUsed finalState, pPartsHave finalState)
|
return (ast, pPartsUsed finalState, pPartsHave finalState)
|
||||||
where
|
where
|
||||||
position = tokenPosition $ head tokens
|
position = tokenPosition $ head tokens
|
||||||
initialState = ParseData position tokens oversizedNumbers mempty mempty
|
initialState = ParseData position tokens oversizedNumbers mempty partsHave
|
||||||
|
|
||||||
positionKeep :: (Token -> ParseState a) -> ParseState a
|
positionKeep :: (Token -> ParseState a) -> ParseState a
|
||||||
positionKeep cont = do
|
positionKeep cont = do
|
||||||
|
|
@ -1885,8 +1888,14 @@ recordPartUsed item = return item
|
||||||
|
|
||||||
recordPartHave :: Identifier -> ParseState ()
|
recordPartHave :: Identifier -> ParseState ()
|
||||||
recordPartHave partName = do
|
recordPartHave partName = do
|
||||||
|
currPos <- gets pPosition
|
||||||
partsHave <- gets pPartsHave
|
partsHave <- gets pPartsHave
|
||||||
let partsHave' = Set.insert partName partsHave
|
case Map.lookup partName partsHave of
|
||||||
modify' $ \s -> s { pPartsHave = partsHave' }
|
Nothing -> do
|
||||||
|
let partsHave' = Map.insert partName currPos partsHave
|
||||||
|
modify' $ \s -> s { pPartsHave = partsHave' }
|
||||||
|
Just prevPos ->
|
||||||
|
parseWarning currPos $ "Redefinition of " ++ show partName
|
||||||
|
++ ". Previously defined at " ++ show prevPos ++ "."
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module top;
|
||||||
|
logic x;
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module top;
|
||||||
|
logic y;
|
||||||
|
endmodule
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
NO_FILES_WARNING="Warning: No input files specified (try \`sv2v --help\`)"
|
NO_FILES_WARNING="Warning: No input files specified (try \`sv2v --help\`)"
|
||||||
INTERFACE_WARNING="Warning: Source includes an interface but the output is empty because there are no modules without any interface ports. Please convert interfaces alongside the modules that instantiate them."
|
INTERFACE_WARNING="Warning: Source includes an interface but the output is empty because there are no modules without any interface ports. Please convert interfaces alongside the modules that instantiate them."
|
||||||
PORT_CONN_ATTR_WARNING="attr.sv:6:11: Warning: Ignored port connection attributes (* foo *)(* bar *)."
|
PORT_CONN_ATTR_WARNING="attr.sv:6:11: Warning: Ignored port connection attributes (* foo *)(* bar *)."
|
||||||
|
DUPLICATE_MODULE_WARNING="module_dupe_2.sv:2:5: Warning: Redefinition of \"top\". Previously defined at module_dupe_1.sv:2:5."
|
||||||
|
|
||||||
test_default() {
|
test_default() {
|
||||||
runAndCapture \
|
runAndCapture \
|
||||||
|
|
@ -28,6 +29,13 @@ test_port_conn_attr() {
|
||||||
assertEquals "stderr should should have warning" "$PORT_CONN_ATTR_WARNING" "$stderr"
|
assertEquals "stderr should should have warning" "$PORT_CONN_ATTR_WARNING" "$stderr"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_duplicate_module() {
|
||||||
|
runAndCapture module_dupe_1.sv module_dupe_2.sv
|
||||||
|
assertTrue "conversion should succeed" $result
|
||||||
|
assertNotNull "stdout should not be empty" "$stdout"
|
||||||
|
assertEquals "stderr should should have warning" "$DUPLICATE_MODULE_WARNING" "$stderr"
|
||||||
|
}
|
||||||
|
|
||||||
no_modules_test() {
|
no_modules_test() {
|
||||||
file=$1
|
file=$1
|
||||||
warning="$2"
|
warning="$2"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue