diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a79c77a2..2808ef48ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ different versioning scheme, following the Haskell community's reader/writer protocols without having to explicitly compute the full cross product. * Add gbc flags to pick which C# files to generate (structs, gRPC, and comm). +* gbc ensures that method names are unique [Issue #381](https://github.com/Microsoft/bond/issues/381) ### C++ ### diff --git a/compiler/src/Language/Bond/Parser.hs b/compiler/src/Language/Bond/Parser.hs index ff59f33fb4..b4abf3d4d9 100755 --- a/compiler/src/Language/Bond/Parser.hs +++ b/compiler/src/Language/Bond/Parser.hs @@ -65,7 +65,7 @@ parseBond :: -> String -- ^ content of a schema file to parse -> FilePath -- ^ path of the file being parsed, used to resolve relative import paths -> ImportResolver -- ^ function to resolve and load imported files - -> IO (Either ParseError Bond) -- ^ function returns 'Bond' which represents the parsed abstract syntax tree + -> IO (Either ParseError Bond) -- ^ function returns 'Bond' which represents the parsed abstract syntax tree -- or 'ParserError' if parsing failed parseBond s c f r = runReaderT (runParserT bond (Symbols [] []) s c) (Environment [] [] f r) @@ -240,8 +240,9 @@ struct = do [] -> return fields' Field {..}:_ -> fail $ "Duplicate definition of the field with ordinal " ++ show fieldOrdinal ++ " and name " ++ show fieldName - where - findDuplicatesBy accessor xs = deleteFirstsBy ((==) `on` accessor) xs (nubBy ((==) `on` accessor) xs) + +findDuplicatesBy :: (Eq b) => (a -> b) -> [a] -> [a] +findDuplicatesBy accessor xs = deleteFirstsBy ((==) `on` accessor) xs (nubBy ((==) `on` accessor) xs) manySortedBy :: (a -> a -> Ordering) -> ParsecT s u m a -> ParsecT s u m [a] manySortedBy = manyAccum . insertBy @@ -410,7 +411,13 @@ service = do local (with params) $ Service namespaces attr name params <$> methods <* optional semi where with params e = e { currentParams = params } - methods = braces $ semiEnd (try event <|> try function) + methods = unique $ braces $ semiEnd (try event <|> try function) + unique p = do + methods' <- p + case findDuplicatesBy methodName methods' of + [] -> return methods' + Function {..}:_ -> fail $ "Duplicate definition of the function with name " ++ show methodName + Event {..}:_ -> fail $ "Duplicate definition of the event with name " ++ show methodName function :: Parser Method function = Function <$> attributes <*> payload <*> identifier <*> input diff --git a/compiler/tests/Main.hs b/compiler/tests/Main.hs index 82e3d44888..13dac813f8 100644 --- a/compiler/tests/Main.hs +++ b/compiler/tests/Main.hs @@ -53,6 +53,7 @@ tests = testGroup "Compiler tests" , testCase "Enum no default value" $ failBadSyntax "Should fail when an enum field has no default value" "enum_no_default" , testCase "Alias default value" $ failBadSyntax "Should fail when underlying default value is of the wrong type" "aliases_default" , testCase "Out of range" $ failBadSyntax "Should fail, out of range for int16" "int_out_of_range" + , testCase "Duplicate method definition in service" $ failBadSyntax "Should fail, method name should be unique" "duplicate_method_service" ] , testGroup "Codegen" [ utilTestGroup, diff --git a/compiler/tests/schema/error/duplicate_method_service.bond b/compiler/tests/schema/error/duplicate_method_service.bond new file mode 100644 index 0000000000..f9590aaf5f --- /dev/null +++ b/compiler/tests/schema/error/duplicate_method_service.bond @@ -0,0 +1,12 @@ +namespace tests + +struct dummy { + 0: int32 count; +} + +// Invalid service definition with two methods with the same name +service Foo +{ + void bar(dummy); + nothing bar(); +} \ No newline at end of file