@@ -18,7 +18,8 @@ type wferr =
1818 | LoopControlOutsideLoop (string , Location . t )
1919 | ReturnStatementOutsideFunction (Location . t )
2020 | MismatchedReturnStyles (Location . t )
21- | LocalIncludeStatement (Location . t );
21+ | LocalIncludeStatement (Location . t )
22+ | ProvidedMultipleTimes (string , Location . t );
2223
2324exception Error ( wferr) ;
2425
@@ -78,6 +79,12 @@ let prepare_error =
7879 ~loc,
7980 "`include` statements may only appear at the file level." ,
8081 )
82+ | ProvidedMultipleTimes (name , loc ) =>
83+ errorf(
84+ ~loc,
85+ "% s was provided multiple times, but can only be provided once." ,
86+ name,
87+ )
8188 )
8289 );
8390
@@ -557,6 +564,194 @@ let no_local_include = (errs, super) => {
557564 };
558565};
559566
567+ type provided_multiple_times_ctx = {
568+ modules: Hashtbl . t (string , unit ),
569+ types: Hashtbl . t (string , unit ),
570+ values: Hashtbl . t (string , unit ),
571+ };
572+
573+ let provided_multiple_times = (errs, super) => {
574+ let rec extract_bindings = (binds, pattern) =>
575+ switch (pattern. ppat_desc) {
576+ | PPatAny => binds
577+ | PPatVar (bind ) => [ bind, ... binds]
578+ | PPatTuple (pats )
579+ | PPatArray (pats ) => List . fold_left(extract_bindings, binds, pats)
580+ | PPatRecord (pats , _ ) =>
581+ List . fold_left(
582+ (binds, (_, pat)) => extract_bindings(binds, pat),
583+ binds,
584+ pats,
585+ )
586+ | PPatConstant (_ ) => binds
587+ | PPatConstraint (pat , _ ) => extract_bindings(binds, pat)
588+ | PPatConstruct (_ , cstr ) =>
589+ switch (cstr) {
590+ | PPatConstrRecord (pats , _ ) =>
591+ List . fold_left(
592+ (binds, (_, pat)) => extract_bindings(binds, pat),
593+ binds,
594+ pats,
595+ )
596+ | PPatConstrTuple (pats ) =>
597+ List . fold_left(extract_bindings, binds, pats)
598+ }
599+ | PPatOr (pat1 , pat2 ) =>
600+ extract_bindings([] , pat1) @ extract_bindings(binds, pat2)
601+ | PPatAlias (pat , bind ) => extract_bindings([ bind, ... binds] , pat)
602+ };
603+
604+ let ctx =
605+ ref ([
606+ {
607+ modules: Hashtbl . create(64 ),
608+ types: Hashtbl . create(64 ),
609+ values: Hashtbl . create(64 ),
610+ },
611+ ] );
612+
613+ let enter_module = (p, d) => {
614+ ctx :=
615+ [
616+ {
617+ modules: Hashtbl . create(64 ),
618+ types: Hashtbl . create(64 ),
619+ values: Hashtbl . create(64 ),
620+ },
621+ ... ctx^,
622+ ] ;
623+ super. enter_module(p, d);
624+ };
625+
626+ let leave_module = (p, d) => {
627+ ctx := List . tl(ctx^ );
628+ super. leave_module(p, d);
629+ };
630+
631+ let enter_toplevel_stmt = ({ptop_desc: desc} as top) => {
632+ let {values, modules, types} = List . hd(ctx^ );
633+ switch (desc) {
634+ | PTopModule (Provided | Abstract , {pmod_name, pmod_loc}) =>
635+ if (Hashtbl . mem(modules, pmod_name. txt)) {
636+ errs := [ ProvidedMultipleTimes (pmod_name. txt, pmod_loc), ... errs^ ] ;
637+ } else {
638+ Hashtbl . add(modules, pmod_name. txt, () );
639+ }
640+ | PTopForeign (
641+ Provided | Abstract ,
642+ {pval_name, pval_name_alias, pval_loc},
643+ )
644+ | PTopPrimitive (
645+ Provided | Abstract ,
646+ {pval_name, pval_name_alias, pval_loc},
647+ ) =>
648+ let name = Option . value(~default= pval_name, pval_name_alias);
649+ if (Hashtbl . mem(values, name. txt)) {
650+ errs := [ ProvidedMultipleTimes (name. txt, pval_loc), ... errs^ ] ;
651+ } else {
652+ Hashtbl . add(values, name. txt, () );
653+ };
654+ | PTopData (decls ) =>
655+ List . iter(
656+ decl => {
657+ switch (decl) {
658+ | (Provided | Abstract , {pdata_name, pdata_loc}) =>
659+ if (Hashtbl . mem(types, pdata_name. txt)) {
660+ errs :=
661+ [ ProvidedMultipleTimes (pdata_name. txt, pdata_loc), ... errs^ ] ;
662+ } else {
663+ Hashtbl . add(types, pdata_name. txt, () );
664+ }
665+ | (NotProvided , _) => ()
666+ }
667+ },
668+ decls,
669+ )
670+ | PTopLet (Provided | Abstract , _ , _ , binds ) =>
671+ List . iter(
672+ bind => {
673+ let names = extract_bindings([] , bind. pvb_pat);
674+ List . iter(
675+ name =>
676+ if (Hashtbl . mem(values, name. txt)) {
677+ errs := [ ProvidedMultipleTimes (name. txt, name. loc), ... errs^ ] ;
678+ } else {
679+ Hashtbl . add(values, name. txt, () );
680+ },
681+ names,
682+ );
683+ },
684+ binds,
685+ )
686+ | PTopException (
687+ Provided | Abstract ,
688+ {ptyexn_constructor: {pext_name, pext_loc}},
689+ ) =>
690+ if (Hashtbl . mem(values, pext_name. txt)) {
691+ errs := [ ProvidedMultipleTimes (pext_name. txt, pext_loc), ... errs^ ] ;
692+ } else {
693+ Hashtbl . add(values, pext_name. txt, () );
694+ }
695+ | PTopProvide (items) =>
696+ let apply_alias = (name, alias) => {
697+ let old_name = Identifier . string_of_ident(name. txt);
698+ let new_name =
699+ switch (alias) {
700+ | Some (alias ) => Identifier . string_of_ident(alias. txt)
701+ | None => old_name
702+ };
703+ (old_name, new_name);
704+ };
705+ List . iter(
706+ item => {
707+ switch (item) {
708+ | PProvideType ({name, alias, loc}) =>
709+ let (_ , name ) = apply_alias(name, alias);
710+ if (Hashtbl . mem(types, name)) {
711+ errs := [ ProvidedMultipleTimes (name, loc), ... errs^ ] ;
712+ } else {
713+ Hashtbl . add(types, name, () );
714+ };
715+ | PProvideModule ({name, alias, loc}) =>
716+ let (_ , name ) = apply_alias(name, alias);
717+ if (Hashtbl . mem(modules, name)) {
718+ errs := [ ProvidedMultipleTimes (name, loc), ... errs^ ] ;
719+ } else {
720+ Hashtbl . add(modules, name, () );
721+ };
722+ | PProvideValue ({name, alias, loc}) =>
723+ let (_ , name ) = apply_alias(name, alias);
724+ if (Hashtbl . mem(values, name)) {
725+ errs := [ ProvidedMultipleTimes (name, loc), ... errs^ ] ;
726+ } else {
727+ Hashtbl . add(values, name, () );
728+ };
729+ }
730+ },
731+ items,
732+ );
733+ | PTopModule (NotProvided , _ )
734+ | PTopForeign (NotProvided , _ )
735+ | PTopPrimitive (NotProvided , _ )
736+ | PTopLet (NotProvided , _ , _ , _ )
737+ | PTopException (NotProvided , _ )
738+ | PTopInclude (_ )
739+ | PTopExpr (_ ) => ()
740+ };
741+ super. enter_toplevel_stmt(top);
742+ };
743+
744+ {
745+ errs,
746+ iter_hooks: {
747+ ... super,
748+ enter_toplevel_stmt,
749+ enter_module,
750+ leave_module,
751+ },
752+ };
753+ };
754+
560755let compose_well_formedness = ({errs, iter_hooks}, cur) =>
561756 cur(errs, iter_hooks);
562757
@@ -572,6 +767,7 @@ let well_formedness_checks = [
572767 no_loop_control_statement_outside_of_loop,
573768 malformed_return_statements,
574769 no_local_include,
770+ provided_multiple_times,
575771] ;
576772
577773let well_formedness_checker = () =>
0 commit comments