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)  1985-2020, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(check,
   38        [ check/0,                      % run all checks
   39          list_undefined/0,             % list undefined predicates
   40          list_undefined/1,             % +Options
   41          list_autoload/0,              % list predicates that need autoloading
   42          list_redefined/0,             % list redefinitions
   43          list_cross_module_calls/0,	% List Module:Goal usage
   44          list_cross_module_calls/1,    % +Options
   45          list_void_declarations/0,     % list declarations with no clauses
   46          list_trivial_fails/0,         % list goals that trivially fail
   47          list_trivial_fails/1,         % +Options
   48          list_format_errors/0,         % list calls to format with wrong args
   49          list_format_errors/1,		% +Options
   50          list_strings/0,               % list string objects in clauses
   51          list_strings/1,               % +Options
   52          list_rationals/0,		% list rational objects in clauses
   53          list_rationals/1              % +Options
   54        ]).   55:- autoload(library(apply),[maplist/2]).   56:- autoload(library(lists),[member/2,append/3]).   57:- autoload(library(occurs),[sub_term/2]).   58:- autoload(library(option),[merge_options/3,option/3]).   59:- autoload(library(pairs),
   60	    [group_pairs_by_key/2,map_list_to_pairs/3,pairs_values/2]).   61:- autoload(library(prolog_clause),
   62	    [clause_info/4,predicate_name/2,clause_name/2]).   63:- autoload(library(prolog_code),[pi_head/2]).   64:- autoload(library(prolog_codewalk),
   65	    [prolog_walk_code/1,prolog_program_clause/2]).   66:- autoload(library(prolog_format),[format_types/2]).   67
   68
   69:- set_prolog_flag(generate_debug_info, false).   70
   71:- multifile
   72       trivial_fail_goal/1,
   73       string_predicate/1,
   74       valid_string_goal/1,
   75       checker/2.   76
   77:- dynamic checker/2.

Consistency checking

This library provides some consistency checks for the loaded Prolog program. The predicate make/0 runs list_undefined/0 to find undefined predicates in `user' modules.

See also
- gxref/0 provides a graphical cross referencer
- PceEmacs performs real time consistency checks while you edit
- library(prolog_xref) implements `offline' cross-referencing
- library(prolog_codewalk) implements `online' analysis */
   92:- predicate_options(list_undefined/1, 1,
   93                     [ module_class(list(oneof([user,library,system])))
   94                     ]).
 check is det
Run all consistency checks defined by checker/2. Checks enabled by default are:
  110check :-
  111    checker(Checker, Message),
  112    print_message(informational,check(pass(Message))),
  113    catch(Checker,E,print_message(error,E)),
  114    fail.
  115check.
 list_undefined is det
 list_undefined(+Options) is det
Report undefined predicates. This predicate finds undefined predicates by decompiling and analyzing the body of all clauses. Options:
module_class(+Classes)
Process modules of the given Classes. The default for classes is [user]. For example, to include the libraries into the examination, use [user,library].
See also
- gxref/0 provides a graphical cross-referencer.
- make/0 calls list_undefined/0
  132:- thread_local
  133    undef/2.  134
  135list_undefined :-
  136    list_undefined([]).
  137
  138list_undefined(Options) :-
  139    merge_options(Options,
  140                  [ module_class([user])
  141                  ],
  142                  WalkOptions),
  143    call_cleanup(
  144        prolog_walk_code([ undefined(trace),
  145                           on_trace(found_undef)
  146                         | WalkOptions
  147                         ]),
  148        collect_undef(Grouped)),
  149    (   Grouped == []
  150    ->  true
  151    ;   print_message(warning, check(undefined_procedures, Grouped))
  152    ).
  153
  154% The following predicates are used from library(prolog_autoload).
  155
  156:- public
  157    found_undef/3,
  158    collect_undef/1.  159
  160collect_undef(Grouped) :-
  161    findall(PI-From, retract(undef(PI, From)), Pairs),
  162    keysort(Pairs, Sorted),
  163    group_pairs_by_key(Sorted, Grouped).
  164
  165found_undef(To, _Caller, From) :-
  166    goal_pi(To, PI),
  167    (   undef(PI, From)
  168    ->  true
  169    ;   compiled(PI)
  170    ->  true
  171    ;   not_always_present(PI)
  172    ->  true
  173    ;   assertz(undef(PI,From))
  174    ).
  175
  176compiled(system:'$call_cleanup'/0).     % compiled to VM instructions
  177compiled(system:'$catch'/0).
  178compiled(system:'$cut'/0).
  179compiled(system:'$reset'/0).
  180compiled(system:'$call_continuation'/1).
  181compiled(system:'$shift'/1).
  182compiled(system:'$shift_for_copy'/1).
  183compiled('$engines':'$yield'/0).
 not_always_present(+PI) is semidet
True when some predicate is known to be part of the state but is not available in this version.
  190not_always_present(_:win_folder/2) :-
  191    \+ current_prolog_flag(windows, true).
  192not_always_present(_:win_add_dll_directory/2) :-
  193    \+ current_prolog_flag(windows, true).
  194
  195
  196goal_pi(M:Head, M:Name/Arity) :-
  197    functor(Head, Name, Arity).
 list_autoload is det
Report predicates that may be auto-loaded. These are predicates that are not defined, but will be loaded on demand if referenced.
See also
- autoload/0
To be done
- This predicate uses an older mechanism for finding undefined predicates. Should be synchronized with list undefined.
  210list_autoload :-
  211    setup_call_cleanup(
  212        ( current_prolog_flag(access_level, OldLevel),
  213          current_prolog_flag(autoload, OldAutoLoad),
  214          set_prolog_flag(access_level, system),
  215          set_prolog_flag(autoload, false)
  216        ),
  217        list_autoload_(OldLevel),
  218        ( set_prolog_flag(access_level, OldLevel),
  219          set_prolog_flag(autoload, OldAutoLoad)
  220        )).
  221
  222list_autoload_(SystemMode) :-
  223    (   setof(Lib-Pred,
  224              autoload_predicate(Module, Lib, Pred, SystemMode),
  225              Pairs),
  226        print_message(informational,
  227                      check(autoload(Module, Pairs))),
  228        fail
  229    ;   true
  230    ).
  231
  232autoload_predicate(Module, Library, Name/Arity, SystemMode) :-
  233    predicate_property(Module:Head, undefined),
  234    check_module_enabled(Module, SystemMode),
  235    (   \+ predicate_property(Module:Head, imported_from(_)),
  236        functor(Head, Name, Arity),
  237        '$find_library'(Module, Name, Arity, _LoadModule, Library),
  238        referenced(Module:Head, Module, _)
  239    ->  true
  240    ).
  241
  242check_module_enabled(_, system) :- !.
  243check_module_enabled(Module, _) :-
  244    \+ import_module(Module, system).
 referenced(+Predicate, ?Module, -ClauseRef) is nondet
True if clause ClauseRef references Predicate.
  250referenced(Term, Module, Ref) :-
  251    Goal = Module:_Head,
  252    current_predicate(_, Goal),
  253    '$get_predicate_attribute'(Goal, system, 0),
  254    \+ '$get_predicate_attribute'(Goal, imported, _),
  255    nth_clause(Goal, _, Ref),
  256    '$xr_member'(Ref, Term).
 list_redefined
Lists predicates that are defined in the global module user as well as in a normal module; that is, predicates for which the local definition overrules the global default definition.
  264list_redefined :-
  265    setup_call_cleanup(
  266        ( current_prolog_flag(access_level, OldLevel),
  267          set_prolog_flag(access_level, system)
  268        ),
  269        list_redefined_,
  270        set_prolog_flag(access_level, OldLevel)).
  271
  272list_redefined_ :-
  273    current_module(Module),
  274    Module \== system,
  275    current_predicate(_, Module:Head),
  276    \+ predicate_property(Module:Head, imported_from(_)),
  277    (   global_module(Super),
  278        Super \== Module,
  279        '$c_current_predicate'(_, Super:Head),
  280        \+ redefined_ok(Head),
  281        '$syspreds':'$defined_predicate'(Super:Head),
  282        \+ predicate_property(Super:Head, (dynamic)),
  283        \+ predicate_property(Super:Head, imported_from(Module)),
  284        functor(Head, Name, Arity)
  285    ->  print_message(informational,
  286                      check(redefined(Module, Super, Name/Arity)))
  287    ),
  288    fail.
  289list_redefined_.
  290
  291redefined_ok('$mode'(_,_)).
  292redefined_ok('$pldoc'(_,_,_,_)).
  293redefined_ok('$pred_option'(_,_,_,_)).
  294redefined_ok('$table_mode'(_,_,_)).
  295redefined_ok('$tabled'(_,_)).
  296redefined_ok('$exported_op'(_,_,_)).
  297redefined_ok('$autoload'(_,_,_)).
  298
  299global_module(user).
  300global_module(system).
 list_cross_module_calls is det
List calls from one module to another using Module:Goal where the callee is not defined exported, public or multifile, i.e., where the callee should be considered private.
  308list_cross_module_calls :-
  309    list_cross_module_calls([]).
  310
  311list_cross_module_calls(Options) :-
  312    call_cleanup(
  313        list_cross_module_calls_guarded(Options),
  314        retractall(cross_module_call(_,_,_))).
  315
  316list_cross_module_calls_guarded(Options) :-
  317    merge_options(Options,
  318                  [ module_class([user])
  319                  ],
  320                  WalkOptions),
  321    prolog_walk_code([ trace_reference(_),
  322                       trace_condition(cross_module_call),
  323                       on_trace(write_call)
  324                     | WalkOptions
  325                     ]).
  326
  327:- thread_local
  328    cross_module_call/3.  329
  330:- public
  331    cross_module_call/2,
  332    write_call/3.  333
  334cross_module_call(Callee, Context) :-
  335    \+ same_module_call(Callee, Context).
  336
  337same_module_call(Callee, Context) :-
  338    caller_module(Context, MCaller),
  339    Callee = (MCallee:_),
  340    (   (   MCaller = MCallee
  341        ;   predicate_property(Callee, exported)
  342        ;   predicate_property(Callee, built_in)
  343        ;   predicate_property(Callee, public)
  344        ;   clause_property(Context.get(clause), module(MCallee))
  345        ;   predicate_property(Callee, multifile)
  346        )
  347    ->  true
  348    ).
  349
  350caller_module(Context, MCaller) :-
  351    Caller = Context.caller,
  352    (   Caller = (MCaller:_)
  353    ->  true
  354    ;   Caller == '<initialization>',
  355        MCaller = Context.module
  356    ).
  357
  358write_call(Callee, Caller, Position) :-
  359    cross_module_call(Callee, Caller, Position),
  360    !.
  361write_call(Callee, Caller, Position) :-
  362    (   cross_module_call(_,_,_)
  363    ->  true
  364    ;   print_message(warning, check(cross_module_calls))
  365    ),
  366    asserta(cross_module_call(Callee, Caller, Position)),
  367    print_message(warning,
  368                  check(cross_module_call(Callee, Caller, Position))).
 list_void_declarations is det
List predicates that have declared attributes, but no clauses.
  374list_void_declarations :-
  375    P = _:_,
  376    (   predicate_property(P, undefined),
  377        (   '$get_predicate_attribute'(P, meta_predicate, Pattern),
  378            print_message(warning,
  379                          check(void_declaration(P, meta_predicate(Pattern))))
  380        ;   void_attribute(Attr),
  381            '$get_predicate_attribute'(P, Attr, 1),
  382            print_message(warning,
  383                          check(void_declaration(P, Attr)))
  384        ),
  385        fail
  386    ;   predicate_property(P, discontiguous),
  387        \+ (predicate_property(P, number_of_clauses(N)), N > 0),
  388        print_message(warning,
  389                      check(void_declaration(P, discontiguous))),
  390        fail
  391    ;   true
  392    ).
  393
  394void_attribute(public).
  395void_attribute(volatile).
  396void_attribute(det).
 list_trivial_fails is det
 list_trivial_fails(+Options) is det
List goals that trivially fail because there is no matching clause. Options:
module_class(+Classes)
Process modules of the given Classes. The default for classes is [user]. For example, to include the libraries into the examination, use [user,library].
  409:- thread_local
  410    trivial_fail/2.  411
  412list_trivial_fails :-
  413    list_trivial_fails([]).
  414
  415list_trivial_fails(Options) :-
  416    merge_options(Options,
  417                  [ module_class([user]),
  418                    infer_meta_predicates(false),
  419                    autoload(false),
  420                    evaluate(false),
  421                    trace_reference(_),
  422                    on_trace(check_trivial_fail)
  423                  ],
  424                  WalkOptions),
  425
  426    prolog_walk_code([ source(false)
  427                     | WalkOptions
  428                     ]),
  429    findall(CRef, retract(trivial_fail(clause(CRef), _)), Clauses),
  430    (   Clauses == []
  431    ->  true
  432    ;   print_message(warning, check(trivial_failures)),
  433        prolog_walk_code([ clauses(Clauses)
  434                         | WalkOptions
  435                         ]),
  436        findall(Goal-From, retract(trivial_fail(From, Goal)), Pairs),
  437        keysort(Pairs, Sorted),
  438        group_pairs_by_key(Sorted, Grouped),
  439        maplist(report_trivial_fail, Grouped)
  440    ).
 trivial_fail_goal(:Goal)
Multifile hook that tells list_trivial_fails/0 to accept Goal as valid.
  447trivial_fail_goal(pce_expansion:pce_class(_, _, template, _, _, _)).
  448trivial_fail_goal(pce_host:property(system_source_prefix(_))).
  449
  450:- public
  451    check_trivial_fail/3.  452
  453check_trivial_fail(MGoal0, _Caller, From) :-
  454    (   MGoal0 = M:Goal,
  455        atom(M),
  456        callable(Goal),
  457        predicate_property(MGoal0, interpreted),
  458        \+ predicate_property(MGoal0, dynamic),
  459        \+ predicate_property(MGoal0, multifile),
  460        \+ trivial_fail_goal(MGoal0)
  461    ->  (   predicate_property(MGoal0, meta_predicate(Meta))
  462        ->  qualify_meta_goal(MGoal0, Meta, MGoal)
  463        ;   MGoal = MGoal0
  464        ),
  465        (   clause(MGoal, _)
  466        ->  true
  467        ;   assertz(trivial_fail(From, MGoal))
  468        )
  469    ;   true
  470    ).
  471
  472report_trivial_fail(Goal-FromList) :-
  473    print_message(warning, check(trivial_failure(Goal, FromList))).
 qualify_meta_goal(+Module, +MetaSpec, +Goal, -QualifiedGoal)
Qualify a goal if the goal calls a meta predicate
  479qualify_meta_goal(M:Goal0, Meta, M:Goal) :-
  480    functor(Goal0, F, N),
  481    functor(Goal, F, N),
  482    qualify_meta_goal(1, M, Meta, Goal0, Goal).
  483
  484qualify_meta_goal(N, M, Meta, Goal0, Goal) :-
  485    arg(N, Meta,  ArgM),
  486    !,
  487    arg(N, Goal0, Arg0),
  488    arg(N, Goal,  Arg),
  489    N1 is N + 1,
  490    (   module_qualified(ArgM)
  491    ->  add_module(Arg0, M, Arg)
  492    ;   Arg = Arg0
  493    ),
  494    meta_goal(N1, Meta, Goal0, Goal).
  495meta_goal(_, _, _, _).
  496
  497add_module(Arg, M, M:Arg) :-
  498    var(Arg),
  499    !.
  500add_module(M:Arg, _, MArg) :-
  501    !,
  502    add_module(Arg, M, MArg).
  503add_module(Arg, M, M:Arg).
  504
  505module_qualified(N) :- integer(N), !.
  506module_qualified(:).
  507module_qualified(^).
 list_strings is det
 list_strings(+Options) is det
List strings that appear in clauses. This predicate is used to find portability issues for changing the Prolog flag double_quotes from codes to string, creating packed string objects. Warnings may be suppressed using the following multifile hooks:
See also
- Prolog flag double_quotes.
  525list_strings :-
  526    list_strings([module_class([user])]).
  527
  528list_strings(Options) :-
  529    (   prolog_program_clause(ClauseRef, Options),
  530        clause(Head, Body, ClauseRef),
  531        \+ ( predicate_indicator(Head, PI),
  532             string_predicate(PI)
  533           ),
  534        make_clause(Head, Body, Clause),
  535        findall(T,
  536                (   sub_term(T, Head),
  537                    string(T)
  538                ;   Head = M:_,
  539                    goal_in_body(Goal, M, Body),
  540                    (   valid_string_goal(Goal)
  541                    ->  fail
  542                    ;   sub_term(T, Goal),
  543                        string(T)
  544                    )
  545                ), Ts0),
  546        sort(Ts0, Ts),
  547        member(T, Ts),
  548        message_context(ClauseRef, T, Clause, Context),
  549        print_message(warning,
  550                      check(string_in_clause(T, Context))),
  551        fail
  552    ;   true
  553    ).
  554
  555make_clause(Head, true, Head) :- !.
  556make_clause(Head, Body, (Head:-Body)).
 list_rationals is det
 list_rationals(+Options) is det
List rational numbers that appear in clauses. This predicate is used to find portability issues for changing the Prolog flag rational_syntax to natural, creating rational numbers from <integer>/<nonneg>. Options:
module_class(+Classes)
Determines the modules classes processed. By default only user code is processed. See prolog_program_clause/2.
arithmetic(+Bool)
If true (default false) also warn on rationals appearing in arithmetic expressions.
See also
- Prolog flag rational_syntax and prefer_rationals.
  575list_rationals :-
  576    list_rationals([module_class([user])]).
  577
  578list_rationals(Options) :-
  579    (   option(arithmetic(DoArith), Options, false),
  580        prolog_program_clause(ClauseRef, Options),
  581        clause(Head, Body, ClauseRef),
  582        make_clause(Head, Body, Clause),
  583        findall(T,
  584                (   sub_term(T, Head),
  585                    rational(T),
  586                    \+ integer(T)
  587                ;   Head = M:_,
  588                    goal_in_body(Goal, M, Body),
  589                    nonvar(Goal),
  590                    (   DoArith == false,
  591                        valid_rational_goal(Goal)
  592                    ->  fail
  593                    ;   sub_term(T, Goal),
  594                        rational(T),
  595                        \+ integer(T)
  596                    )
  597                ), Ts0),
  598        sort(Ts0, Ts),
  599        member(T, Ts),
  600        message_context(ClauseRef, T, Clause, Context),
  601        print_message(warning,
  602                      check(rational_in_clause(T, Context))),
  603        fail
  604    ;   true
  605    ).
  606
  607
  608valid_rational_goal(_ is _).
  609valid_rational_goal(_ =:= _).
  610valid_rational_goal(_ < _).
  611valid_rational_goal(_ > _).
  612valid_rational_goal(_ =< _).
  613valid_rational_goal(_ >= _).
 list_format_errors is det
 list_format_errors(+Options) is det
List argument errors for format/2,3.
  621list_format_errors :-
  622    list_format_errors([module_class([user])]).
  623
  624list_format_errors(Options) :-
  625    (   prolog_program_clause(ClauseRef, Options),
  626        clause(Head, Body, ClauseRef),
  627        make_clause(Head, Body, Clause),
  628        Head = M:_,
  629        goal_in_body(Goal, M, Body),
  630        format_warning(Goal, Msg),
  631        message_context(ClauseRef, Goal, Clause, Context),
  632        print_message(warning, check(Msg, Goal, Context)),
  633        fail
  634    ;   true
  635    ).
  636
  637format_warning(system:format(Format, Args), Msg) :-
  638    ground(Format),
  639    (   is_list(Args)
  640    ->  length(Args, ArgC)
  641    ;   nonvar(Args)
  642    ->  ArgC = 1
  643    ),
  644    E = error(Formal,_),
  645    catch(format_types(Format, Types), E, true),
  646    (   var(Formal)
  647    ->  length(Types, TypeC),
  648        TypeC =\= ArgC,
  649        Msg = format_argc(TypeC, ArgC)
  650    ;   Msg = format_template(Formal)
  651    ).
  652format_warning(system:format(_Stream, Format, Args), Msg) :-
  653    format_warning(system:format(Format, Args), Msg).
  654format_warning(prolog_debug:debug(_Channel, Format, Args), Msg) :-
  655    format_warning(system:format(Format, Args), Msg).
 goal_in_body(-G, +M, +Body) is nondet
True when G is a goal called from Body.
  662goal_in_body(M:G, M, G) :-
  663    var(G),
  664    !.
  665goal_in_body(G, _, M:G0) :-
  666    atom(M),
  667    !,
  668    goal_in_body(G, M, G0).
  669goal_in_body(G, M, Control) :-
  670    nonvar(Control),
  671    control(Control, Subs),
  672    !,
  673    member(Sub, Subs),
  674    goal_in_body(G, M, Sub).
  675goal_in_body(G, M, G0) :-
  676    callable(G0),
  677    (   atom(M)
  678    ->  TM = M
  679    ;   TM = system
  680    ),
  681    predicate_property(TM:G0, meta_predicate(Spec)),
  682    !,
  683    (   strip_goals(G0, Spec, G1),
  684        simple_goal_in_body(G, M, G1)
  685    ;   arg(I, Spec, Meta),
  686        arg(I, G0, G1),
  687        extend(Meta, G1, G2),
  688        goal_in_body(G, M, G2)
  689    ).
  690goal_in_body(G, M, G0) :-
  691    simple_goal_in_body(G, M, G0).
  692
  693simple_goal_in_body(G, M, G0) :-
  694    (   atom(M),
  695        callable(G0),
  696        predicate_property(M:G0, imported_from(M2))
  697    ->  G = M2:G0
  698    ;   G = M:G0
  699    ).
  700
  701control((A,B), [A,B]).
  702control((A;B), [A,B]).
  703control((A->B), [A,B]).
  704control((A*->B), [A,B]).
  705control((\+A), [A]).
  706
  707strip_goals(G0, Spec, G) :-
  708    functor(G0, Name, Arity),
  709    functor(G,  Name, Arity),
  710    strip_goal_args(1, G0, Spec, G).
  711
  712strip_goal_args(I, G0, Spec, G) :-
  713    arg(I, G0, A0),
  714    !,
  715    arg(I, Spec, M),
  716    (   extend(M, A0, _)
  717    ->  arg(I, G, '<meta-goal>')
  718    ;   arg(I, G, A0)
  719    ),
  720    I2 is I + 1,
  721    strip_goal_args(I2, G0, Spec, G).
  722strip_goal_args(_, _, _, _).
  723
  724extend(I, G0, G) :-
  725    callable(G0),
  726    integer(I), I>0,
  727    !,
  728    length(L, I),
  729    extend_list(G0, L, G).
  730extend(0, G, G).
  731extend(^, G, G).
  732
  733extend_list(M:G0, L, M:G) :-
  734    !,
  735    callable(G0),
  736    extend_list(G0, L, G).
  737extend_list(G0, L, G) :-
  738    G0 =.. List,
  739    append(List, L, All),
  740    G =.. All.
 message_context(+ClauseRef, +Term, +Clause, -Pos) is det
Find an as accurate as possible location for Term in Clause.
  747message_context(ClauseRef, Term, Clause, file_term_position(File, TermPos)) :-
  748    clause_info(ClauseRef, File, Layout, _Vars),
  749    (   Term = _:Goal,
  750        prolog_codewalk:subterm_pos(Goal, Clause, ==, Layout, TermPos)
  751    ;   prolog_codewalk:subterm_pos(Term, Clause, ==, Layout, TermPos)
  752    ),
  753    !.
  754message_context(ClauseRef, _String, _Clause, file(File, Line, -1, _)) :-
  755    clause_property(ClauseRef, file(File)),
  756    clause_property(ClauseRef, line_count(Line)),
  757    !.
  758message_context(ClauseRef, _String, _Clause, clause(ClauseRef)).
  759
  760
  761:- meta_predicate
  762    predicate_indicator(:, -).  763
  764predicate_indicator(Module:Head, Module:Name/Arity) :-
  765    functor(Head, Name, Arity).
  766predicate_indicator(Module:Head, Module:Name//DCGArity) :-
  767    functor(Head, Name, Arity),
  768    DCGArity is Arity-2.
 string_predicate(:PredicateIndicator)
Multifile hook to disable list_strings/0 on the given predicate. This is typically used for facts that store strings.
  775string_predicate(_:'$pldoc'/4).
  776string_predicate(pce_principal:send_implementation/3).
  777string_predicate(pce_principal:pce_lazy_get_method/3).
  778string_predicate(pce_principal:pce_lazy_send_method/3).
  779string_predicate(pce_principal:pce_class/6).
  780string_predicate(prolog_xref:pred_comment/4).
  781string_predicate(prolog_xref:module_comment/3).
  782string_predicate(pldoc_process:structured_comment//2).
  783string_predicate(pldoc_process:structured_command_start/3).
  784string_predicate(pldoc_process:separator_line//0).
  785string_predicate(pldoc_register:mydoc/3).
  786string_predicate(http_header:separators/1).
 valid_string_goal(+Goal) is semidet
Multifile hook that qualifies Goal as valid for list_strings/0. For example, format("Hello world~n") is considered proper use of string constants.
  794% system predicates
  795valid_string_goal(system:format(S)) :- string(S).
  796valid_string_goal(system:format(S,_)) :- string(S).
  797valid_string_goal(system:format(_,S,_)) :- string(S).
  798valid_string_goal(system:string_codes(S,_)) :- string(S).
  799valid_string_goal(system:string_code(_,S,_)) :- string(S).
  800valid_string_goal(system:throw(msg(S,_))) :- string(S).
  801valid_string_goal('$dcg':phrase(S,_,_)) :- string(S).
  802valid_string_goal('$dcg':phrase(S,_)) :- string(S).
  803valid_string_goal(system: is(_,_)).     % arithmetic allows for "x"
  804valid_string_goal(system: =:=(_,_)).
  805valid_string_goal(system: >(_,_)).
  806valid_string_goal(system: <(_,_)).
  807valid_string_goal(system: >=(_,_)).
  808valid_string_goal(system: =<(_,_)).
  809% library stuff
  810valid_string_goal(dcg_basics:string_without(S,_,_,_)) :- string(S).
  811valid_string_goal(git:read_url(S,_,_)) :- string(S).
  812valid_string_goal(tipc:tipc_subscribe(_,_,_,_,S)) :- string(S).
  813valid_string_goal(charsio:format_to_chars(Format,_,_)) :- string(Format).
  814valid_string_goal(charsio:format_to_chars(Format,_,_,_)) :- string(Format).
  815valid_string_goal(codesio:format_to_codes(Format,_,_)) :- string(Format).
  816valid_string_goal(codesio:format_to_codes(Format,_,_,_)) :- string(Format).
  817
  818
  819                 /*******************************
  820                 *        EXTENSION HOOKS       *
  821                 *******************************/
 checker(:Goal, +Message:text) is nondet
Register code validation routines. Each clause defines a Goal which performs a consistency check executed by check/0. Message is a short description of the check. For example, assuming the my_checks module defines a predicate list_format_mistakes/0:
:- multifile check:checker/2.
check:checker(my_checks:list_format_mistakes,
              "errors with format/2 arguments").

The predicate is dynamic, so you can disable checks with retract/1. For example, to stop reporting redefined predicates:

retract(check:checker(list_redefined,_)).
  843checker(list_undefined,         'undefined predicates').
  844checker(list_trivial_fails,     'trivial failures').
  845checker(list_format_errors,     'format/2,3 and debug/3 templates').
  846checker(list_redefined,         'redefined system and global predicates').
  847checker(list_void_declarations, 'predicates with declarations but without clauses').
  848checker(list_autoload,          'predicates that need autoloading').
  849
  850
  851                 /*******************************
  852                 *            MESSAGES          *
  853                 *******************************/
  854
  855:- multifile
  856    prolog:message/3.  857
  858prolog:message(check(pass(Comment))) -->
  859    [ 'Checking ~w ...'-[Comment] ].
  860prolog:message(check(find_references(Preds))) -->
  861    { length(Preds, N)
  862    },
  863    [ 'Scanning for references to ~D possibly undefined predicates'-[N] ].
  864prolog:message(check(undefined_procedures, Grouped)) -->
  865    [ 'The predicates below are not defined. If these are defined', nl,
  866      'at runtime using assert/1, use :- dynamic Name/Arity.', nl, nl
  867    ],
  868    undefined_procedures(Grouped).
  869prolog:message(check(undefined_unreferenced_predicates)) -->
  870    [ 'The predicates below are not defined, and are not', nl,
  871      'referenced.', nl, nl
  872    ].
  873prolog:message(check(undefined_unreferenced(Pred))) -->
  874    predicate(Pred).
  875prolog:message(check(autoload(Module, Pairs))) -->
  876    { module_property(Module, file(Path))
  877    },
  878    !,
  879    [ 'Into module ~w ('-[Module] ],
  880    short_filename(Path),
  881    [ ')', nl ],
  882    autoload(Pairs).
  883prolog:message(check(autoload(Module, Pairs))) -->
  884    [ 'Into module ~w'-[Module], nl ],
  885    autoload(Pairs).
  886prolog:message(check(redefined(In, From, Pred))) -->
  887    predicate(In:Pred),
  888    redefined(In, From).
  889prolog:message(check(cross_module_calls)) -->
  890    [ 'Qualified calls to private predicates'-[] ].
  891prolog:message(check(cross_module_call(Callee, _Caller, Location))) -->
  892    { pi_head(PI, Callee) },
  893    [ '  '-[] ],
  894    '$messages':swi_location(Location),
  895    [ 'Cross-module call to ~p'-[PI] ].
  896prolog:message(check(trivial_failures)) -->
  897    [ 'The following goals fail because there are no matching clauses.' ].
  898prolog:message(check(trivial_failure(Goal, Refs))) -->
  899    { map_list_to_pairs(sort_reference_key, Refs, Keyed),
  900      keysort(Keyed, KeySorted),
  901      pairs_values(KeySorted, SortedRefs)
  902    },
  903    goal(Goal),
  904    [ ', which is called from'-[], nl ],
  905    referenced_by(SortedRefs).
  906prolog:message(check(string_in_clause(String, Context))) -->
  907    '$messages':swi_location(Context),
  908    [ 'String ~q'-[String] ].
  909prolog:message(check(rational_in_clause(String, Context))) -->
  910    '$messages':swi_location(Context),
  911    [ 'Rational ~q'-[String] ].
  912prolog:message(check(Msg, Goal, Context)) -->
  913    '$messages':swi_location(Context),
  914    { pi_head(PI, Goal) },
  915    [ nl, '    '-[] ],
  916    predicate(PI),
  917    [ ': '-[] ],
  918    check_message(Msg).
  919prolog:message(check(void_declaration(P, Decl))) -->
  920    predicate(P),
  921    [ ' is declared as ~p, but has no clauses'-[Decl] ].
  922
  923undefined_procedures([]) -->
  924    [].
  925undefined_procedures([H|T]) -->
  926    undefined_procedure(H),
  927    undefined_procedures(T).
  928
  929undefined_procedure(Pred-Refs) -->
  930    { map_list_to_pairs(sort_reference_key, Refs, Keyed),
  931      keysort(Keyed, KeySorted),
  932      pairs_values(KeySorted, SortedRefs)
  933    },
  934    predicate(Pred),
  935    [ ', which is referenced by', nl ],
  936    referenced_by(SortedRefs).
  937
  938redefined(user, system) -->
  939    [ '~t~30| System predicate redefined globally' ].
  940redefined(_, system) -->
  941    [ '~t~30| Redefined system predicate' ].
  942redefined(_, user) -->
  943    [ '~t~30| Redefined global predicate' ].
  944
  945goal(user:Goal) -->
  946    !,
  947    [ '~p'-[Goal] ].
  948goal(Goal) -->
  949    !,
  950    [ '~p'-[Goal] ].
  951
  952predicate(Module:Name/Arity) -->
  953    { atom(Module),
  954      atom(Name),
  955      integer(Arity),
  956      functor(Head, Name, Arity),
  957      predicate_name(Module:Head, PName)
  958    },
  959    !,
  960    [ '~w'-[PName] ].
  961predicate(Module:Head) -->
  962    { atom(Module),
  963      callable(Head),
  964      predicate_name(Module:Head, PName)
  965    },
  966    !,
  967    [ '~w'-[PName] ].
  968predicate(Name/Arity) -->
  969    { atom(Name),
  970      integer(Arity)
  971    },
  972    !,
  973    predicate(user:Name/Arity).
  974
  975autoload([]) -->
  976    [].
  977autoload([Lib-Pred|T]) -->
  978    [ '    ' ],
  979    predicate(Pred),
  980    [ '~t~24| from ' ],
  981    short_filename(Lib),
  982    [ nl ],
  983    autoload(T).
 sort_reference_key(+Reference, -Key) is det
Create a stable key for sorting references to predicates.
  989sort_reference_key(Term, key(M:Name/Arity, N, ClausePos)) :-
  990    clause_ref(Term, ClauseRef, ClausePos),
  991    !,
  992    nth_clause(Pred, N, ClauseRef),
  993    strip_module(Pred, M, Head),
  994    functor(Head, Name, Arity).
  995sort_reference_key(Term, Term).
  996
  997clause_ref(clause_term_position(ClauseRef, TermPos), ClauseRef, ClausePos) :-
  998    arg(1, TermPos, ClausePos).
  999clause_ref(clause(ClauseRef), ClauseRef, 0).
 1000
 1001
 1002referenced_by([]) -->
 1003    [].
 1004referenced_by([Ref|T]) -->
 1005    ['\t'], prolog:message_location(Ref),
 1006            predicate_indicator(Ref),
 1007    [ nl ],
 1008    referenced_by(T).
 1009
 1010predicate_indicator(clause_term_position(ClauseRef, _)) -->
 1011    { nonvar(ClauseRef) },
 1012    !,
 1013    predicate_indicator(clause(ClauseRef)).
 1014predicate_indicator(clause(ClauseRef)) -->
 1015    { clause_name(ClauseRef, Name) },
 1016    [ '~w'-[Name] ].
 1017predicate_indicator(file_term_position(_,_)) -->
 1018    [ '(initialization)' ].
 1019predicate_indicator(file(_,_,_,_)) -->
 1020    [ '(initialization)' ].
 1021
 1022
 1023short_filename(Path) -->
 1024    { short_filename(Path, Spec)
 1025    },
 1026    [ '~q'-[Spec] ].
 1027
 1028short_filename(Path, Spec) :-
 1029    absolute_file_name('', Here),
 1030    atom_concat(Here, Local0, Path),
 1031    !,
 1032    remove_leading_slash(Local0, Spec).
 1033short_filename(Path, Spec) :-
 1034    findall(LenAlias, aliased_path(Path, LenAlias), Keyed),
 1035    keysort(Keyed, [_-Spec|_]).
 1036short_filename(Path, Path).
 1037
 1038aliased_path(Path, Len-Spec) :-
 1039    setof(Alias, Spec^(user:file_search_path(Alias, Spec)), Aliases),
 1040    member(Alias, Aliases),
 1041    Term =.. [Alias, '.'],
 1042    absolute_file_name(Term,
 1043                       [ file_type(directory),
 1044                         file_errors(fail),
 1045                         solutions(all)
 1046                       ], Prefix),
 1047    atom_concat(Prefix, Local0, Path),
 1048    remove_leading_slash(Local0, Local),
 1049    atom_length(Local, Len),
 1050    Spec =.. [Alias, Local].
 1051
 1052remove_leading_slash(Path, Local) :-
 1053    atom_concat(/, Local, Path),
 1054    !.
 1055remove_leading_slash(Path, Path).
 1056
 1057check_message(format_argc(Expected, InList)) -->
 1058    [ 'Template requires ~w arguments, got ~w'-[Expected, InList] ].
 1059check_message(format_template(Formal)) -->
 1060    { message_to_string(error(Formal, _), Msg) },
 1061    [ 'Invalid template: ~s'-[Msg] ]