slicec 0.4.0

The Slice parser and other core components for Slice compilers.
Documentation
// Copyright (c) ZeroC, Inc.

mod test_helpers;

mod typealias {

    use crate::test_helpers::*;
    use slicec::diagnostics::{Diagnostic, Error};
    use slicec::grammar::*;
    use slicec::slice_file::Span;
    use test_case::test_case;

    #[test_case("struct S {}", "S"; "structs")]
    #[test_case("enum E { Foo }", "E"; "enums")]
    #[test_case("custom C", "C"; "custom types")]
    #[test_case("", "bool"; "primitives")]
    #[test_case("", "Sequence<bool>"; "sequences")]
    #[test_case("", "Dictionary<bool, bool>"; "dictionaries")]
    #[test_case("typealias T = bool", "T"; "type aliases")]
    #[test_case("", "Result<bool, string>"; "result types")]
    fn can_have_type_alias_of(definition: &str, identifier: &str) {
        // Arrange
        let slice = format!(
            "
                module Test
                {definition}
                typealias Alias = {identifier}
            "
        );

        // Act
        let ast = parse_for_ast(slice);

        // Assert
        let type_alias = ast.find_element::<TypeAlias>("Test::Alias").unwrap();
        type_alias.underlying.definition(); // Panics if the type-alias hasn't been initialized correctly.
    }

    #[test]
    fn can_be_used_as_field() {
        // Arrange
        let slice = "
            module Test
            typealias MyDict = Dictionary<varint32, Sequence<uint8>>
            compact struct S {
                dict: MyDict
            }
        ";

        // Act/Assert
        assert_parses(slice);
    }

    #[test]
    fn can_be_used_as_parameter() {
        // Arrange
        let slice = "
            module Test
            typealias MyDict = Dictionary<varint32, Sequence<uint8>>
            interface I {
                op(dict: MyDict)
            }
        ";

        // Act/Assert
        assert_parses(slice);
    }

    #[test]
    fn is_resolvable() {
        // Arrange
        let slice = "
            module Test
            typealias MyInt = varuint32
        ";

        // Act
        let ast = parse_for_ast(slice);

        // Assert
        let type_alias = ast.find_element::<TypeAlias>("Test::MyInt").unwrap();

        assert_eq!(type_alias.identifier(), "MyInt");
        assert!(matches!(
            type_alias.underlying.concrete_type(),
            Types::Primitive(Primitive::VarUInt32),
        ));
    }

    #[test]
    fn is_resolved_as_the_aliased_type_when_used() {
        // Arrange
        let slice = "
            module Test
            typealias MyInt = varuint32
            compact struct S {
                a: MyInt
            }
        ";

        // Act
        let ast = parse_for_ast(slice);

        // Assert
        let field = ast.find_element::<Field>("Test::S::a").unwrap();

        assert_eq!(field.identifier(), "a");
        assert!(matches!(
            field.data_type.concrete_type(),
            Types::Primitive(Primitive::VarUInt32),
        ));
    }

    #[test]
    fn underlying_type_cannot_be_optional() {
        // Arrange
        let slice = "
            module Test
            typealias Test = bool?
        ";

        // Act
        let diagnostics = parse_for_diagnostics(slice);

        // Assert
        let expected = Diagnostic::from_error(Error::TypeAliasOfOptional)
            .set_span(&Span::new((3, 13).into(), (3, 27).into(), "string-0"))
            .add_note(
                "try removing the trailing `?` modifier from its definition",
                Some(&Span::new((3, 30).into(), (3, 35).into(), "string-0")),
            )
            .add_note(
                "instead of aliasing an optional type directly, try making it optional where you use it",
                None,
            );

        check_diagnostics(diagnostics, [expected]);
    }
}