Stabel

Check-in [d888dffff5]
Login
Overview
Comment:Qualifier nearly understands imports, now. A failing test marks what remains of work.
Timelines: family | ancestors | descendants | both | module-definition
Files: files | file ages | folders
SHA3-256: d888dffff5a870c9169213e206c67b8cac4fa24abd363360d80a18f404576b5a
User & Date: robin.hansen on 2021-05-25 19:37:15
Other Links: branch diff | manifest | tags
Context
2021-05-26
09:43
Imported types are now qualified when used in type match. check-in: 655d66871f user: robin.hansen tags: module-definition
2021-05-25
19:37
Qualifier nearly understands imports, now. A failing test marks what remains of work. check-in: d888dffff5 user: robin.hansen tags: module-definition
17:30
Qualifier now fully understands aliasing of types. check-in: cd6f72e996 user: robin.hansen tags: module-definition
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Modified src/Play/Qualifier.elm from [1ea017ec95] to [8c3fd626e6].

166
167
168
169
170
171
172
173
174
175
176

177

178
179


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
...
259
260
261
262
263
264
265






















266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
...
481
482
483
484
485
486
487
488
489
490
491

492

493
494
495
496

497
498
499
500
501
502
503
504
505
506
507
508
...
752
753
754
755
756
757
758





759
760
761
762
763
764
765
766
767
....
1046
1047
1048
1049
1050
1051
1052









































1053
1054
1055
1056
1057
1058
1059
qualifyType :
    RunConfig
    -> Parser.TypeDefinition
    -> ( List Problem, Dict String TypeDefinition )
    -> ( List Problem, Dict String TypeDefinition )
qualifyType config typeDef ( errors, acc ) =
    let
        ( aliases, imports ) =
            case config.ast.moduleDefinition of
                Parser.Undefined ->
                    ( Dict.empty, Dict.empty )



                Parser.Defined mod ->
                    ( mod.aliases, mod.imports )


    in
    case typeDef of
        Parser.CustomTypeDef range name generics members ->
            let
                qualifiedName =
                    qualifyName config name

                qualifiedMemberResult =
                    List.map (Tuple.mapSecond (qualifyMemberType config aliases imports range)) members
                        |> List.map raiseTupleError
                        |> Result.combine

                raiseTupleError ( label, result ) =
                    case result of
                        Ok value ->
                            Ok ( label, value )
................................................................................

        Parser.UnionTypeDef range name generics memberTypes ->
            let
                qualifiedName =
                    qualifyName config name

                qualifiedMemberTypesResult =
                    List.map (qualifyMemberType config aliases imports range) memberTypes
                        |> Result.combine
            in
            case qualifiedMemberTypesResult of
                Err err ->
                    ( err :: errors
                    , acc
                    )
................................................................................
                    ( errors
                    , Dict.insert qualifiedName (UnionTypeDef qualifiedName range generics qualifiedMemberTypes) acc
                    )


qualifyMemberType :
    RunConfig
    -> Dict String String
    -> Dict String (List String)
    -> SourceLocationRange
    -> Parser.PossiblyQualifiedType
    -> Result Problem Type
qualifyMemberType config aliases imports range type_ =
    let
        internalRefLookup path name binds =
            let
                qualifiedName =
                    path
                        ++ [ name ]
                        |> String.join "/"
                        |> qualifyPackageModule config.packageName

                bindResult =
                    binds
                        |> List.map (qualifyMemberType config aliases imports range)
                        |> Result.combine
            in
            case ( Dict.get qualifiedName config.inProgressAST.types, bindResult ) of
                ( Just (CustomTypeDef _ _ [] _), _ ) ->
                    Ok <| Type.Custom qualifiedName

                ( Just (CustomTypeDef _ _ _ _), Ok qualifiedBinds ) ->
................................................................................
                    Ok <| Type.CustomGeneric qualifiedName qualifiedBinds

                ( Just (UnionTypeDef _ _ _ memberTypes), _ ) ->
                    Ok <| Type.Union memberTypes

                _ ->
                    Err <| UnknownTypeRef range qualifiedName






















    in
    case type_ of
        Parser.LocalRef "Int" [] ->
            Ok <| Type.Int

        Parser.LocalRef name [] ->
            case Dict.get name config.ast.types of
                Just _ ->
                    Ok <| Type.Custom (qualifyName config name)

                Nothing ->
                    Err <| UnknownTypeRef range name

        Parser.LocalRef name binds ->
            case Dict.get name config.ast.types of
                Just _ ->
                    let
                        bindResult =
                            binds
                                |> List.map (qualifyMemberType config aliases imports range)
                                |> Result.combine
                    in
                    case bindResult of
                        Ok convertedBindings ->
                            Ok <|
                                Type.CustomGeneric
                                    (qualifyName config name)
                                    convertedBindings

                        Err err ->
                            Err err

                Nothing ->
                    Err <| UnknownTypeRef range name

        Parser.InternalRef ([ possibleAlias ] as path) name binds ->
            case Dict.get possibleAlias aliases of
                Just val ->
                    if String.startsWith "/" val then
                        let
                            newPath =
                                val
                                    |> String.split "/"
                                    |> List.drop 1
                        in
                        qualifyMemberType config aliases imports range <|
                            Parser.ExternalRef newPath name binds

                    else
                        internalRefLookup (String.split "/" val) name binds

                Nothing ->
                    internalRefLookup path name binds
................................................................................
                qualifiedName =
                    Dict.get pathString config.externalModules
                        |> Maybe.map (\prefix -> "/" ++ prefix ++ pathString ++ "/" ++ name)
                        |> Maybe.withDefault ""

                bindResult =
                    binds
                        |> List.map (qualifyMemberType config aliases imports range)
                        |> Result.combine
            in
            case ( Dict.get qualifiedName config.inProgressAST.types, bindResult ) of
                ( Just (CustomTypeDef _ _ [] _), _ ) ->
                    Ok <| Type.Custom qualifiedName

                ( Just (CustomTypeDef _ _ _ _), Ok qualifiedBinds ) ->
................................................................................
        Parser.StackRange sym ->
            Ok (Type.Generic sym)

        Parser.QuotationType sign ->
            let
                inputResult =
                    sign.input
                        |> List.map (qualifyMemberType config aliases imports range)
                        |> Result.combine

                outputResult =
                    sign.output
                        |> List.map (qualifyMemberType config aliases imports range)
                        |> Result.combine
            in
            case ( inputResult, outputResult ) of
                ( Ok input, Ok output ) ->
                    Ok <|
                        Type.Quotation
                            { input = input
................................................................................

                Parser.UserProvided wt ->
                    List.length wt.input

                Parser.Verified wt ->
                    List.length wt.input

        ( aliases, imports ) =
            case config.ast.moduleDefinition of
                Parser.Undefined ->
                    ( word.aliases, word.imports )



                Parser.Defined mod ->
                    ( Dict.union mod.aliases word.aliases
                    , Dict.union mod.imports word.imports
                    )

    in
    Parser.typeSignatureToMaybe word.typeSignature
        |> Maybe.map (\ts -> ts.input ++ ts.output)
        |> Maybe.withDefault []
        |> List.map (qualifyMemberType config aliases imports wordRange)
        |> Result.combine
        |> Result.map
            (\qualifiedFlatTypeSignature ->
                let
                    wordType =
                        { input = List.take inputLength qualifiedFlatTypeSignature
                        , output = List.drop inputLength qualifiedFlatTypeSignature
................................................................................
    if Set.member fieldName memberNames then
        case matchValue of
            Parser.LiteralInt val ->
                Ok <| ( fieldName, LiteralInt val )

            Parser.LiteralType type_ ->
                let





                    qualifyTypeResult =
                        qualifyMemberType config aliases imports range type_
                in
                case qualifyTypeResult of
                    Ok qualifiedType ->
                        Ok <| ( fieldName, LiteralType qualifiedType )

                    Err err ->
                        Err err
................................................................................
        Nothing ->
            potentialCandidates
                |> List.map (\( mod, qName ) -> ( mod, Dict.get qName config.inProgressAST.words ))
                |> List.filter (\( _, possibleDef ) -> possibleDef /= Nothing)
                |> List.head
                |> Maybe.map Tuple.first












































-- Dependant modules


type alias RequiredModulesConfig =
    { packageName : String







|


|
>
|
>

|
>
>








|







 







|







 







|
<



|











|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











|







|













|


|








|







 







|







 







|




|







 







|


|
>
|
>

|
|
<
>




|







 







>
>
>
>
>

|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
...
231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522

523
524
525
526
527
528
529
530
531
532
533
534
535
...
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
....
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
qualifyType :
    RunConfig
    -> Parser.TypeDefinition
    -> ( List Problem, Dict String TypeDefinition )
    -> ( List Problem, Dict String TypeDefinition )
qualifyType config typeDef ( errors, acc ) =
    let
        modRefs =
            case config.ast.moduleDefinition of
                Parser.Undefined ->
                    { aliases = Dict.empty
                    , imports = Dict.empty
                    }

                Parser.Defined mod ->
                    { aliases = mod.aliases
                    , imports = mod.imports
                    }
    in
    case typeDef of
        Parser.CustomTypeDef range name generics members ->
            let
                qualifiedName =
                    qualifyName config name

                qualifiedMemberResult =
                    List.map (Tuple.mapSecond (qualifyMemberType config modRefs range)) members
                        |> List.map raiseTupleError
                        |> Result.combine

                raiseTupleError ( label, result ) =
                    case result of
                        Ok value ->
                            Ok ( label, value )
................................................................................

        Parser.UnionTypeDef range name generics memberTypes ->
            let
                qualifiedName =
                    qualifyName config name

                qualifiedMemberTypesResult =
                    List.map (qualifyMemberType config modRefs range) memberTypes
                        |> Result.combine
            in
            case qualifiedMemberTypesResult of
                Err err ->
                    ( err :: errors
                    , acc
                    )
................................................................................
                    ( errors
                    , Dict.insert qualifiedName (UnionTypeDef qualifiedName range generics qualifiedMemberTypes) acc
                    )


qualifyMemberType :
    RunConfig
    -> ModuleReferences

    -> SourceLocationRange
    -> Parser.PossiblyQualifiedType
    -> Result Problem Type
qualifyMemberType config modRefs range type_ =
    let
        internalRefLookup path name binds =
            let
                qualifiedName =
                    path
                        ++ [ name ]
                        |> String.join "/"
                        |> qualifyPackageModule config.packageName

                bindResult =
                    binds
                        |> List.map (qualifyMemberType config modRefs range)
                        |> Result.combine
            in
            case ( Dict.get qualifiedName config.inProgressAST.types, bindResult ) of
                ( Just (CustomTypeDef _ _ [] _), _ ) ->
                    Ok <| Type.Custom qualifiedName

                ( Just (CustomTypeDef _ _ _ _), Ok qualifiedBinds ) ->
................................................................................
                    Ok <| Type.CustomGeneric qualifiedName qualifiedBinds

                ( Just (UnionTypeDef _ _ _ memberTypes), _ ) ->
                    Ok <| Type.Union memberTypes

                _ ->
                    Err <| UnknownTypeRef range qualifiedName

        importsLookup name binds =
            case resolveImportedType config modRefs name of
                Just importedModule ->
                    if String.startsWith "/" importedModule then
                        let
                            nextPath =
                                importedModule
                                    |> String.dropLeft 1
                                    |> String.split "/"
                                    -- Drop author/packageName part
                                    |> List.drop 2
                        in
                        qualifyMemberType config modRefs range <|
                            Parser.ExternalRef nextPath name binds

                    else
                        qualifyMemberType config modRefs range <|
                            Parser.InternalRef (String.split "/" importedModule) name binds

                Nothing ->
                    Err <| UnknownTypeRef range name
    in
    case type_ of
        Parser.LocalRef "Int" [] ->
            Ok <| Type.Int

        Parser.LocalRef name [] ->
            case Dict.get name config.ast.types of
                Just _ ->
                    Ok <| Type.Custom (qualifyName config name)

                Nothing ->
                    importsLookup name []

        Parser.LocalRef name binds ->
            case Dict.get name config.ast.types of
                Just _ ->
                    let
                        bindResult =
                            binds
                                |> List.map (qualifyMemberType config modRefs range)
                                |> Result.combine
                    in
                    case bindResult of
                        Ok convertedBindings ->
                            Ok <|
                                Type.CustomGeneric
                                    (qualifyName config name)
                                    convertedBindings

                        Err err ->
                            Err err

                Nothing ->
                    importsLookup name binds

        Parser.InternalRef ([ possibleAlias ] as path) name binds ->
            case Dict.get possibleAlias modRefs.aliases of
                Just val ->
                    if String.startsWith "/" val then
                        let
                            newPath =
                                val
                                    |> String.split "/"
                                    |> List.drop 1
                        in
                        qualifyMemberType config modRefs range <|
                            Parser.ExternalRef newPath name binds

                    else
                        internalRefLookup (String.split "/" val) name binds

                Nothing ->
                    internalRefLookup path name binds
................................................................................
                qualifiedName =
                    Dict.get pathString config.externalModules
                        |> Maybe.map (\prefix -> "/" ++ prefix ++ pathString ++ "/" ++ name)
                        |> Maybe.withDefault ""

                bindResult =
                    binds
                        |> List.map (qualifyMemberType config modRefs range)
                        |> Result.combine
            in
            case ( Dict.get qualifiedName config.inProgressAST.types, bindResult ) of
                ( Just (CustomTypeDef _ _ [] _), _ ) ->
                    Ok <| Type.Custom qualifiedName

                ( Just (CustomTypeDef _ _ _ _), Ok qualifiedBinds ) ->
................................................................................
        Parser.StackRange sym ->
            Ok (Type.Generic sym)

        Parser.QuotationType sign ->
            let
                inputResult =
                    sign.input
                        |> List.map (qualifyMemberType config modRefs range)
                        |> Result.combine

                outputResult =
                    sign.output
                        |> List.map (qualifyMemberType config modRefs range)
                        |> Result.combine
            in
            case ( inputResult, outputResult ) of
                ( Ok input, Ok output ) ->
                    Ok <|
                        Type.Quotation
                            { input = input
................................................................................

                Parser.UserProvided wt ->
                    List.length wt.input

                Parser.Verified wt ->
                    List.length wt.input

        modRefs =
            case config.ast.moduleDefinition of
                Parser.Undefined ->
                    { aliases = word.aliases
                    , imports = word.imports
                    }

                Parser.Defined mod ->
                    { aliases = Dict.union mod.aliases word.aliases
                    , imports = Dict.union mod.imports word.imports

                    }
    in
    Parser.typeSignatureToMaybe word.typeSignature
        |> Maybe.map (\ts -> ts.input ++ ts.output)
        |> Maybe.withDefault []
        |> List.map (qualifyMemberType config modRefs wordRange)
        |> Result.combine
        |> Result.map
            (\qualifiedFlatTypeSignature ->
                let
                    wordType =
                        { input = List.take inputLength qualifiedFlatTypeSignature
                        , output = List.drop inputLength qualifiedFlatTypeSignature
................................................................................
    if Set.member fieldName memberNames then
        case matchValue of
            Parser.LiteralInt val ->
                Ok <| ( fieldName, LiteralInt val )

            Parser.LiteralType type_ ->
                let
                    modRefs =
                        { aliases = aliases
                        , imports = imports
                        }

                    qualifyTypeResult =
                        qualifyMemberType config modRefs range type_
                in
                case qualifyTypeResult of
                    Ok qualifiedType ->
                        Ok <| ( fieldName, LiteralType qualifiedType )

                    Err err ->
                        Err err
................................................................................
        Nothing ->
            potentialCandidates
                |> List.map (\( mod, qName ) -> ( mod, Dict.get qName config.inProgressAST.words ))
                |> List.filter (\( _, possibleDef ) -> possibleDef /= Nothing)
                |> List.head
                |> Maybe.map Tuple.first


resolveImportedType : RunConfig -> ModuleReferences -> String -> Maybe String
resolveImportedType config modRefs name =
    let
        explicitImports =
            modRefs.imports
                |> Dict.toList
                |> List.find (\( _, v ) -> List.member name v)
                |> Maybe.map Tuple.first
                |> Maybe.andThen resolveMod

        potentialCandidates =
            modRefs.imports
                |> Dict.filter (\_ v -> List.isEmpty v)
                |> Dict.keys
                |> List.filterMap resolveMod
                |> List.map (\mod -> ( mod, mod ++ "/" ++ name ))

        resolveMod mod =
            if String.startsWith "/" mod then
                Dict.get mod config.externalModules
                    |> Maybe.map
                        (\package ->
                            String.dropLeft 1 mod
                                |> qualifyPackageModule package
                        )

            else
                Just <| qualifyPackageModule config.packageName mod
    in
    case explicitImports of
        Just _ ->
            explicitImports

        Nothing ->
            potentialCandidates
                |> List.map (\( mod, qName ) -> ( mod, Dict.get qName config.inProgressAST.types ))
                |> List.filter (\( _, possibleDef ) -> possibleDef /= Nothing)
                |> List.head
                |> Maybe.map Tuple.first



-- Dependant modules


type alias RequiredModulesConfig =
    { packageName : String

Modified tests/Test/Qualifier.elm from [b95204a661] to [fdc23db8aa].

2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096




2097
2098
2099
2100
2101
2102
2103
....
2111
2112
2113
2114
2115
2116
2117



2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129





2130
2131
2132
2133
2134
2135
2136
....
2151
2152
2153
2154
2155
2156
2157
2158




2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
....
2173
2174
2175
2176
2177
2178
2179



2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191





2192
2193
2194
2195
2196
2197



































































































































2198
2199
2200
2201
2202
2203
2204
                    let
                        unqualifiedAst =
                            { moduleDefinition =
                                AST.Defined
                                    { aliases = Dict.empty
                                    , imports =
                                        Dict.fromList
                                            [ ( "/mod", [ "add" ] )
                                            , ( "internal/mod", [] )
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , typeSignature = AST.NotProvided




                                      , sourceLocationRange = Nothing
                                      , aliases = Dict.empty
                                      , imports = Dict.empty
                                      , implementation =
                                            AST.SoloImpl
                                                [ AST.Integer emptyRange 1
                                                , AST.Word emptyRange "value"
................................................................................
                            { types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                                |> Metadata.isExposed False



                                      , implementation =
                                            SoloImpl
                                                [ Integer emptyRange 1
                                                , Word emptyRange "internal/mod/value"
                                                , Word emptyRange "/external/package/mod/add"
                                                ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types = Dict.empty





                            , words =
                                Dict.fromList
                                    [ dummyWord "/external/package/mod/add"
                                    , dummyWord "internal/mod/value"
                                    ]
                            }
                    in
................................................................................
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , typeSignature = AST.NotProvided




                                      , sourceLocationRange = Nothing
                                      , aliases = Dict.empty
                                      , imports = Dict.fromList [ ( "/mod", [ "add" ] ) ]
                                      , implementation =
                                            AST.SoloImpl
                                                [ AST.Integer emptyRange 1
                                                , AST.Word emptyRange "value"
                                                , AST.Word emptyRange "add"
                                                ]
                                      }
................................................................................
                            { types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                                |> Metadata.isExposed False



                                      , implementation =
                                            SoloImpl
                                                [ Integer emptyRange 1
                                                , Word emptyRange "internal/mod/value"
                                                , Word emptyRange "/external/package/mod/add"
                                                ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types = Dict.empty





                            , words =
                                Dict.fromList
                                    [ dummyWord "/external/package/mod/add"
                                    , dummyWord "internal/mod/value"
                                    ]
                            }



































































































































                    in
                    QualifierUtil.expectExternalOutput
                        inProgressAst
                        unqualifiedAst
                        expectedAst
            , test "When module doesn't have a definition, all functions are exposed" <|
                \_ ->







|








|
>
>
>
>







 







>
>
>











|
>
>
>
>
>







 







|
>
>
>
>


|







 







>
>
>











|
>
>
>
>
>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
....
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
....
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
....
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
                    let
                        unqualifiedAst =
                            { moduleDefinition =
                                AST.Defined
                                    { aliases = Dict.empty
                                    , imports =
                                        Dict.fromList
                                            [ ( "/mod", [ "add", "Tipe" ] )
                                            , ( "internal/mod", [] )
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , typeSignature =
                                            AST.UserProvided
                                                { input = [ AST.LocalRef "Tipe" [] ]
                                                , output = [ AST.LocalRef "Tope" [] ]
                                                }
                                      , sourceLocationRange = Nothing
                                      , aliases = Dict.empty
                                      , imports = Dict.empty
                                      , implementation =
                                            AST.SoloImpl
                                                [ AST.Integer emptyRange 1
                                                , AST.Word emptyRange "value"
................................................................................
                            { types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                                |> Metadata.isExposed False
                                                |> Metadata.withType
                                                    [ Type.Custom "/external/package/mod/Tipe" ]
                                                    [ Type.Custom "internal/mod/Tope" ]
                                      , implementation =
                                            SoloImpl
                                                [ Integer emptyRange 1
                                                , Word emptyRange "internal/mod/value"
                                                , Word emptyRange "/external/package/mod/add"
                                                ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types =
                                [ dummyType "/external/package/mod/Tipe"
                                , dummyType "internal/mod/Tope"
                                ]
                                    |> List.concat
                                    |> Dict.fromList
                            , words =
                                Dict.fromList
                                    [ dummyWord "/external/package/mod/add"
                                    , dummyWord "internal/mod/value"
                                    ]
                            }
                    in
................................................................................
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , typeSignature =
                                            AST.UserProvided
                                                { input = [ AST.LocalRef "Tipe" [] ]
                                                , output = [ AST.LocalRef "Tope" [] ]
                                                }
                                      , sourceLocationRange = Nothing
                                      , aliases = Dict.empty
                                      , imports = Dict.fromList [ ( "/mod", [ "add", "Tipe" ] ) ]
                                      , implementation =
                                            AST.SoloImpl
                                                [ AST.Integer emptyRange 1
                                                , AST.Word emptyRange "value"
                                                , AST.Word emptyRange "add"
                                                ]
                                      }
................................................................................
                            { types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                                |> Metadata.isExposed False
                                                |> Metadata.withType
                                                    [ Type.Custom "/external/package/mod/Tipe" ]
                                                    [ Type.Custom "internal/mod/Tope" ]
                                      , implementation =
                                            SoloImpl
                                                [ Integer emptyRange 1
                                                , Word emptyRange "internal/mod/value"
                                                , Word emptyRange "/external/package/mod/add"
                                                ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types =
                                [ dummyType "/external/package/mod/Tipe"
                                , dummyType "internal/mod/Tope"
                                ]
                                    |> List.concat
                                    |> Dict.fromList
                            , words =
                                Dict.fromList
                                    [ dummyWord "/external/package/mod/add"
                                    , dummyWord "internal/mod/value"
                                    ]
                            }
                    in
                    QualifierUtil.expectExternalOutput
                        inProgressAst
                        unqualifiedAst
                        expectedAst
            , test "Type definition imports" <|
                \_ ->
                    let
                        unqualifiedAst =
                            { moduleDefinition =
                                AST.Defined
                                    { aliases = Dict.empty
                                    , imports =
                                        Dict.fromList
                                            [ ( "/mod", [] )
                                            , ( "internal/mod", [ "Tope" ] )
                                            ]
                                    , exposes = Set.empty
                                    }
                            , types =
                                Dict.fromList
                                    [ ( "Tepip"
                                      , AST.CustomTypeDef
                                            emptyRange
                                            "Tepip"
                                            []
                                            [ ( "first", AST.LocalRef "Tipe" [] )
                                            , ( "second", AST.LocalRef "Tope" [] )
                                            ]
                                      )
                                    ]
                            , words = Dict.empty
                            }

                        expectedAst =
                            { types =
                                Dict.fromList
                                    [ ( "Tepip"
                                      , CustomTypeDef
                                            "Tepip"
                                            emptyRange
                                            []
                                            [ ( "first", Type.Custom "/external/package/mod/Tipe" )
                                            , ( "second", Type.Custom "internal/mod/Tope" )
                                            ]
                                      )
                                    ]
                            , words = Dict.empty
                            }

                        inProgressAst =
                            { types =
                                [ dummyType "/external/package/mod/Tipe"
                                , dummyType "internal/mod/Tope"
                                ]
                                    |> List.concat
                                    |> Dict.fromList
                            , words = Dict.empty
                            }
                    in
                    QualifierUtil.expectExternalOutput
                        inProgressAst
                        unqualifiedAst
                        expectedAst
            , test "Module and function imports in type match" <|
                \_ ->
                    let
                        unqualifiedAst =
                            { moduleDefinition =
                                AST.Defined
                                    { aliases = Dict.empty
                                    , imports =
                                        Dict.fromList
                                            [ ( "/mod", [ "Tipe" ] ) ]
                                    , exposes = Set.empty
                                    }
                            , types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , typeSignature = AST.NotProvided
                                      , sourceLocationRange = Nothing
                                      , aliases = Dict.empty
                                      , imports =
                                            Dict.fromList
                                                [ ( "internal/mod", [] ) ]
                                      , implementation =
                                            AST.MultiImpl
                                                [ ( AST.TypeMatch emptyRange (AST.LocalRef "Tipe" []) []
                                                  , [ AST.Word emptyRange "drop" ]
                                                  )
                                                , ( AST.TypeMatch emptyRange (AST.LocalRef "Tope" []) []
                                                  , [ AST.Word emptyRange "drop" ]
                                                  )
                                                ]
                                                [ AST.Word emptyRange "drop" ]
                                      }
                                    ]
                            }

                        expectedAst =
                            { types = Dict.empty
                            , words =
                                Dict.fromListBy .name
                                    [ { name = "external-call"
                                      , metadata =
                                            Metadata.default
                                                |> Metadata.isExposed False
                                      , implementation =
                                            MultiImpl
                                                [ ( TypeMatch emptyRange (Type.Custom "/external/package/mod/Tipe") []
                                                  , [ Builtin emptyRange Builtin.StackDrop ]
                                                  )
                                                , ( TypeMatch emptyRange (Type.Custom "internal/mod/Tope") []
                                                  , [ Builtin emptyRange Builtin.StackDrop ]
                                                  )
                                                ]
                                                [ Builtin emptyRange Builtin.StackDrop ]
                                      }
                                    ]
                            }

                        inProgressAst =
                            { types =
                                [ dummyType "/external/package/mod/Tipe"
                                , dummyType "internal/mod/Tope"
                                ]
                                    |> List.concat
                                    |> Dict.fromList
                            , words = Dict.empty
                            }
                    in
                    QualifierUtil.expectExternalOutput
                        inProgressAst
                        unqualifiedAst
                        expectedAst
            , test "When module doesn't have a definition, all functions are exposed" <|
                \_ ->