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_void_declarations/0,     % list declarations with no clauses
   44          list_trivial_fails/0,         % list goals that trivially fail
   45          list_trivial_fails/1,         % +Options
   46          list_format_errors/0,         % list calls to format with wrong args
   47          list_format_errors/1,		% +Options
   48          list_strings/0,               % list string objects in clauses
   49          list_strings/1,               % +Options
   50          list_rationals/0,		% list rational objects in clauses
   51          list_rationals/1              % +Options
   52        ]).   53:- autoload(library(apply),[maplist/2]).   54:- autoload(library(lists),[member/2,append/3]).   55:- autoload(library(occurs),[sub_term/2]).   56:- autoload(library(option),[merge_options/3,option/3]).   57:- autoload(library(pairs),
   58	    [group_pairs_by_key/2,map_list_to_pairs/3,pairs_values/2]).   59:- autoload(library(prolog_clause),
   60	    [clause_info/4,predicate_name/2,clause_name/2]).   61:- autoload(library(prolog_code),[pi_head/2]).   62:- autoload(library(prolog_codewalk),
   63	    [prolog_walk_code/1,prolog_program_clause/2]).   64:- autoload(library(prolog_format),[format_types/2]).   65
   66
   67:- set_prolog_flag(generate_debug_info, false).   68
   69:- multifile
   70       trivial_fail_goal/1,
   71       string_predicate/1,
   72       valid_string_goal/1,
   73       checker/2.   74
   75:- 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 */
   90:- predicate_options(list_undefined/1, 1,
   91                     [ module_class(list(oneof([user,library,system])))
   92                     ]).
 check is det
Run all consistency checks defined by checker/2. Checks enabled by default are:
  108check :-
  109    checker(Checker, Message),
  110    print_message(informational,check(pass(Message))),
  111    catch(Checker,E,print_message(error,E)),
  112    fail.
  113check.
 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
  130:- thread_local
  131    undef/2.  132
  133list_undefined :-
  134    list_undefined([]).
  135
  136list_undefined(Options) :-
  137    merge_options(Options,
  138                  [ module_class([user])
  139                  ],
  140                  WalkOptions),
  141    call_cleanup(
  142        prolog_walk_code([ undefined(trace),
  143                           on_trace(found_undef)
  144                         | WalkOptions
  145                         ]),
  146        collect_undef(Grouped)),
  147    (   Grouped == []
  148    ->  true
  149    ;   print_message(warning, check(undefined_procedures, Grouped))
  150    ).
  151
  152% The following predicates are used from library(prolog_autoload).
  153
  154:- public
  155    found_undef/3,
  156    collect_undef/1.  157
  158collect_undef(Grouped) :-
  159    findall(PI-From, retract(undef(PI, From)), Pairs),
  160    keysort(Pairs, Sorted),
  161    group_pairs_by_key(Sorted, Grouped).
  162
  163found_undef(To, _Caller, From) :-
  164    goal_pi(To, PI),
  165    (   undef(PI, From)
  166    ->  true
  167    ;   compiled(PI)
  168    ->  true
  169    ;   not_always_present(PI)
  170    ->  true
  171    ;   assertz(undef(PI,From))
  172    ).
  173
  174compiled(system:'$call_cleanup'/0).     % compiled to VM instructions
  175compiled(system:'$catch'/0).
  176compiled(system:'$cut'/0).
  177compiled(system:'$reset'/0).
  178compiled(system:'$call_continuation'/1).
  179compiled(system:'$shift'/1).
  180compiled('$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.
  187not_always_present(_:win_folder/2) :-
  188    \+ current_prolog_flag(windows, true).
  189not_always_present(_:win_add_dll_directory/2) :-
  190    \+ current_prolog_flag(windows, true).
  191
  192
  193goal_pi(M:Head, M:Name/Arity) :-
  194    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.
  207list_autoload :-
  208    setup_call_cleanup(
  209        ( current_prolog_flag(access_level, OldLevel),
  210          current_prolog_flag(autoload, OldAutoLoad),
  211          set_prolog_flag(access_level, system),
  212          set_prolog_flag(autoload, false)
  213        ),
  214        list_autoload_(OldLevel),
  215        ( set_prolog_flag(access_level, OldLevel),
  216          set_prolog_flag(autoload, OldAutoLoad)
  217        )).
  218
  219list_autoload_(SystemMode) :-
  220    (   setof(Lib-Pred,
  221              autoload_predicate(Module, Lib, Pred, SystemMode),
  222              Pairs),
  223        print_message(informational,
  224                      check(autoload(Module, Pairs))),
  225        fail
  226    ;   true
  227    ).
  228
  229autoload_predicate(Module, Library, Name/Arity, SystemMode) :-
  230    predicate_property(Module:Head, undefined),
  231    check_module_enabled(Module, SystemMode),
  232    (   \+ predicate_property(Module:Head, imported_from(_)),
  233        functor(Head, Name, Arity),
  234        '$find_library'(Module, Name, Arity, _LoadModule, Library),
  235        referenced(Module:Head, Module, _)
  236    ->  true
  237    ).
  238
  239check_module_enabled(_, system) :- !.
  240check_module_enabled(Module, _) :-
  241    \+ import_module(Module, system).
 referenced(+Predicate, ?Module, -ClauseRef) is nondet
True if clause ClauseRef references Predicate.
  247referenced(Term, Module, Ref) :-
  248    Goal = Module:_Head,
  249    current_predicate(_, Goal),
  250    '$get_predicate_attribute'(Goal, system, 0),
  251    \+ '$get_predicate_attribute'(Goal, imported, _),
  252    nth_clause(Goal, _, Ref),
  253    '$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.
  261list_redefined :-
  262    setup_call_cleanup(
  263        ( current_prolog_flag(access_level, OldLevel),
  264          set_prolog_flag(access_level, system)
  265        ),
  266        list_redefined_,
  267        set_prolog_flag(access_level, OldLevel)).
  268
  269list_redefined_ :-
  270    current_module(Module),
  271    Module \== system,
  272    current_predicate(_, Module:Head),
  273    \+ predicate_property(Module:Head, imported_from(_)),
  274    (   global_module(Super),
  275        Super \== Module,
  276        '$c_current_predicate'(_, Super:Head),
  277        \+ redefined_ok(Head),
  278        '$syspreds':'$defined_predicate'(Super:Head),
  279        \+ predicate_property(Super:Head, (dynamic)),
  280        \+ predicate_property(Super:Head, imported_from(Module)),
  281        functor(Head, Name, Arity)
  282    ->  print_message(informational,
  283                      check(redefined(Module, Super, Name/Arity)))
  284    ),
  285    fail.
  286list_redefined_.
  287
  288redefined_ok('$mode'(_,_)).
  289redefined_ok('$pldoc'(_,_,_,_)).
  290redefined_ok('$pred_option'(_,_,_,_)).
  291redefined_ok('$table_mode'(_,_,_)).
  292redefined_ok('$tabled'(_,_)).
  293
  294global_module(user).
  295global_module(system).
 list_void_declarations is det
List predicates that have declared attributes, but no clauses.
  301list_void_declarations :-
  302    P = _:_,
  303    (   predicate_property(P, undefined),
  304        (   '$get_predicate_attribute'(P, meta_predicate, Pattern),
  305            print_message(warning,
  306                          check(void_declaration(P, meta_predicate(Pattern))))
  307        ;   void_attribute(Attr),
  308            '$get_predicate_attribute'(P, Attr, 1),
  309            print_message(warning,
  310                          check(void_declaration(P, Attr)))
  311        ),
  312        fail
  313    ;   true
  314    ).
  315
  316void_attribute(public).
  317void_attribute(volatile).
 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].
  330:- thread_local
  331    trivial_fail/2.  332
  333list_trivial_fails :-
  334    list_trivial_fails([]).
  335
  336list_trivial_fails(Options) :-
  337    merge_options(Options,
  338                  [ module_class([user]),
  339                    infer_meta_predicates(false),
  340                    autoload(false),
  341                    evaluate(false),
  342                    trace_reference(_),
  343                    on_trace(check_trivial_fail)
  344                  ],
  345                  WalkOptions),
  346
  347    prolog_walk_code([ source(false)
  348                     | WalkOptions
  349                     ]),
  350    findall(CRef, retract(trivial_fail(clause(CRef), _)), Clauses),
  351    (   Clauses == []
  352    ->  true
  353    ;   print_message(warning, check(trivial_failures)),
  354        prolog_walk_code([ clauses(Clauses)
  355                         | WalkOptions
  356                         ]),
  357        findall(Goal-From, retract(trivial_fail(From, Goal)), Pairs),
  358        keysort(Pairs, Sorted),
  359        group_pairs_by_key(Sorted, Grouped),
  360        maplist(report_trivial_fail, Grouped)
  361    ).
 trivial_fail_goal(:Goal)
Multifile hook that tells list_trivial_fails/0 to accept Goal as valid.
  368trivial_fail_goal(pce_expansion:pce_class(_, _, template, _, _, _)).
  369trivial_fail_goal(pce_host:property(system_source_prefix(_))).
  370
  371:- public
  372    check_trivial_fail/3.  373
  374check_trivial_fail(MGoal0, _Caller, From) :-
  375    (   MGoal0 = M:Goal,
  376        atom(M),
  377        callable(Goal),
  378        predicate_property(MGoal0, interpreted),
  379        \+ predicate_property(MGoal0, dynamic),
  380        \+ predicate_property(MGoal0, multifile),
  381        \+ trivial_fail_goal(MGoal0)
  382    ->  (   predicate_property(MGoal0, meta_predicate(Meta))
  383        ->  qualify_meta_goal(MGoal0, Meta, MGoal)
  384        ;   MGoal = MGoal0
  385        ),
  386        (   clause(MGoal, _)
  387        ->  true
  388        ;   assertz(trivial_fail(From, MGoal))
  389        )
  390    ;   true
  391    ).
  392
  393report_trivial_fail(Goal-FromList) :-
  394    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
  400qualify_meta_goal(M:Goal0, Meta, M:Goal) :-
  401    functor(Goal0, F, N),
  402    functor(Goal, F, N),
  403    qualify_meta_goal(1, M, Meta, Goal0, Goal).
  404
  405qualify_meta_goal(N, M, Meta, Goal0, Goal) :-
  406    arg(N, Meta,  ArgM),
  407    !,
  408    arg(N, Goal0, Arg0),
  409    arg(N, Goal,  Arg),
  410    N1 is N + 1,
  411    (   module_qualified(ArgM)
  412    ->  add_module(Arg0, M, Arg)
  413    ;   Arg = Arg0
  414    ),
  415    meta_goal(N1, Meta, Goal0, Goal).
  416meta_goal(_, _, _, _).
  417
  418add_module(Arg, M, M:Arg) :-
  419    var(Arg),
  420    !.
  421add_module(M:Arg, _, MArg) :-
  422    !,
  423    add_module(Arg, M, MArg).
  424add_module(Arg, M, M:Arg).
  425
  426module_qualified(N) :- integer(N), !.
  427module_qualified(:).
  428module_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.
  446list_strings :-
  447    list_strings([module_class([user])]).
  448
  449list_strings(Options) :-
  450    (   prolog_program_clause(ClauseRef, Options),
  451        clause(Head, Body, ClauseRef),
  452        \+ ( predicate_indicator(Head, PI),
  453             string_predicate(PI)
  454           ),
  455        make_clause(Head, Body, Clause),
  456        findall(T,
  457                (   sub_term(T, Head),
  458                    string(T)
  459                ;   Head = M:_,
  460                    goal_in_body(Goal, M, Body),
  461                    (   valid_string_goal(Goal)
  462                    ->  fail
  463                    ;   sub_term(T, Goal),
  464                        string(T)
  465                    )
  466                ), Ts0),
  467        sort(Ts0, Ts),
  468        member(T, Ts),
  469        message_context(ClauseRef, T, Clause, Context),
  470        print_message(warning,
  471                      check(string_in_clause(T, Context))),
  472        fail
  473    ;   true
  474    ).
  475
  476make_clause(Head, true, Head) :- !.
  477make_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.
  496list_rationals :-
  497    list_rationals([module_class([user])]).
  498
  499list_rationals(Options) :-
  500    (   option(arithmetic(DoArith), Options, false),
  501        prolog_program_clause(ClauseRef, Options),
  502        clause(Head, Body, ClauseRef),
  503        make_clause(Head, Body, Clause),
  504        findall(T,
  505                (   sub_term(T, Head),
  506                    rational(T),
  507                    \+ integer(T)
  508                ;   Head = M:_,
  509                    goal_in_body(Goal, M, Body),
  510                    nonvar(Goal),
  511                    (   DoArith == false,
  512                        valid_rational_goal(Goal)
  513                    ->  fail
  514                    ;   sub_term(T, Goal),
  515                        rational(T),
  516                        \+ integer(T)
  517                    )
  518                ), Ts0),
  519        sort(Ts0, Ts),
  520        member(T, Ts),
  521        message_context(ClauseRef, T, Clause, Context),
  522        print_message(warning,
  523                      check(rational_in_clause(T, Context))),
  524        fail
  525    ;   true
  526    ).
  527
  528
  529valid_rational_goal(_ is _).
  530valid_rational_goal(_ =:= _).
  531valid_rational_goal(_ < _).
  532valid_rational_goal(_ > _).
  533valid_rational_goal(_ =< _).
  534valid_rational_goal(_ >= _).
 list_format_errors is det
 list_format_errors(+Options) is det
List argument errors for format/2,3.
  542list_format_errors :-
  543    list_format_errors([module_class([user])]).
  544
  545list_format_errors(Options) :-
  546    (   prolog_program_clause(ClauseRef, Options),
  547        clause(Head, Body, ClauseRef),
  548        make_clause(Head, Body, Clause),
  549        Head = M:_,
  550        goal_in_body(Goal, M, Body),
  551        format_warning(Goal, Msg),
  552        message_context(ClauseRef, Goal, Clause, Context),
  553        print_message(warning, check(Msg, Goal, Context)),
  554        fail
  555    ;   true
  556    ).
  557
  558format_warning(system:format(Format, Args), Msg) :-
  559    ground(Format),
  560    (   is_list(Args)
  561    ->  length(Args, ArgC)
  562    ;   nonvar(Args)
  563    ->  ArgC = 1
  564    ),
  565    E = error(Formal,_),
  566    catch(format_types(Format, Types), E, true),
  567    (   var(Formal)
  568    ->  length(Types, TypeC),
  569        TypeC =\= ArgC,
  570        Msg = format_argc(TypeC, ArgC)
  571    ;   Msg = format_template(Formal)
  572    ).
  573format_warning(system:format(_Stream, Format, Args), Msg) :-
  574    format_warning(system:format(Format, Args), Msg).
  575format_warning(prolog_debug:debug(_Channel, Format, Args), Msg) :-
  576    format_warning(system:format(Format, Args), Msg).
 goal_in_body(-G, +M, +Body) is nondet
True when G is a goal called from Body.
  583goal_in_body(M:G, M, G) :-
  584    var(G),
  585    !.
  586goal_in_body(G, _, M:G0) :-
  587    atom(M),
  588    !,
  589    goal_in_body(G, M, G0).
  590goal_in_body(G, M, Control) :-
  591    nonvar(Control),
  592    control(Control, Subs),
  593    !,
  594    member(Sub, Subs),
  595    goal_in_body(G, M, Sub).
  596goal_in_body(G, M, G0) :-
  597    callable(G0),
  598    (   atom(M)
  599    ->  TM = M
  600    ;   TM = system
  601    ),
  602    predicate_property(TM:G0, meta_predicate(Spec)),
  603    !,
  604    (   strip_goals(G0, Spec, G1),
  605        simple_goal_in_body(G, M, G1)
  606    ;   arg(I, Spec, Meta),
  607        arg(I, G0, G1),
  608        extend(Meta, G1, G2),
  609        goal_in_body(G, M, G2)
  610    ).
  611goal_in_body(G, M, G0) :-
  612    simple_goal_in_body(G, M, G0).
  613
  614simple_goal_in_body(G, M, G0) :-
  615    (   atom(M),
  616        callable(G0),
  617        predicate_property(M:G0, imported_from(M2))
  618    ->  G = M2:G0
  619    ;   G = M:G0
  620    ).
  621
  622control((A,B), [A,B]).
  623control((A;B), [A,B]).
  624control((A->B), [A,B]).
  625control((A*->B), [A,B]).
  626control((\+A), [A]).
  627
  628strip_goals(G0, Spec, G) :-
  629    functor(G0, Name, Arity),
  630    functor(G,  Name, Arity),
  631    strip_goal_args(1, G0, Spec, G).
  632
  633strip_goal_args(I, G0, Spec, G) :-
  634    arg(I, G0, A0),
  635    !,
  636    arg(I, Spec, M),
  637    (   extend(M, A0, _)
  638    ->  arg(I, G, '<meta-goal>')
  639    ;   arg(I, G, A0)
  640    ),
  641    I2 is I + 1,
  642    strip_goal_args(I2, G0, Spec, G).
  643strip_goal_args(_, _, _, _).
  644
  645extend(I, G0, G) :-
  646    callable(G0),
  647    integer(I), I>0,
  648    !,
  649    length(L, I),
  650    extend_list(G0, L, G).
  651extend(0, G, G).
  652extend(^, G, G).
  653
  654extend_list(M:G0, L, M:G) :-
  655    !,
  656    callable(G0),
  657    extend_list(G0, L, G).
  658extend_list(G0, L, G) :-
  659    G0 =.. List,
  660    append(List, L, All),
  661    G =.. All.
 message_context(+ClauseRef, +Term, +Clause, -Pos) is det
Find an as accurate as possible location for Term in Clause.
  668message_context(ClauseRef, Term, Clause, file_term_position(File, TermPos)) :-
  669    clause_info(ClauseRef, File, Layout, _Vars),
  670    (   Term = _:Goal,
  671        prolog_codewalk:subterm_pos(Goal, Clause, ==, Layout, TermPos)
  672    ;   prolog_codewalk:subterm_pos(Term, Clause, ==, Layout, TermPos)
  673    ),
  674    !.
  675message_context(ClauseRef, _String, _Clause, file(File, Line, -1, _)) :-
  676    clause_property(ClauseRef, file(File)),
  677    clause_property(ClauseRef, line_count(Line)),
  678    !.
  679message_context(ClauseRef, _String, _Clause, clause(ClauseRef)).
  680
  681
  682:- meta_predicate
  683    predicate_indicator(:, -).  684
  685predicate_indicator(Module:Head, Module:Name/Arity) :-
  686    functor(Head, Name, Arity).
  687predicate_indicator(Module:Head, Module:Name//DCGArity) :-
  688    functor(Head, Name, Arity),
  689    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.
  696string_predicate(_:'$pldoc'/4).
  697string_predicate(pce_principal:send_implementation/3).
  698string_predicate(pce_principal:pce_lazy_get_method/3).
  699string_predicate(pce_principal:pce_lazy_send_method/3).
  700string_predicate(pce_principal:pce_class/6).
  701string_predicate(prolog_xref:pred_comment/4).
  702string_predicate(prolog_xref:module_comment/3).
  703string_predicate(pldoc_process:structured_comment//2).
  704string_predicate(pldoc_process:structured_command_start/3).
  705string_predicate(pldoc_process:separator_line//0).
  706string_predicate(pldoc_register:mydoc/3).
  707string_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.
  715% system predicates
  716valid_string_goal(system:format(S)) :- string(S).
  717valid_string_goal(system:format(S,_)) :- string(S).
  718valid_string_goal(system:format(_,S,_)) :- string(S).
  719valid_string_goal(system:string_codes(S,_)) :- string(S).
  720valid_string_goal(system:string_code(_,S,_)) :- string(S).
  721valid_string_goal(system:throw(msg(S,_))) :- string(S).
  722valid_string_goal('$dcg':phrase(S,_,_)) :- string(S).
  723valid_string_goal('$dcg':phrase(S,_)) :- string(S).
  724valid_string_goal(system: is(_,_)).     % arithmetic allows for "x"
  725valid_string_goal(system: =:=(_,_)).
  726valid_string_goal(system: >(_,_)).
  727valid_string_goal(system: <(_,_)).
  728valid_string_goal(system: >=(_,_)).
  729valid_string_goal(system: =<(_,_)).
  730% library stuff
  731valid_string_goal(dcg_basics:string_without(S,_,_,_)) :- string(S).
  732valid_string_goal(git:read_url(S,_,_)) :- string(S).
  733valid_string_goal(tipc:tipc_subscribe(_,_,_,_,S)) :- string(S).
  734valid_string_goal(charsio:format_to_chars(Format,_,_)) :- string(Format).
  735valid_string_goal(charsio:format_to_chars(Format,_,_,_)) :- string(Format).
  736valid_string_goal(codesio:format_to_codes(Format,_,_)) :- string(Format).
  737valid_string_goal(codesio:format_to_codes(Format,_,_,_)) :- string(Format).
  738
  739
  740                 /*******************************
  741                 *        EXTENSION HOOKS       *
  742                 *******************************/
 checker(:Goal, +Message:text)
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,_)).
  764checker(list_undefined,         'undefined predicates').
  765checker(list_trivial_fails,     'trivial failures').
  766checker(list_format_errors,     'format/2,3 and debug/3 templates').
  767checker(list_redefined,         'redefined system and global predicates').
  768checker(list_void_declarations, 'predicates with declarations but without clauses').
  769checker(list_autoload,          'predicates that need autoloading').
  770
  771
  772                 /*******************************
  773                 *            MESSAGES          *
  774                 *******************************/
  775
  776:- multifile
  777    prolog:message/3.  778
  779prolog:message(check(pass(Comment))) -->
  780    [ 'Checking ~w ...'-[Comment] ].
  781prolog:message(check(find_references(Preds))) -->
  782    { length(Preds, N)
  783    },
  784    [ 'Scanning for references to ~D possibly undefined predicates'-[N] ].
  785prolog:message(check(undefined_procedures, Grouped)) -->
  786    [ 'The predicates below are not defined. If these are defined', nl,
  787      'at runtime using assert/1, use :- dynamic Name/Arity.', nl, nl
  788    ],
  789    undefined_procedures(Grouped).
  790prolog:message(check(undefined_unreferenced_predicates)) -->
  791    [ 'The predicates below are not defined, and are not', nl,
  792      'referenced.', nl, nl
  793    ].
  794prolog:message(check(undefined_unreferenced(Pred))) -->
  795    predicate(Pred).
  796prolog:message(check(autoload(Module, Pairs))) -->
  797    { module_property(Module, file(Path))
  798    },
  799    !,
  800    [ 'Into module ~w ('-[Module] ],
  801    short_filename(Path),
  802    [ ')', nl ],
  803    autoload(Pairs).
  804prolog:message(check(autoload(Module, Pairs))) -->
  805    [ 'Into module ~w'-[Module], nl ],
  806    autoload(Pairs).
  807prolog:message(check(redefined(In, From, Pred))) -->
  808    predicate(In:Pred),
  809    redefined(In, From).
  810prolog:message(check(trivial_failures)) -->
  811    [ 'The following goals fail because there are no matching clauses.' ].
  812prolog:message(check(trivial_failure(Goal, Refs))) -->
  813    { map_list_to_pairs(sort_reference_key, Refs, Keyed),
  814      keysort(Keyed, KeySorted),
  815      pairs_values(KeySorted, SortedRefs)
  816    },
  817    goal(Goal),
  818    [ ', which is called from'-[], nl ],
  819    referenced_by(SortedRefs).
  820prolog:message(check(string_in_clause(String, Context))) -->
  821    '$messages':swi_location(Context),
  822    [ 'String ~q'-[String] ].
  823prolog:message(check(rational_in_clause(String, Context))) -->
  824    '$messages':swi_location(Context),
  825    [ 'Rational ~q'-[String] ].
  826prolog:message(check(Msg, Goal, Context)) -->
  827    '$messages':swi_location(Context),
  828    { pi_head(PI, Goal) },
  829    [ nl, '    '-[] ],
  830    predicate(PI),
  831    [ ': '-[] ],
  832    check_message(Msg).
  833prolog:message(check(void_declaration(P, Decl))) -->
  834    predicate(P),
  835    [ ' is declared as ~p, but has no clauses'-[Decl] ].
  836
  837undefined_procedures([]) -->
  838    [].
  839undefined_procedures([H|T]) -->
  840    undefined_procedure(H),
  841    undefined_procedures(T).
  842
  843undefined_procedure(Pred-Refs) -->
  844    { map_list_to_pairs(sort_reference_key, Refs, Keyed),
  845      keysort(Keyed, KeySorted),
  846      pairs_values(KeySorted, SortedRefs)
  847    },
  848    predicate(Pred),
  849    [ ', which is referenced by', nl ],
  850    referenced_by(SortedRefs).
  851
  852redefined(user, system) -->
  853    [ '~t~30| System predicate redefined globally' ].
  854redefined(_, system) -->
  855    [ '~t~30| Redefined system predicate' ].
  856redefined(_, user) -->
  857    [ '~t~30| Redefined global predicate' ].
  858
  859goal(user:Goal) -->
  860    !,
  861    [ '~p'-[Goal] ].
  862goal(Goal) -->
  863    !,
  864    [ '~p'-[Goal] ].
  865
  866predicate(Module:Name/Arity) -->
  867    { atom(Module),
  868      atom(Name),
  869      integer(Arity),
  870      functor(Head, Name, Arity),
  871      predicate_name(Module:Head, PName)
  872    },
  873    !,
  874    [ '~w'-[PName] ].
  875predicate(Module:Head) -->
  876    { atom(Module),
  877      callable(Head),
  878      predicate_name(Module:Head, PName)
  879    },
  880    !,
  881    [ '~w'-[PName] ].
  882predicate(Name/Arity) -->
  883    { atom(Name),
  884      integer(Arity)
  885    },
  886    !,
  887    predicate(user:Name/Arity).
  888
  889autoload([]) -->
  890    [].
  891autoload([Lib-Pred|T]) -->
  892    [ '    ' ],
  893    predicate(Pred),
  894    [ '~t~24| from ' ],
  895    short_filename(Lib),
  896    [ nl ],
  897    autoload(T).
 sort_reference_key(+Reference, -Key) is det
Create a stable key for sorting references to predicates.
  903sort_reference_key(Term, key(M:Name/Arity, N, ClausePos)) :-
  904    clause_ref(Term, ClauseRef, ClausePos),
  905    !,
  906    nth_clause(Pred, N, ClauseRef),
  907    strip_module(Pred, M, Head),
  908    functor(Head, Name, Arity).
  909sort_reference_key(Term, Term).
  910
  911clause_ref(clause_term_position(ClauseRef, TermPos), ClauseRef, ClausePos) :-
  912    arg(1, TermPos, ClausePos).
  913clause_ref(clause(ClauseRef), ClauseRef, 0).
  914
  915
  916referenced_by([]) -->
  917    [].
  918referenced_by([Ref|T]) -->
  919    ['\t'], prolog:message_location(Ref),
  920            predicate_indicator(Ref),
  921    [ nl ],
  922    referenced_by(T).
  923
  924predicate_indicator(clause_term_position(ClauseRef, _)) -->
  925    { nonvar(ClauseRef) },
  926    !,
  927    predicate_indicator(clause(ClauseRef)).
  928predicate_indicator(clause(ClauseRef)) -->
  929    { clause_name(ClauseRef, Name) },
  930    [ '~w'-[Name] ].
  931predicate_indicator(file_term_position(_,_)) -->
  932    [ '(initialization)' ].
  933predicate_indicator(file(_,_,_,_)) -->
  934    [ '(initialization)' ].
  935
  936
  937short_filename(Path) -->
  938    { short_filename(Path, Spec)
  939    },
  940    [ '~q'-[Spec] ].
  941
  942short_filename(Path, Spec) :-
  943    absolute_file_name('', Here),
  944    atom_concat(Here, Local0, Path),
  945    !,
  946    remove_leading_slash(Local0, Spec).
  947short_filename(Path, Spec) :-
  948    findall(LenAlias, aliased_path(Path, LenAlias), Keyed),
  949    keysort(Keyed, [_-Spec|_]).
  950short_filename(Path, Path).
  951
  952aliased_path(Path, Len-Spec) :-
  953    setof(Alias, Spec^(user:file_search_path(Alias, Spec)), Aliases),
  954    member(Alias, Aliases),
  955    Term =.. [Alias, '.'],
  956    absolute_file_name(Term,
  957                       [ file_type(directory),
  958                         file_errors(fail),
  959                         solutions(all)
  960                       ], Prefix),
  961    atom_concat(Prefix, Local0, Path),
  962    remove_leading_slash(Local0, Local),
  963    atom_length(Local, Len),
  964    Spec =.. [Alias, Local].
  965
  966remove_leading_slash(Path, Local) :-
  967    atom_concat(/, Local, Path),
  968    !.
  969remove_leading_slash(Path, Path).
  970
  971check_message(format_argc(Expected, InList)) -->
  972    [ 'Template requires ~w arguments, got ~w'-[Expected, InList] ].
  973check_message(format_template(Formal)) -->
  974    { message_to_string(error(Formal, _), Msg) },
  975    [ 'Invalid template: ~s'-[Msg] ]