@@ -1179,39 +1179,63 @@ and reresolve_module_gpath :
1179
1179
( reresolve_module_gpath env functor_path,
1180
1180
reresolve_module_gpath env argument_path )
1181
1181
| `Module (parent , name ) -> `Module (reresolve_module_gpath env parent, name)
1182
- | `Alias (p1 , `Resolved p2 ) ->
1183
- `Alias
1184
- ( reresolve_module_gpath env p1,
1185
- `Resolved (reresolve_module_gpath env p2) )
1186
1182
| `Alias (p1 , p2 ) ->
1187
1183
let dest' = reresolve_module_gpath env p1 in
1188
- let p2' =
1189
- if
1190
- Odoc_model.Paths.Path.Resolved.Module. is_hidden
1191
- ~weak_canonical_test: false dest'
1192
- then
1193
- let cp2 = Component.Of_Lang. (module_path (empty () ) p2) in
1194
- match
1195
- resolve_module env ~mark_substituted: false ~add_canonical: true cp2
1196
- with
1197
- | Ok (p2' , _ ) ->
1198
- Lang_of. (
1199
- Path. module_ (empty () ) (`Resolved (reresolve_module env p2')))
1200
- | Error _ -> p2
1201
- else p2
1202
- in
1203
- `Alias (dest', p2')
1184
+ if
1185
+ Odoc_model.Paths.Path.Resolved.Module. is_hidden
1186
+ ~weak_canonical_test: false dest'
1187
+ then
1188
+ let cp2 = Component.Of_Lang. (module_path (empty () ) p2) in
1189
+ match
1190
+ resolve_module env ~mark_substituted: false ~add_canonical: true cp2
1191
+ with
1192
+ | Ok (`Alias (_ , _ , Some p3 ), _ ) ->
1193
+ let p = reresolve_module env p3 in
1194
+ Lang_of. (Path. resolved_module (empty () ) p)
1195
+ | _ -> `Alias (dest', p2)
1196
+ else `Alias (dest', p2)
1204
1197
| `Subst (p1 , p2 ) ->
1205
1198
`Subst (reresolve_module_type_gpath env p1, reresolve_module_gpath env p2)
1206
1199
| `Hidden p ->
1207
1200
let p' = reresolve_module_gpath env p in
1208
1201
`Hidden p'
1209
- | `Canonical (p , (`Resolved _ as p2 )) ->
1210
- `Canonical (reresolve_module_gpath env p, p2)
1202
+ | `Canonical (p , `Resolved p2 ) ->
1203
+ `Canonical
1204
+ (reresolve_module_gpath env p, `Resolved (reresolve_module_gpath env p2))
1211
1205
| `Canonical (p , p2 ) ->
1212
1206
`Canonical (reresolve_module_gpath env p, handle_canonical_module env p2)
1213
1207
| `OpaqueModule m -> `OpaqueModule (reresolve_module_gpath env m)
1214
1208
1209
+ and strip_canonical :
1210
+ c :Odoc_model.Paths.Path.Module. t ->
1211
+ Cpath.Resolved. module_ ->
1212
+ Cpath.Resolved. module_ =
1213
+ fun ~c path ->
1214
+ match path with
1215
+ | `Canonical (x , y ) when y = c -> strip_canonical ~c x
1216
+ | `Canonical (x , y ) -> `Canonical (strip_canonical ~c x, y)
1217
+ | `Alias (x , y , z ) -> `Alias (strip_canonical ~c x, y, z)
1218
+ | `Subst (x , y ) -> `Subst (x, strip_canonical ~c y)
1219
+ | `Hidden x -> `Hidden (strip_canonical ~c x)
1220
+ | `OpaqueModule x -> `OpaqueModule (strip_canonical ~c x)
1221
+ | `Substituted x -> `Substituted (strip_canonical ~c x)
1222
+ | `Gpath p -> `Gpath (strip_canonical_gpath ~c p)
1223
+ | `Local _ | `Apply _ | `Module _ -> path
1224
+
1225
+ and strip_canonical_gpath :
1226
+ c :Odoc_model.Paths.Path.Module. t ->
1227
+ Odoc_model.Paths.Path.Resolved.Module. t ->
1228
+ Odoc_model.Paths.Path.Resolved.Module. t =
1229
+ fun ~c path ->
1230
+ match path with
1231
+ | `Canonical (x , y ) when y = c -> strip_canonical_gpath ~c x
1232
+ | `Canonical (x , y ) -> `Canonical (strip_canonical_gpath ~c x, y)
1233
+ | `Alias (x , y ) -> `Alias (strip_canonical_gpath ~c x, y)
1234
+ | `Subst (x , y ) -> `Subst (x, strip_canonical_gpath ~c y)
1235
+ | `Hidden x -> `Hidden (strip_canonical_gpath ~c x)
1236
+ | `OpaqueModule x -> `OpaqueModule (strip_canonical_gpath ~c x)
1237
+ | `Apply _ | `Module _ | `Identifier _ -> path
1238
+
1215
1239
and reresolve_module : Env. t -> Cpath.Resolved. module_ -> Cpath.Resolved. module_
1216
1240
=
1217
1241
fun env path ->
@@ -1223,42 +1247,89 @@ and reresolve_module : Env.t -> Cpath.Resolved.module_ -> Cpath.Resolved.module_
1223
1247
`Apply
1224
1248
(reresolve_module env functor_path, reresolve_module env argument_path)
1225
1249
| `Module (parent , name ) -> `Module (reresolve_parent env parent, name)
1226
- | `Alias (p1 , `Resolved p2 , p3 ) ->
1227
- `Alias (reresolve_module env p1, `Resolved (reresolve_module env p2), p3)
1228
- | `Alias (p1 , p2 , p3 ) ->
1250
+ | `Alias (p1 , p2 , p3opt ) ->
1229
1251
let dest' = reresolve_module env p1 in
1230
1252
if Cpath. is_resolved_module_hidden ~weak_canonical_test: false dest' then
1231
- match
1232
- resolve_module env ~mark_substituted: false ~add_canonical: true p2
1233
- with
1234
- | Ok (`Alias (_ , _ , Some p3 ), _ ) -> reresolve_module env p3
1235
- | _ -> `Alias (dest', p2, p3)
1236
- else `Alias (dest', p2, p3)
1253
+ match p3opt with
1254
+ | Some p3 -> reresolve_module env p3
1255
+ | None -> (
1256
+ match
1257
+ resolve_module env ~mark_substituted: false ~add_canonical: true p2
1258
+ with
1259
+ | Ok (`Alias (_ , _ , Some p3 ), _ ) -> reresolve_module env p3
1260
+ | _ -> `Alias (dest', p2, None ))
1261
+ else `Alias (dest', p2, p3opt)
1237
1262
| `Subst (p1 , p2 ) ->
1238
1263
`Subst (reresolve_module_type env p1, reresolve_module env p2)
1239
1264
| `Hidden p ->
1240
1265
let p' = reresolve_module env p in
1241
1266
`Hidden p'
1242
1267
| `Canonical (p , `Resolved p2 ) ->
1243
- `Canonical (reresolve_module env p, `Resolved p2)
1268
+ let cp2 = Component.Of_Lang. (resolved_module_path (empty () ) p2) in
1269
+ let cp2' = reresolve_module env cp2 in
1270
+ let p2' = Lang_of. (Path. resolved_module (empty () ) cp2') in
1271
+ `Canonical (reresolve_module env p, `Resolved p2')
1244
1272
| `Canonical (p , p2 ) -> (
1245
1273
match handle_canonical_module env p2 with
1246
1274
| `Resolved _ as r -> `Canonical (p, r)
1247
1275
| r -> `Canonical (reresolve_module env p, r))
1248
1276
| `OpaqueModule m -> `OpaqueModule (reresolve_module env m)
1249
1277
1250
1278
and handle_canonical_module_real env p2 =
1251
- let strip_alias : Cpath.Resolved.module_ -> Cpath.Resolved.module_ =
1252
- fun x -> match x with `Alias (_ , _ , Some p ) -> p | _ -> x
1279
+ (* Canonical paths are always fully qualified, but this isn't
1280
+ necessarily good for rendering, as the full path would
1281
+ always be written out whenever a canonical module path is
1282
+ encountered.
1283
+
1284
+ Instead the intent of this code is to try to find the shortest
1285
+ path that still correctly references the canonical module.
1286
+
1287
+ It works by starting with the fully qualified path, e.g.
1288
+ A.B.C.D where A is a root module. It then makes the list
1289
+ of possibilities (A).B.C.D (A.B).C.D (A.B.C).D and (A.B.C.D)
1290
+ where brackets represent the part that's an identifier.
1291
+ It then resolved each one in turn and calculates the
1292
+ identifier of the resolved path. The shortest path that
1293
+ has the same identifier as the fully-qualified path is
1294
+ chosen as the canonical path.
1295
+
1296
+ When doing this, we end up resolving each possibility.
1297
+ Additionally, we need to 'reresolve' - resolve the canonical
1298
+ references - while we're doing this. This is because the
1299
+ parent parts of the resolved path can contain aliases and
1300
+ canonical paths themselves which require resolving in order
1301
+ to check the identifier is the same.
1302
+
1303
+ However, we first need to strip off any alias/canonical paths
1304
+ in the resolved module, as we want the identifier for the
1305
+ module itself, not any aliased module, and the canonical path
1306
+ _ought_ to be the same as the one we're _currently_ resolving
1307
+ anyway, so we'd end up looping forever. Note that it's not
1308
+ sufficient to simply ask not to add on the canonical paths
1309
+ at this point (ie, ~add_canonical=false) as the alias chain
1310
+ may include modules that have already been resolved and hence
1311
+ have canonical constructors in their resolved paths.
1312
+ *)
1313
+
1314
+ (* [strip p] strips the top-level aliases and canonical paths from
1315
+ the path [p]. Any aliases/canonicals in parents are left as is. *)
1316
+ let strip : Cpath.Resolved.module_ -> Cpath.Resolved.module_ =
1317
+ fun x ->
1318
+ match x with `Alias (_ , _ , Some p ) -> strip_canonical ~c: p2 p | _ -> x
1253
1319
in
1320
+
1321
+ (* Resolve the path, then 'reresolve', making sure to strip off the
1322
+ top-level alias and canonicals to avoid looping forever *)
1254
1323
let resolve env p =
1255
1324
resolve_module env ~mark_substituted: false ~add_canonical: false p
1256
- >> = fun (p , m ) -> Ok (strip_alias p , m)
1325
+ >> = fun (p , m ) -> Ok (reresolve_module env (strip p) , m)
1257
1326
in
1327
+
1258
1328
let lang_of cpath =
1259
1329
(Lang_of. (Path. resolved_module (empty () ) cpath)
1260
1330
:> Odoc_model.Paths.Path.Resolved. t)
1261
1331
in
1332
+
1262
1333
let cp2 = Component.Of_Lang. (module_path (empty () ) p2) in
1263
1334
match canonical_helper env resolve lang_of c_mod_poss cp2 with
1264
1335
| None -> p2
@@ -1284,30 +1355,42 @@ and handle_canonical_module_real env p2 =
1284
1355
let expanded =
1285
1356
match m.type_ with
1286
1357
| Component.Module. Alias (_ , Some _ ) -> true
1287
- | Alias (`Resolved p , None) ->
1288
- (* we're an alias - check to see if we're marked as the canonical path.
1289
- If not, check for an alias chain with us as canonical in it... *)
1290
- let rec check m =
1291
- match m.Component.Module. canonical with
1292
- | Some p ->
1293
- p = p2
1294
- (* The canonical path is the same one we're trying to resolve *)
1295
- | None -> (
1296
- match m.type_ with
1297
- | Component.Module. Alias (`Resolved p , _ ) -> (
1298
- match lookup_module ~mark_substituted: false env p with
1299
- | Error _ -> false
1300
- | Ok m ->
1301
- let m = Component.Delayed. get m in
1302
- check m)
1303
- | _ -> false )
1304
- in
1305
- let self_canonical () = check m in
1306
- let hidden =
1307
- Cpath. is_resolved_module_hidden ~weak_canonical_test: true p
1308
- in
1309
- hidden || self_canonical ()
1310
- | Alias (_ , _ ) -> false
1358
+ | Alias (p , None) -> (
1359
+ match
1360
+ resolve_module ~mark_substituted: false ~add_canonical: false env p
1361
+ with
1362
+ | Ok (rp , _ ) ->
1363
+ (* we're an alias - check to see if we're marked as the canonical path.
1364
+ If not, check for an alias chain with us as canonical in it... *)
1365
+ let rec check m =
1366
+ match m.Component.Module. canonical with
1367
+ | Some p ->
1368
+ p = p2
1369
+ (* The canonical path is the same one we're trying to resolve *)
1370
+ | None -> (
1371
+ match m.type_ with
1372
+ | Component.Module. Alias (p , _ ) -> (
1373
+ match
1374
+ resolve_module ~mark_substituted: false
1375
+ ~add_canonical: false env p
1376
+ with
1377
+ | Ok (rp , _ ) -> (
1378
+ match
1379
+ lookup_module ~mark_substituted: false env rp
1380
+ with
1381
+ | Error _ -> false
1382
+ | Ok m ->
1383
+ let m = Component.Delayed. get m in
1384
+ check m)
1385
+ | _ -> false )
1386
+ | _ -> false )
1387
+ in
1388
+ let self_canonical () = check m in
1389
+ let hidden =
1390
+ Cpath. is_resolved_module_hidden ~weak_canonical_test: true rp
1391
+ in
1392
+ hidden || self_canonical ()
1393
+ | _ -> false )
1311
1394
| ModuleType _ -> true
1312
1395
in
1313
1396
let cpath =
0 commit comments