Play

Check-in [fa15a3e3bd]
Login
Overview
Comment:Qualifier now throws an error if trying to call an unexposed function in a different module.
Timelines: family | ancestors | module-definition
Files: files | file ages | folders
SHA3-256: fa15a3e3bd0cedbd90aed146693c36b2ba3e6a3791c11810d06470f1f5953abe
User & Date: robin.hansen on 2021-05-07 09:07:02
Other Links: branch diff | manifest | tags
Context
2021-05-07
09:07
Qualifier now throws an error if trying to call an unexposed function in a different module. Leaf check-in: fa15a3e3bd user: robin.hansen tags: module-definition
08:41
Store in metadata whether a function is exposed or not. check-in: ecc08d85a0 user: robin.hansen tags: module-definition
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Modified src/Play/Qualifier.elm from [c9b8e2da71] to [f368b70ffb].

629
630
631
632
633
634
635
636

637



638
639
640
641
642
643
644
...
656
657
658
659
660
661
662
663

664



665
666
667
668
669
670
671
                    qualifiedName =
                        String.join "/" [ qualifiedPath, value ]
                in
                case Dict.get qualifiedName config.inProgressAST.words of
                    Nothing ->
                        { acc | qualifiedNodes = Err (UnknownWordRef loc qualifiedName) :: acc.qualifiedNodes }

                    Just _ ->

                        { acc | qualifiedNodes = Ok (Word loc qualifiedName) :: acc.qualifiedNodes }




        Parser.ExternalWord loc path value ->
            let
                normalizedPath =
                    "/" ++ String.join "/" path
            in
            case Dict.get normalizedPath config.externalModules of
................................................................................
                                , value
                                ]
                    in
                    case Dict.get fullReference config.inProgressAST.words of
                        Nothing ->
                            { acc | qualifiedNodes = Err (UnknownWordRef loc fullReference) :: acc.qualifiedNodes }

                        Just _ ->

                            { acc | qualifiedNodes = Ok (Word loc fullReference) :: acc.qualifiedNodes }




        Parser.ConstructType typeName ->
            { acc | qualifiedNodes = Ok (ConstructType (qualifyName config typeName)) :: acc.qualifiedNodes }

        Parser.SetMember typeName memberName ->
            { acc | qualifiedNodes = Ok (SetMember (qualifyName config typeName) memberName) :: acc.qualifiedNodes }








|
>
|
>
>
>







 







|
>
|
>
>
>







629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
                    qualifiedName =
                        String.join "/" [ qualifiedPath, value ]
                in
                case Dict.get qualifiedName config.inProgressAST.words of
                    Nothing ->
                        { acc | qualifiedNodes = Err (UnknownWordRef loc qualifiedName) :: acc.qualifiedNodes }

                    Just word ->
                        if word.metadata.isExposed then
                            { acc | qualifiedNodes = Ok (Word loc qualifiedName) :: acc.qualifiedNodes }

                        else
                            { acc | qualifiedNodes = Err (WordNotExposed loc qualifiedName) :: acc.qualifiedNodes }

        Parser.ExternalWord loc path value ->
            let
                normalizedPath =
                    "/" ++ String.join "/" path
            in
            case Dict.get normalizedPath config.externalModules of
................................................................................
                                , value
                                ]
                    in
                    case Dict.get fullReference config.inProgressAST.words of
                        Nothing ->
                            { acc | qualifiedNodes = Err (UnknownWordRef loc fullReference) :: acc.qualifiedNodes }

                        Just def ->
                            if def.metadata.isExposed then
                                { acc | qualifiedNodes = Ok (Word loc fullReference) :: acc.qualifiedNodes }

                            else
                                { acc | qualifiedNodes = Err (WordNotExposed loc fullReference) :: acc.qualifiedNodes }

        Parser.ConstructType typeName ->
            { acc | qualifiedNodes = Ok (ConstructType (qualifyName config typeName)) :: acc.qualifiedNodes }

        Parser.SetMember typeName memberName ->
            { acc | qualifiedNodes = Ok (SetMember (qualifyName config typeName) memberName) :: acc.qualifiedNodes }

Modified src/Play/Qualifier/Problem.elm from [9fd0a6e7f6] to [9c7d2aaeb2].

8
9
10
11
12
13
14

15
16
17
18
19
20
21
..
44
45
46
47
48
49
50








type Problem
    = UnknownWordRef SourceLocationRange String
    | UnknownTypeRef SourceLocationRange String
    | UnionTypeMatchWithPatterns SourceLocationRange
    | InvalidTypeMatch SourceLocationRange
    | NoSuchMemberOnType SourceLocationRange String String



toString : String -> Problem -> String
toString source problem =
    case problem of
        UnknownWordRef range wordRef ->
            SourceLocation.extractFromString source range
................................................................................
        NoSuchMemberOnType range typeName member ->
            SourceLocation.extractFromString source range
                ++ "\n\n"
                ++ typeName
                ++ " does not have a member called '"
                ++ member
                ++ "'."














>







 







>
>
>
>
>
>
>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58

type Problem
    = UnknownWordRef SourceLocationRange String
    | UnknownTypeRef SourceLocationRange String
    | UnionTypeMatchWithPatterns SourceLocationRange
    | InvalidTypeMatch SourceLocationRange
    | NoSuchMemberOnType SourceLocationRange String String
    | WordNotExposed SourceLocationRange String


toString : String -> Problem -> String
toString source problem =
    case problem of
        UnknownWordRef range wordRef ->
            SourceLocation.extractFromString source range
................................................................................
        NoSuchMemberOnType range typeName member ->
            SourceLocation.extractFromString source range
                ++ "\n\n"
                ++ typeName
                ++ " does not have a member called '"
                ++ member
                ++ "'."

        WordNotExposed range wordRef ->
            SourceLocation.extractFromString source range
                ++ "\n\n"
                ++ "Trying to call '"
                ++ wordRef
                ++ "' but this function is not exposed."

Modified tests/Test/Qualifier.elm from [74c8820178] to [19e5f92462].

5
6
7
8
9
10
11

12
13
14
15
16
17
18
....
1108
1109
1110
1111
1112
1113
1114











1115
1116
1117
1118
1119
1120
1121
....
1673
1674
1675
1676
1677
1678
1679










1680












1681


























































































import Expect
import Play.Data.Builtin as Builtin
import Play.Data.Metadata as Metadata
import Play.Data.SourceLocation exposing (emptyRange)
import Play.Data.Type as Type
import Play.Parser as AST
import Play.Qualifier exposing (..)

import Set
import Test exposing (Test, describe, test)
import Test.Parser.Util as ParserUtil
import Test.Qualifier.Util as QualifierUtil


suite : Test
................................................................................
                    ( name
                    , { name = name
                      , metadata = Metadata.default
                      , implementation =
                            SoloImpl []
                      }
                    )











            in
            [ test "Qualifies word in internal package" <|
                \_ ->
                    let
                        unqualifiedAst =
                            { moduleDefinition = AST.emptyModuleDefinition
                            , types = Dict.empty
................................................................................
                                                , Builtin emptyRange Builtin.Plus
                                                ]
                                      }
                                    ]
                            }
                    in
                    QualifierUtil.expectOutput unqualifiedAst expectedAst










            ]












        ]

































































































>







 







>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
....
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
import Expect
import Play.Data.Builtin as Builtin
import Play.Data.Metadata as Metadata
import Play.Data.SourceLocation exposing (emptyRange)
import Play.Data.Type as Type
import Play.Parser as AST
import Play.Qualifier exposing (..)
import Play.Qualifier.Problem as Problem
import Set
import Test exposing (Test, describe, test)
import Test.Parser.Util as ParserUtil
import Test.Qualifier.Util as QualifierUtil


suite : Test
................................................................................
                    ( name
                    , { name = name
                      , metadata = Metadata.default
                      , implementation =
                            SoloImpl []
                      }
                    )

                dummyWordUnexposed name =
                    ( name
                    , { name = name
                      , metadata =
                            Metadata.default
                                |> Metadata.isExposed False
                      , implementation =
                            SoloImpl []
                      }
                    )
            in
            [ test "Qualifies word in internal package" <|
                \_ ->
                    let
                        unqualifiedAst =
                            { moduleDefinition = AST.emptyModuleDefinition
                            , types = Dict.empty
................................................................................
                                                , Builtin emptyRange Builtin.Plus
                                                ]
                                      }
                                    ]
                            }
                    in
                    QualifierUtil.expectOutput unqualifiedAst expectedAst
            , test "Referencing a function from an internal module which isn't exposed ends in a error" <|
                \_ ->
                    let
                        unqualifiedAst =
                            { moduleDefinition =
                                AST.Defined
                                    { aliases = Dict.empty
                                    , imports =
                                        Dict.fromList
                                            [ ( "internal/mod", [] )
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                      , implementation =
                                            AST.SoloImpl
                                                [ AST.Integer emptyRange 1
                                                , AST.Word emptyRange "value"
                                                ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types = Dict.empty
                            , words =
                                Dict.fromList
                                    [ dummyWordUnexposed "internal/mod/value"
                                    ]
                            }

                        result =
                            run
                                { packageName = ""
                                , modulePath = ""
                                , ast = unqualifiedAst
                                , externalModules =
                                    Dict.fromList
                                        [ ( "/mod", "external/package" ) ]
                                , inProgressAST = inProgressAst
                                }
                    in
                    case result of
                        Ok _ ->
                            Expect.fail "Expected qualification to fail because an unexposed function is called"

                        Err [ Problem.WordNotExposed _ "internal/mod/value" ] ->
                            Expect.pass

                        Err errs ->
                            Expect.fail <| "Qualification failed with unexpected error: " ++ Debug.toString errs
            , test "Referencing a function from an external module which isn't exposed ends in a error" <|
                \_ ->
                    let
                        unqualifiedAst =
                            { moduleDefinition =
                                AST.Defined
                                    { aliases = Dict.empty
                                    , imports =
                                        Dict.fromList
                                            [ ( "/mod", [ "add" ] )
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                      , implementation =
                                            AST.SoloImpl
                                                [ AST.Integer emptyRange 1
                                                , AST.Word emptyRange "add"
                                                ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types = Dict.empty
                            , words =
                                Dict.fromList
                                    [ dummyWordUnexposed "/external/package/mod/add"
                                    ]
                            }

                        result =
                            run
                                { packageName = ""
                                , modulePath = ""
                                , ast = unqualifiedAst
                                , externalModules =
                                    Dict.fromList
                                        [ ( "/mod", "external/package" ) ]
                                , inProgressAST = inProgressAst
                                }
                    in
                    case result of
                        Ok _ ->
                            Expect.fail "Expected qualification to fail because an unexposed function is called"

                        Err [ Problem.WordNotExposed _ "/external/package/mod/add" ] ->
                            Expect.pass

                        Err errs ->
                            Expect.fail <| "Qualification failed with unexpected error: " ++ Debug.toString errs
            ]
        ]