View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2011-2016, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(predicate_options,
   36          [ predicate_options/3,                % +PI, +Arg, +Options
   37            assert_predicate_options/4,         % +PI, +Arg, +Options, ?New
   38
   39            current_option_arg/2,               % ?PI, ?Arg
   40            current_predicate_option/3,         % ?PI, ?Arg, ?Option
   41            check_predicate_option/3,           % +PI, +Arg, +Option
   42                                                % Create declarations
   43            current_predicate_options/3,        % ?PI, ?Arg, ?Options
   44            retractall_predicate_options/0,
   45            derived_predicate_options/3,        % :PI, ?Arg, ?Options
   46            derived_predicate_options/1,        % +Module
   47                                                % Checking
   48            check_predicate_options/0,
   49            derive_predicate_options/0,
   50            check_predicate_options/1           % :PredicateIndicator
   51          ]).   52:- autoload(library(apply),[maplist/3]).   53:- autoload(library(debug),[debug/3]).   54:- autoload(library(error),
   55	    [ existence_error/2,
   56	      must_be/2,
   57	      instantiation_error/1,
   58	      uninstantiation_error/1,
   59	      is_of_type/2
   60	    ]).   61:- use_module(library(dialect/swi/syspred_options)).   62
   63:- autoload(library(listing),[portray_clause/1]).   64:- autoload(library(lists),[member/2,nth1/3,append/3,delete/3]).   65:- autoload(library(pairs),[group_pairs_by_key/2]).   66:- autoload(library(prolog_clause),[clause_info/4]).   67
   68
   69:- meta_predicate
   70    predicate_options(:, +, +),
   71    assert_predicate_options(:, +, +, ?),
   72    current_predicate_option(:, ?, ?),
   73    check_predicate_option(:, ?, ?),
   74    current_predicate_options(:, ?, ?),
   75    current_option_arg(:, ?),
   76    pred_option(:,-),
   77    derived_predicate_options(:,?,?),
   78    check_predicate_options(:).

Access and analyse predicate options

This module provides the developers interface for the directive predicate_options/3. This directive allows us to specify that, e.g., open/4 processes options using the 4th argument and supports the option type using the values text and binary. Declaring options that are processed allows for more reliable handling of predicate options and simplifies porting applications. This library provides the following functionality:

Below, we describe some use-cases.

Quick check of a program
This scenario is useful as an occasional check or to assess problems with option-handling for porting an application to SWI-Prolog. It consists of three steps: loading the program (1 and 2), deriving option handling for application predicates (3) and running the checker (4).
1 ?- [load].
2 ?- autoload.
3 ?- derive_predicate_options.
4 ?- check_predicate_options.
Add declarations to your program
Adding declarations about option processes improves the quality of the checking. The analysis of derive_predicate_options/0 may miss options and does not derive the types for options that are processed in Prolog code. The process is similar to the above. In steps 4 and further, the inferred declarations are listed, inspected and added to the source code of the module.
1 ?- [load].
2 ?- autoload.
3 ?- derive_predicate_options.
4 ?- derived_predicate_options(module_1).
5 ?- derived_predicate_options(module_2).
6 ?- ...
Declare option processing requirements
If an application requires that open/4 needs to support lock(write), it may do so using the directive below. This directive raises an exception when loaded on a Prolog implementation that does not support this option.
:- current_predicate_option(open/4, 4, lock(write)).
See also
- library(option) for accessing options in Prolog code. */
  144:- multifile option_decl/3, pred_option/3.  145:- dynamic   dyn_option_decl/3.
 predicate_options(:PI, +Arg, +Options) is det
Declare that the predicate PI processes options on Arg. Options is a list of options processed. Each element is one of:

Below is an example that processes the option header(boolean) and passes all options to open/4:

:- predicate_options(write_xml_file/3, 3,
                     [ header(boolean),
                       pass_to(open/4, 4)
                     ]).

write_xml_file(File, XMLTerm, Options) :-
    open(File, write, Out, Options),
    (   option(header(true), Options, true)
    ->  write_xml_header(Out)
    ;   true
    ),
    ...

This predicate may only be used as a directive and is processed by expand_term/2. Option processing can be specified at runtime using assert_predicate_options/3, which is intended to support program analysis.

  183predicate_options(PI, Arg, Options) :-
  184    throw(error(context_error(nodirective,
  185                              predicate_options(PI, Arg, Options)), _)).
 assert_predicate_options(:PI, +Arg, +Options, ?New) is semidet
As predicate_options(:PI, +Arg, +Options). New is a boolean indicating whether the declarations have changed. If New is provided and false, the predicate becomes semidet and fails without modifications if modifications are required.
  195assert_predicate_options(PI, Arg, Options, New) :-
  196    canonical_pi(PI, M:Name/Arity),
  197    functor(Head, Name, Arity),
  198    (   dyn_option_decl(Head, M, Arg)
  199    ->  true
  200    ;   New = true,
  201        assertz(dyn_option_decl(Head, M, Arg))
  202    ),
  203    phrase('$predopts':option_clauses(Options, Head, M, Arg),
  204           OptionClauses),
  205    forall(member(Clause, OptionClauses),
  206           assert_option_clause(Clause, New)),
  207    (   var(New)
  208    ->  New = false
  209    ;   true
  210    ).
  211
  212assert_option_clause(Clause, New) :-
  213    rename_clause(Clause, NewClause,
  214                  '$pred_option'(A,B,C,D), '$dyn_pred_option'(A,B,C,D)),
  215    clause_head(NewClause, NewHead),
  216    (   clause(NewHead, _)
  217    ->  true
  218    ;   New = true,
  219        assertz(NewClause)
  220    ).
  221
  222clause_head(M:(Head:-_Body), M:Head) :- !.
  223clause_head((M:Head :-_Body), M:Head) :- !.
  224clause_head(Head, Head).
  225
  226rename_clause(M:Clause, M:NewClause, Head, NewHead) :-
  227    !,
  228    rename_clause(Clause, NewClause, Head, NewHead).
  229rename_clause((Head :- Body), (NewHead :- Body), Head, NewHead) :- !.
  230rename_clause(Head, NewHead, Head, NewHead) :- !.
  231rename_clause(Head, Head, _, _).
  232
  233
  234
  235                 /*******************************
  236                 *        QUERY OPTIONS         *
  237                 *******************************/
 current_option_arg(:PI, ?Arg) is nondet
True when Arg of PI processes predicate options. Which options are processed can be accessed using current_predicate_option/3.
  244current_option_arg(Module:Name/Arity, Arg) :-
  245    current_option_arg(Module:Name/Arity, Arg, _DefM).
  246
  247current_option_arg(Module:Name/Arity, Arg, DefM) :-
  248    atom(Name), integer(Arity),
  249    !,
  250    resolve_module(Module:Name/Arity, DefM:Name/Arity),
  251    functor(Head, Name, Arity),
  252    (   option_decl(Head, DefM, Arg)
  253    ;   dyn_option_decl(Head, DefM, Arg)
  254    ).
  255current_option_arg(M:Name/Arity, Arg, M) :-
  256    (   option_decl(Head, M, Arg)
  257    ;   dyn_option_decl(Head, M, Arg)
  258    ),
  259    functor(Head, Name, Arity).
 current_predicate_option(:PI, ?Arg, ?Option) is nondet
True when Arg of PI processes Option. For example, the following is true:
?- current_predicate_option(open/4, 4, type(text)).
true.

This predicate is intended to support conditional compilation using if/1 ... endif/0. The predicate current_predicate_options/3 can be used to access the full capabilities of a predicate.

  276current_predicate_option(Module:PI, Arg, Option) :-
  277    current_option_arg(Module:PI, Arg, DefM),
  278    PI = Name/Arity,
  279    functor(Head, Name, Arity),
  280    catch(pred_option(DefM:Head, Option),
  281          error(type_error(_,_),_),
  282          fail).
 check_predicate_option(:PI, +Arg, +Option) is det
Verify predicate options at runtime. Similar to current_predicate_option/3, but intended to support runtime checking.
Errors
- existence_error(option, OptionName) if the option is not supported by PI.
- type_error(Type, Value) if the option is supported but the value does not match the option type. See must_be/2.
  295check_predicate_option(Module:PI, Arg, Option) :-
  296    define_predicate(Module:PI),
  297    current_option_arg(Module:PI, Arg, DefM),
  298    PI = Name/Arity,
  299    functor(Head, Name, Arity),
  300    (   pred_option(DefM:Head, Option)
  301    ->  true
  302    ;   existence_error(option, Option)
  303    ).
  304
  305
  306pred_option(M:Head, Option) :-
  307    pred_option(M:Head, Option, []).
  308
  309pred_option(M:Head, Option, Seen) :-
  310    (   has_static_option_decl(M),
  311        M:'$pred_option'(Head, _, Option, Seen)
  312    ;   has_dynamic_option_decl(M),
  313        M:'$dyn_pred_option'(Head, _, Option, Seen)
  314    ).
  315
  316has_static_option_decl(M) :-
  317    '$c_current_predicate'(_, M:'$pred_option'(_,_,_,_)).
  318has_dynamic_option_decl(M) :-
  319    '$c_current_predicate'(_, M:'$dyn_pred_option'(_,_,_,_)).
  320
  321
  322                 /*******************************
  323                 *     TYPE&MODE CONSTRAINTS    *
  324                 *******************************/
  325
  326:- public
  327    system:predicate_option_mode/2,
  328    system:predicate_option_type/2.  329
  330add_attr(Var, Value) :-
  331    (   get_attr(Var, predicate_options, Old)
  332    ->  put_attr(Var, predicate_options, [Value|Old])
  333    ;   put_attr(Var, predicate_options, [Value])
  334    ).
  335
  336system:predicate_option_type(Type, Arg) :-
  337    var(Arg),
  338    !,
  339    add_attr(Arg, option_type(Type)).
  340system:predicate_option_type(Type, Arg) :-
  341    must_be(Type, Arg).
  342
  343system:predicate_option_mode(Mode, Arg) :-
  344    var(Arg),
  345    !,
  346    add_attr(Arg, option_mode(Mode)).
  347system:predicate_option_mode(Mode, Arg) :-
  348    check_mode(Mode, Arg).
  349
  350check_mode(input, Arg) :-
  351    (   nonvar(Arg)
  352    ->  true
  353    ;   instantiation_error(Arg)
  354    ).
  355check_mode(output, Arg) :-
  356    (   var(Arg)
  357    ->  true
  358    ;   uninstantiation_error(Arg)
  359    ).
  360
  361attr_unify_hook([], _).
  362attr_unify_hook([H|T], Var) :-
  363    option_hook(H, Var),
  364    attr_unify_hook(T, Var).
  365
  366option_hook(option_type(Type), Value) :-
  367    is_of_type(Type, Value).
  368option_hook(option_mode(Mode), Value) :-
  369    check_mode(Mode, Value).
  370
  371
  372attribute_goals(Var) -->
  373    { get_attr(Var, predicate_options, Attrs) },
  374    option_goals(Attrs, Var).
  375
  376option_goals([], _) --> [].
  377option_goals([H|T], Var) -->
  378    option_goal(H, Var),
  379    option_goals(T, Var).
  380
  381option_goal(option_type(Type), Var) --> [predicate_option_type(Type, Var)].
  382option_goal(option_mode(Mode), Var) --> [predicate_option_mode(Mode, Var)].
  383
  384
  385                 /*******************************
  386                 *      OUTPUT DECLARATIONS     *
  387                 *******************************/
 current_predicate_options(:PI, ?Arg, ?Options) is nondet
True when Options is the current active option declaration for PI on Arg. See predicate_options/3 for the argument descriptions. If PI is ground and refers to an undefined predicate, the autoloader is used to obtain a definition of the predicate.
  397current_predicate_options(PI, Arg, Options) :-
  398    define_predicate(PI),
  399    setof(Arg-Option,
  400          current_predicate_option_decl(PI, Arg, Option),
  401          Options0),
  402    group_pairs_by_key(Options0, Grouped),
  403    member(Arg-Options, Grouped).
  404
  405current_predicate_option_decl(PI, Arg, Option) :-
  406    current_predicate_option(PI, Arg, Option0),
  407    Option0 =.. [Name|Values],
  408    maplist(mode_and_type, Values, Types),
  409    Option =.. [Name|Types].
  410
  411mode_and_type(Value, ModeAndType) :-
  412    copy_term(Value,_,Goals),
  413    (   memberchk(predicate_option_mode(output, _), Goals)
  414    ->  ModeAndType = -(Type)
  415    ;   ModeAndType = Type
  416    ),
  417    (   memberchk(predicate_option_type(Type, _), Goals)
  418    ->  true
  419    ;   Type = any
  420    ).
  421
  422define_predicate(PI) :-
  423    ground(PI),
  424    !,
  425    PI = M:Name/Arity,
  426    functor(Head, Name, Arity),
  427    once(predicate_property(M:Head, _)).
  428define_predicate(_).
 derived_predicate_options(:PI, ?Arg, ?Options) is nondet
Derive option arguments using static analysis. True when Options is the current derived active option declaration for PI on Arg.
  436derived_predicate_options(PI, Arg, Options) :-
  437    define_predicate(PI),
  438    setof(Arg-Option,
  439          derived_predicate_option(PI, Arg, Option),
  440          Options0),
  441    group_pairs_by_key(Options0, Grouped),
  442    member(Arg-Options1, Grouped),
  443    PI = M:_,
  444    phrase(expand_pass_to_options(Options1, M), Options2),
  445    sort(Options2, Options).
  446
  447derived_predicate_option(PI, Arg, Decl) :-
  448    current_option_arg(PI, Arg, DefM),
  449    PI = _:Name/Arity,
  450    functor(Head, Name, Arity),
  451    has_dynamic_option_decl(DefM),
  452    (   has_static_option_decl(DefM),
  453        DefM:'$pred_option'(Head, Decl, _, [])
  454    ;   DefM:'$dyn_pred_option'(Head, Decl, _, [])
  455    ).
 expand_pass_to_options(+OptionsIn, +Module, -OptionsOut)// is det
Expand the options of pass_to(PI,Arg) if PI does not refer to a public predicate.
  462expand_pass_to_options([], _) --> [].
  463expand_pass_to_options([H|T], M) -->
  464    expand_pass_to(H, M),
  465    expand_pass_to_options(T, M).
  466
  467expand_pass_to(pass_to(PI, Arg), Module) -->
  468    { strip_module(Module:PI, M, Name/Arity),
  469      functor(Head, Name, Arity),
  470      \+ (   predicate_property(M:Head, exported)
  471         ;   predicate_property(M:Head, public)
  472         ;   M == system
  473         ),
  474      !,
  475      current_predicate_options(M:Name/Arity, Arg, Options)
  476    },
  477    list(Options).
  478expand_pass_to(Option, _) -->
  479    [Option].
  480
  481list([]) --> [].
  482list([H|T]) --> [H], list(T).
 derived_predicate_options(+Module) is det
Derive predicate option declarations for a module. The derived options are printed to the current_output stream.
  489derived_predicate_options(Module) :-
  490    var(Module),
  491    !,
  492    forall(current_module(Module),
  493           derived_predicate_options(Module)).
  494derived_predicate_options(Module) :-
  495    findall(predicate_options(Module:PI, Arg, Options),
  496            ( derived_predicate_options(Module:PI, Arg, Options),
  497              PI = Name/Arity,
  498              functor(Head, Name, Arity),
  499              (   predicate_property(Module:Head, exported)
  500              ->  true
  501              ;   predicate_property(Module:Head, public)
  502              )
  503            ),
  504            Decls0),
  505    maplist(qualify_decl(Module), Decls0, Decls1),
  506    sort(Decls1, Decls),
  507    (   Decls \== []
  508    ->  format('~N~n~n% Predicate option declarations for module ~q~n~n',
  509               [Module]),
  510        forall(member(Decl, Decls),
  511               portray_clause((:-Decl)))
  512    ;   true
  513    ).
  514
  515qualify_decl(M,
  516             predicate_options(PI0, Arg, Options0),
  517             predicate_options(PI1, Arg, Options1)) :-
  518    qualify(PI0, M, PI1),
  519    maplist(qualify_option(M), Options0, Options1).
  520
  521qualify_option(M, pass_to(PI0, Arg), pass_to(PI1, Arg)) :-
  522    !,
  523    qualify(PI0, M, PI1).
  524qualify_option(_, Opt, Opt).
  525
  526qualify(M:Term, M, Term) :- !.
  527qualify(QTerm, _, QTerm).
  528
  529
  530                 /*******************************
  531                 *            CLEANUP           *
  532                 *******************************/
 retractall_predicate_options is det
Remove all dynamically (derived) predicate options.
  538retractall_predicate_options :-
  539    forall(retract(dyn_option_decl(_,M,_)),
  540           abolish(M:'$dyn_pred_option'/4)).
  541
  542
  543                 /*******************************
  544                 *     COMPILE-TIME CHECKER     *
  545                 *******************************/
  546
  547
  548:- thread_local
  549    new_decl/1.
 check_predicate_options is det
Analyse loaded program for erroneous options. This predicate decompiles the current program and searches for calls to predicates that process options. For each option list, it validates whether the provided options are supported and validates the argument type. This predicate performs partial dataflow analysis to track option-lists inside a clause.
See also
- derive_predicate_options/0 can be used to derive declarations for predicates that pass options. This predicate should normally be called before check_predicate_options/0.
  565check_predicate_options :-
  566    forall(current_module(Module),
  567           check_predicate_options_module(Module)).
 derive_predicate_options is det
Derive new predicate option declarations. This predicate analyses the loaded program to find clauses that process options using one of the predicates from library(option) or passes options to other predicates that are known to process options. The process is repeated until no new declarations are retrieved.
See also
- autoload/0 may be used to complete the loaded program.
  579derive_predicate_options :-
  580    derive_predicate_options(NewDecls),
  581    (   NewDecls == []
  582    ->  true
  583    ;   print_message(informational, check_options(new(NewDecls))),
  584        new_decls(NewDecls),
  585        derive_predicate_options
  586    ).
  587
  588new_decls([]).
  589new_decls([predicate_options(PI, A, O)|T]) :-
  590    assert_predicate_options(PI, A, O, _),
  591    new_decls(T).
  592
  593
  594derive_predicate_options(NewDecls) :-
  595    call_cleanup(
  596        ( forall(
  597              current_module(Module),
  598              forall(
  599                  ( predicate_in_module(Module, PI),
  600                    PI = Name/Arity,
  601                    functor(Head, Name, Arity),
  602                    catch(Module:clause(Head, Body, Ref), _, fail)
  603                  ),
  604                  check_clause((Head:-Body), Module, Ref, decl))),
  605          (   setof(Decl, retract(new_decl(Decl)), NewDecls)
  606              ->  true
  607              ;   NewDecls = []
  608          )
  609        ),
  610        retractall(new_decl(_))).
  611
  612
  613check_predicate_options_module(Module) :-
  614    forall(predicate_in_module(Module, PI),
  615           check_predicate_options(Module:PI)).
  616
  617predicate_in_module(Module, PI) :-
  618    current_predicate(Module:PI),
  619    PI = Name/Arity,
  620    functor(Head, Name, Arity),
  621    \+ predicate_property(Module:Head, imported_from(_)).
 check_predicate_options(:PredicateIndicator) is det
Verify calls to predicates that have options in all clauses of the predicate indicated by PredicateIndicator.
  628check_predicate_options(Module:Name/Arity) :-
  629    debug(predicate_options, 'Checking ~q', [Module:Name/Arity]),
  630    functor(Head, Name, Arity),
  631    forall(catch(Module:clause(Head, Body, Ref), _, fail),
  632           check_clause((Head:-Body), Module, Ref, check)).
 check_clause(+Clause, +Module, +Ref, +Action) is det
Action is one of
decl
Create additional declarations
check
Produce error messages
  643check_clause((Head:-Body), M, ClauseRef, Action) :-
  644    !,
  645    catch(check_body(Body, M, _, Action), E, true),
  646    (   var(E)
  647    ->  option_decl(M:Head, Action)
  648    ;   (   clause_info(ClauseRef, File, TermPos, _NameOffset),
  649            TermPos = term_position(_,_,_,_,[_,BodyPos]),
  650            catch(check_body(Body, M, BodyPos, Action),
  651                  error(Formal, ArgPos), true),
  652            compound(ArgPos),
  653            arg(1, ArgPos, CharCount),
  654            integer(CharCount)
  655        ->  Location = file_char_count(File, CharCount)
  656        ;   Location = clause(ClauseRef),
  657            E = error(Formal, _)
  658        ),
  659        print_message(error, predicate_option_error(Formal, Location))
  660    ).
 check_body(+Body, +Module, +TermPos, +Action)
  665:- multifile
  666    prolog:called_by/4,             % +Goal, +Module, +Context, -Called
  667    prolog:called_by/2.             % +Goal, -Called
  668
  669check_body(Var, _, _, _) :-
  670    var(Var),
  671    !.
  672check_body(M:G, _, term_position(_,_,_,_,[_,Pos]), Action) :-
  673    !,
  674    check_body(G, M, Pos, Action).
  675check_body((A,B), M, term_position(_,_,_,_,[PA,PB]), Action) :-
  676    !,
  677    check_body(A, M, PA, Action),
  678    check_body(B, M, PB, Action).
  679check_body(A=B, _, _, _) :-             % partial evaluation
  680    unify_with_occurs_check(A,B),
  681    !.
  682check_body(Goal, M, term_position(_,_,_,_,ArgPosList), Action) :-
  683    callable(Goal),
  684    functor(Goal, Name, Arity),
  685    (   '$get_predicate_attribute'(M:Goal, imported, DefM)
  686    ->  true
  687    ;   DefM = M
  688    ),
  689    (   eval_option_pred(DefM:Goal)
  690    ->  true
  691    ;   current_option_arg(DefM:Name/Arity, OptArg),
  692        !,
  693        arg(OptArg, Goal, Options),
  694        nth1(OptArg, ArgPosList, ArgPos),
  695        check_options(DefM:Name/Arity, OptArg, Options, ArgPos, Action)
  696    ).
  697check_body(Goal, M, _, Action) :-
  698    (   (   predicate_property(M:Goal, imported_from(IM))
  699        ->  true
  700        ;   IM = M
  701        ),
  702        prolog:called_by(Goal, IM, M, Called)
  703    ;   prolog:called_by(Goal, Called)
  704    ),
  705    !,
  706    check_called_by(Called, M, Action).
  707check_body(Meta, M, term_position(_,_,_,_,ArgPosList), Action) :-
  708    '$get_predicate_attribute'(M:Meta, meta_predicate, Head),
  709    !,
  710    check_meta_args(1, Head, Meta, M, ArgPosList, Action).
  711check_body(_, _, _, _).
  712
  713check_meta_args(I, Head, Meta, M, [ArgPos|ArgPosList], Action) :-
  714    arg(I, Head, AS),
  715    !,
  716    (   AS == 0
  717    ->  arg(I, Meta, MA),
  718        check_body(MA, M, ArgPos, Action)
  719    ;   true
  720    ),
  721    succ(I, I2),
  722    check_meta_args(I2, Head, Meta, M, ArgPosList, Action).
  723check_meta_args(_,_,_,_, _, _).
 check_called_by(+CalledBy, +M, +Action) is det
Handle results from prolog:called_by/2.
  729check_called_by([], _, _).
  730check_called_by([H|T], M, Action) :-
  731    (   H = G+N
  732    ->  (   extend(G, N, G2)
  733        ->  check_body(G2, M, _, Action)
  734        ;   true
  735        )
  736    ;   check_body(H, M, _, Action)
  737    ),
  738    check_called_by(T, M, Action).
  739
  740extend(Goal, N, GoalEx) :-
  741    callable(Goal),
  742    Goal =.. List,
  743    length(Extra, N),
  744    append(List, Extra, ListEx),
  745    GoalEx =.. ListEx.
 check_options(:Predicate, +OptionArg, +Options, +ArgPos, +Action)
Verify the list Options, that is passed into Predicate on argument OptionArg. ArgPos is a term-position term describing the location of the Options list. If Options is a partial list, the tail is annotated with pass_to(PI, OptArg).
  755check_options(PI, OptArg, QOptions, ArgPos, Action) :-
  756    debug(predicate_options, '\tChecking call to ~q', [PI]),
  757    remove_qualifier(QOptions, Options),
  758    must_be(list_or_partial_list, Options),
  759    check_option_list(Options, PI, OptArg, Options, ArgPos, Action).
  760
  761remove_qualifier(X, X) :-
  762    var(X),
  763    !.
  764remove_qualifier(_:X, X) :- !.
  765remove_qualifier(X, X).
  766
  767check_option_list(Var,  PI, OptArg, _, _, _) :-
  768    var(Var),
  769    !,
  770    annotate(Var, pass_to(PI, OptArg)).
  771check_option_list([], _, _, _, _, _).
  772check_option_list([H|T], PI, OptArg, Options, ArgPos, Action) :-
  773    check_option(PI, OptArg, H, ArgPos, Action),
  774    check_option_list(T, PI, OptArg, Options, ArgPos, Action).
  775
  776check_option(_, _, _, _, decl) :- !.
  777check_option(PI, OptArg, Opt, ArgPos, _) :-
  778    catch(check_predicate_option(PI, OptArg, Opt), E, true),
  779    !,
  780    (   var(E)
  781    ->  true
  782    ;   E = error(Formal,_),
  783        throw(error(Formal,ArgPos))
  784    ).
  785
  786
  787                 /*******************************
  788                 *          ANNOTATIONS         *
  789                 *******************************/
 annotate(+Var, +Term) is det
Use constraints to accumulate annotations about variables. If two annotated variables are unified, the attributes are joined.
  796annotate(Var, Term) :-
  797    (   get_attr(Var, predopts_analysis, Old)
  798    ->  put_attr(Var, predopts_analysis, [Term|Old])
  799    ;   var(Var)
  800    ->  put_attr(Var, predopts_analysis, [Term])
  801    ;   true
  802    ).
  803
  804annotations(Var, Annotations) :-
  805    get_attr(Var, predopts_analysis, Annotations).
  806
  807predopts_analysis:attr_unify_hook(Opts, Value) :-
  808    get_attr(Value, predopts_analysis, Others),
  809    !,
  810    append(Opts, Others, All),
  811    put_attr(Value, predopts_analysis, All).
  812predopts_analysis:attr_unify_hook(_, _).
  813
  814
  815                 /*******************************
  816                 *         PARTIAL EVAL         *
  817                 *******************************/
  818
  819eval_option_pred(swi_option:option(Opt, Options)) :-
  820    processes(Opt, Spec),
  821    annotate(Options, Spec).
  822eval_option_pred(swi_option:option(Opt, Options, _Default)) :-
  823    processes(Opt, Spec),
  824    annotate(Options, Spec).
  825eval_option_pred(swi_option:select_option(Opt, Options, Rest)) :-
  826    ignore(unify_with_occurs_check(Rest, Options)),
  827    processes(Opt, Spec),
  828    annotate(Options, Spec).
  829eval_option_pred(swi_option:select_option(Opt, Options, Rest, _Default)) :-
  830    ignore(unify_with_occurs_check(Rest, Options)),
  831    processes(Opt, Spec),
  832    annotate(Options, Spec).
  833eval_option_pred(swi_option:meta_options(_Cond, QOptionsIn, QOptionsOut)) :-
  834    remove_qualifier(QOptionsIn, OptionsIn),
  835    remove_qualifier(QOptionsOut, OptionsOut),
  836    ignore(unify_with_occurs_check(OptionsIn, OptionsOut)).
  837
  838processes(Opt, Spec) :-
  839    compound(Opt),
  840    functor(Opt, OptName, 1),
  841    Spec =.. [OptName,any].
  842
  843
  844                 /*******************************
  845                 *        NEW DECLARTIONS       *
  846                 *******************************/
 option_decl(:Head, +Action) is det
Add new declarations based on attributes left by the analysis pass. We do not add declarations for system modules or modules that already contain static declarations.
To be done
- Should we add a mode to include generating declarations for system modules and modules with static declarations?
  857option_decl(_, check) :- !.
  858option_decl(M:_, _) :-
  859    system_module(M),
  860    !.
  861option_decl(M:_, _) :-
  862    has_static_option_decl(M),
  863    !.
  864option_decl(M:Head, _) :-
  865    compound(Head),
  866    arg(AP, Head, QA),
  867    remove_qualifier(QA, A),
  868    annotations(A, Annotations0),
  869    functor(Head, Name, Arity),
  870    PI = M:Name/Arity,
  871    delete(Annotations0, pass_to(PI,AP), Annotations),
  872    Annotations \== [],
  873    Decl = predicate_options(PI, AP, Annotations),
  874    (   new_decl(Decl)
  875    ->  true
  876    ;   assert_predicate_options(M:Name/Arity, AP, Annotations, false)
  877    ->  true
  878    ;   assertz(new_decl(Decl)),
  879        debug(predicate_options(decl), '~q', [Decl])
  880    ),
  881    fail.
  882option_decl(_, _).
  883
  884system_module(system) :- !.
  885system_module(Module) :-
  886    sub_atom(Module, 0, _, _, $).
  887
  888
  889                 /*******************************
  890                 *             MISC             *
  891                 *******************************/
  892
  893canonical_pi(M:Name//Arity, M:Name/PArity) :-
  894    integer(Arity),
  895    PArity is Arity+2.
  896canonical_pi(PI, PI).
 resolve_module(:PI, -DefPI) is det
Find the real predicate indicator pointing to the definition module of PI. This is similar to using predicate_property/3 with the property imported_from, but using '$get_predicate_attribute'/3 avoids auto-importing the predicate.
  906resolve_module(Module:Name/Arity, DefM:Name/Arity) :-
  907    functor(Head, Name, Arity),
  908    (   '$get_predicate_attribute'(Module:Head, imported, M)
  909    ->  DefM = M
  910    ;   DefM = Module
  911    ).
  912
  913
  914                 /*******************************
  915                 *            MESSAGES          *
  916                 *******************************/
  917:- multifile
  918    prolog:message//1.  919
  920prolog:message(predicate_option_error(Formal, Location)) -->
  921    error_location(Location),
  922    '$messages':term_message(Formal). % TBD: clean interface
  923prolog:message(check_options(new(Decls))) -->
  924    [ 'Inferred declarations:'-[], nl ],
  925    new_decls(Decls).
  926
  927error_location(file_char_count(File, CharPos)) -->
  928    { filepos_line(File, CharPos, Line, LinePos) },
  929    [ url(File:Line:LinePos), ': ' ].
  930error_location(clause(ClauseRef)) -->
  931    { clause_property(ClauseRef, file(File)),
  932      clause_property(ClauseRef, line_count(Line))
  933    },
  934    !,
  935    [ url(File:Line), ': ' ].
  936error_location(clause(ClauseRef)) -->
  937    [ 'Clause ~q: '-[ClauseRef] ].
  938
  939filepos_line(File, CharPos, Line, LinePos) :-
  940    setup_call_cleanup(
  941        ( open(File, read, In),
  942          open_null_stream(Out)
  943        ),
  944        ( Skip is CharPos-1,
  945          copy_stream_data(In, Out, Skip),
  946          stream_property(In, position(Pos)),
  947          stream_position_data(line_count, Pos, Line),
  948          stream_position_data(line_position, Pos, LinePos)
  949        ),
  950        ( close(Out),
  951          close(In)
  952        )).
  953
  954new_decls([]) --> [].
  955new_decls([H|T]) -->
  956    [ '    :- ~q'-[H], nl ],
  957    new_decls(T).
  958
  959
  960                 /*******************************
  961                 *      SYSTEM DECLARATIONS     *
  962                 *******************************/