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'(_,_)).
  293redefined_ok('$exported_op'(_,_,_)).
  294redefined_ok('$autoload'(_,_,_)).
  295
  296global_module(user).
  297global_module(system).
 list_void_declarations is det
List predicates that have declared attributes, but no clauses.
  303list_void_declarations :-
  304    P = _:_,
  305    (   predicate_property(P, undefined),
  306        (   '$get_predicate_attribute'(P, meta_predicate, Pattern),
  307            print_message(warning,
  308                          check(void_declaration(P, meta_predicate(Pattern))))
  309        ;   void_attribute(Attr),
  310            '$get_predicate_attribute'(P, Attr, 1),
  311            print_message(warning,
  312                          check(void_declaration(P, Attr)))
  313        ),
  314        fail
  315    ;   true
  316    ).
  317
  318void_attribute(public).
  319void_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].
  332:- thread_local
  333    trivial_fail/2.  334
  335list_trivial_fails :-
  336    list_trivial_fails([]).
  337
  338list_trivial_fails(Options) :-
  339    merge_options(Options,
  340                  [ module_class([user]),
  341                    infer_meta_predicates(false),
  342                    autoload(false),
  343                    evaluate(false),
  344                    trace_reference(_),
  345                    on_trace(check_trivial_fail)
  346                  ],
  347                  WalkOptions),
  348
  349    prolog_walk_code([ source(false)
  350                     | WalkOptions
  351                     ]),
  352    findall(CRef, retract(trivial_fail(clause(CRef), _)), Clauses),
  353    (   Clauses == []
  354    ->  true
  355    ;   print_message(warning, check(trivial_failures)),
  356        prolog_walk_code([ clauses(Clauses)
  357                         | WalkOptions
  358                         ]),
  359        findall(Goal-From, retract(trivial_fail(From, Goal)), Pairs),
  360        keysort(Pairs, Sorted),
  361        group_pairs_by_key(Sorted, Grouped),
  362        maplist(report_trivial_fail, Grouped)
  363    ).
 trivial_fail_goal(:Goal)
Multifile hook that tells list_trivial_fails/0 to accept Goal as valid.
  370trivial_fail_goal(pce_expansion:pce_class(_, _, template, _, _, _)).
  371trivial_fail_goal(pce_host:property(system_source_prefix(_))).
  372
  373:- public
  374    check_trivial_fail/3.  375
  376check_trivial_fail(MGoal0, _Caller, From) :-
  377    (   MGoal0 = M:Goal,
  378        atom(M),
  379        callable(Goal),
  380        predicate_property(MGoal0, interpreted),
  381        \+ predicate_property(MGoal0, dynamic),
  382        \+ predicate_property(MGoal0, multifile),
  383        \+ trivial_fail_goal(MGoal0)
  384    ->  (   predicate_property(MGoal0, meta_predicate(Meta))
  385        ->  qualify_meta_goal(MGoal0, Meta, MGoal)
  386        ;   MGoal = MGoal0
  387        ),
  388        (   clause(MGoal, _)
  389        ->  true
  390        ;   assertz(trivial_fail(From, MGoal))
  391        )
  392    ;   true
  393    ).
  394
  395report_trivial_fail(Goal-FromList) :-
  396    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
  402qualify_meta_goal(M:Goal0, Meta, M:Goal) :-
  403    functor(Goal0, F, N),
  404    functor(Goal, F, N),
  405    qualify_meta_goal(1, M, Meta, Goal0, Goal).
  406
  407qualify_meta_goal(N, M, Meta, Goal0, Goal) :-
  408    arg(N, Meta,  ArgM),
  409    !,
  410    arg(N, Goal0, Arg0),
  411    arg(N, Goal,  Arg),
  412    N1 is N + 1,
  413    (   module_qualified(ArgM)
  414    ->  add_module(Arg0, M, Arg)
  415    ;   Arg = Arg0
  416    ),
  417    meta_goal(N1, Meta, Goal0, Goal).
  418meta_goal(_, _, _, _).
  419
  420add_module(Arg, M, M:Arg) :-
  421    var(Arg),
  422    !.
  423add_module(M:Arg, _, MArg) :-
  424    !,
  425    add_module(Arg, M, MArg).
  426add_module(Arg, M, M:Arg).
  427
  428module_qualified(N) :- integer(N), !.
  429module_qualified(:).
  430module_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.
  448list_strings :-
  449    list_strings([module_class([user])]).
  450
  451list_strings(Options) :-
  452    (   prolog_program_clause(ClauseRef, Options),
  453        clause(Head, Body, ClauseRef),
  454        \+ ( predicate_indicator(Head, PI),
  455             string_predicate(PI)
  456           ),
  457        make_clause(Head, Body, Clause),
  458        findall(T,
  459                (   sub_term(T, Head),
  460                    string(T)
  461                ;   Head = M:_,
  462                    goal_in_body(Goal, M, Body),
  463                    (   valid_string_goal(Goal)
  464                    ->  fail
  465                    ;   sub_term(T, Goal),
  466                        string(T)
  467                    )
  468                ), Ts0),
  469        sort(Ts0, Ts),
  470        member(T, Ts),
  471        message_context(ClauseRef, T, Clause, Context),
  472        print_message(warning,
  473                      check(string_in_clause(T, Context))),
  474        fail
  475    ;   true
  476    ).
  477
  478make_clause(Head, true, Head) :- !.
  479make_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.
  498list_rationals :-
  499    list_rationals([module_class([user])]).
  500
  501list_rationals(Options) :-
  502    (   option(arithmetic(DoArith), Options, false),
  503        prolog_program_clause(ClauseRef, Options),
  504        clause(Head, Body, ClauseRef),
  505        make_clause(Head, Body, Clause),
  506        findall(T,
  507                (   sub_term(T, Head),
  508                    rational(T),
  509                    \+ integer(T)
  510                ;   Head = M:_,
  511                    goal_in_body(Goal, M, Body),
  512                    nonvar(Goal),
  513                    (   DoArith == false,
  514                        valid_rational_goal(Goal)
  515                    ->  fail
  516                    ;   sub_term(T, Goal),
  517                        rational(T),
  518                        \+ integer(T)
  519                    )
  520                ), Ts0),
  521        sort(Ts0, Ts),
  522        member(T, Ts),
  523        message_context(ClauseRef, T, Clause, Context),
  524        print_message(warning,
  525                      check(rational_in_clause(T, Context))),
  526        fail
  527    ;   true
  528    ).
  529
  530
  531valid_rational_goal(_ is _).
  532valid_rational_goal(_ =:= _).
  533valid_rational_goal(_ < _).
  534valid_rational_goal(_ > _).
  535valid_rational_goal(_ =< _).
  536valid_rational_goal(_ >= _).
 list_format_errors is det
 list_format_errors(+Options) is det
List argument errors for format/2,3.
  544list_format_errors :-
  545    list_format_errors([module_class([user])]).
  546
  547list_format_errors(Options) :-
  548    (   prolog_program_clause(ClauseRef, Options),
  549        clause(Head, Body, ClauseRef),
  550        make_clause(Head, Body, Clause),
  551        Head = M:_,
  552        goal_in_body(Goal, M, Body),
  553        format_warning(Goal, Msg),
  554        message_context(ClauseRef, Goal, Clause, Context),
  555        print_message(warning, check(Msg, Goal, Context)),
  556        fail
  557    ;   true
  558    ).
  559
  560format_warning(system:format(Format, Args), Msg) :-
  561    ground(Format),
  562    (   is_list(Args)
  563    ->  length(Args, ArgC)
  564    ;   nonvar(Args)
  565    ->  ArgC = 1
  566    ),
  567    E = error(Formal,_),
  568    catch(format_types(Format, Types), E, true),
  569    (   var(Formal)
  570    ->  length(Types, TypeC),
  571        TypeC =\= ArgC,
  572        Msg = format_argc(TypeC, ArgC)
  573    ;   Msg = format_template(Formal)
  574    ).
  575format_warning(system:format(_Stream, Format, Args), Msg) :-
  576    format_warning(system:format(Format, Args), Msg).
  577format_warning(prolog_debug:debug(_Channel, Format, Args), Msg) :-
  578    format_warning(system:format(Format, Args), Msg).
 goal_in_body(-G, +M, +Body) is nondet
True when G is a goal called from Body.
  585goal_in_body(M:G, M, G) :-
  586    var(G),
  587    !.
  588goal_in_body(G, _, M:G0) :-
  589    atom(M),
  590    !,
  591    goal_in_body(G, M, G0).
  592goal_in_body(G, M, Control) :-
  593    nonvar(Control),
  594    control(Control, Subs),
  595    !,
  596    member(Sub, Subs),
  597    goal_in_body(G, M, Sub).
  598goal_in_body(G, M, G0) :-
  599    callable(G0),
  600    (   atom(M)
  601    ->  TM = M
  602    ;   TM = system
  603    ),
  604    predicate_property(TM:G0, meta_predicate(Spec)),
  605    !,
  606    (   strip_goals(G0, Spec, G1),
  607        simple_goal_in_body(G, M, G1)
  608    ;   arg(I, Spec, Meta),
  609        arg(I, G0, G1),
  610        extend(Meta, G1, G2),
  611        goal_in_body(G, M, G2)
  612    ).
  613goal_in_body(G, M, G0) :-
  614    simple_goal_in_body(G, M, G0).
  615
  616simple_goal_in_body(G, M, G0) :-
  617    (   atom(M),
  618        callable(G0),
  619        predicate_property(M:G0, imported_from(M2))
  620    ->  G = M2:G0
  621    ;   G = M:G0
  622    ).
  623
  624control((A,B), [A,B]).
  625control((A;B), [A,B]).
  626control((A->B), [A,B]).
  627control((A*->B), [A,B]).
  628control((\+A), [A]).
  629
  630strip_goals(G0, Spec, G) :-
  631    functor(G0, Name, Arity),
  632    functor(G,  Name, Arity),
  633    strip_goal_args(1, G0, Spec, G).
  634
  635strip_goal_args(I, G0, Spec, G) :-
  636    arg(I, G0, A0),
  637    !,
  638    arg(I, Spec, M),
  639    (   extend(M, A0, _)
  640    ->  arg(I, G, '<meta-goal>')
  641    ;   arg(I, G, A0)
  642    ),
  643    I2 is I + 1,
  644    strip_goal_args(I2, G0, Spec, G).
  645strip_goal_args(_, _, _, _).
  646
  647extend(I, G0, G) :-
  648    callable(G0),
  649    integer(I), I>0,
  650    !,
  651    length(L, I),
  652    extend_list(G0, L, G).
  653extend(0, G, G).
  654extend(^, G, G).
  655
  656extend_list(M:G0, L, M:G) :-
  657    !,
  658    callable(G0),
  659    extend_list(G0, L, G).
  660extend_list(G0, L, G) :-
  661    G0 =.. List,
  662    append(List, L, All),
  663    G =.. All.
 message_context(+ClauseRef, +Term, +Clause, -Pos) is det
Find an as accurate as possible location for Term in Clause.
  670message_context(ClauseRef, Term, Clause, file_term_position(File, TermPos)) :-
  671    clause_info(ClauseRef, File, Layout, _Vars),
  672    (   Term = _:Goal,
  673        prolog_codewalk:subterm_pos(Goal, Clause, ==, Layout, TermPos)
  674    ;   prolog_codewalk:subterm_pos(Term, Clause, ==, Layout, TermPos)
  675    ),
  676    !.
  677message_context(ClauseRef, _String, _Clause, file(File, Line, -1, _)) :-
  678    clause_property(ClauseRef, file(File)),
  679    clause_property(ClauseRef, line_count(Line)),
  680    !.
  681message_context(ClauseRef, _String, _Clause, clause(ClauseRef)).
  682
  683
  684:- meta_predicate
  685    predicate_indicator(:, -).  686
  687predicate_indicator(Module:Head, Module:Name/Arity) :-
  688    functor(Head, Name, Arity).
  689predicate_indicator(Module:Head, Module:Name//DCGArity) :-
  690    functor(Head, Name, Arity),
  691    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.
  698string_predicate(_:'$pldoc'/4).
  699string_predicate(pce_principal:send_implementation/3).
  700string_predicate(pce_principal:pce_lazy_get_method/3).
  701string_predicate(pce_principal:pce_lazy_send_method/3).
  702string_predicate(pce_principal:pce_class/6).
  703string_predicate(prolog_xref:pred_comment/4).
  704string_predicate(prolog_xref:module_comment/3).
  705string_predicate(pldoc_process:structured_comment//2).
  706string_predicate(pldoc_process:structured_command_start/3).
  707string_predicate(pldoc_process:separator_line//0).
  708string_predicate(pldoc_register:mydoc/3).
  709string_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.
  717% system predicates
  718valid_string_goal(system:format(S)) :- string(S).
  719valid_string_goal(system:format(S,_)) :- string(S).
  720valid_string_goal(system:format(_,S,_)) :- string(S).
  721valid_string_goal(system:string_codes(S,_)) :- string(S).
  722valid_string_goal(system:string_code(_,S,_)) :- string(S).
  723valid_string_goal(system:throw(msg(S,_))) :- string(S).
  724valid_string_goal('$dcg':phrase(S,_,_)) :- string(S).
  725valid_string_goal('$dcg':phrase(S,_)) :- string(S).
  726valid_string_goal(system: is(_,_)).     % arithmetic allows for "x"
  727valid_string_goal(system: =:=(_,_)).
  728valid_string_goal(system: >(_,_)).
  729valid_string_goal(system: <(_,_)).
  730valid_string_goal(system: >=(_,_)).
  731valid_string_goal(system: =<(_,_)).
  732% library stuff
  733valid_string_goal(dcg_basics:string_without(S,_,_,_)) :- string(S).
  734valid_string_goal(git:read_url(S,_,_)) :- string(S).
  735valid_string_goal(tipc:tipc_subscribe(_,_,_,_,S)) :- string(S).
  736valid_string_goal(charsio:format_to_chars(Format,_,_)) :- string(Format).
  737valid_string_goal(charsio:format_to_chars(Format,_,_,_)) :- string(Format).
  738valid_string_goal(codesio:format_to_codes(Format,_,_)) :- string(Format).
  739valid_string_goal(codesio:format_to_codes(Format,_,_,_)) :- string(Format).
  740
  741
  742                 /*******************************
  743                 *        EXTENSION HOOKS       *
  744                 *******************************/
 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,_)).
  766checker(list_undefined,         'undefined predicates').
  767checker(list_trivial_fails,     'trivial failures').
  768checker(list_format_errors,     'format/2,3 and debug/3 templates').
  769checker(list_redefined,         'redefined system and global predicates').
  770checker(list_void_declarations, 'predicates with declarations but without clauses').
  771checker(list_autoload,          'predicates that need autoloading').
  772
  773
  774                 /*******************************
  775                 *            MESSAGES          *
  776                 *******************************/
  777
  778:- multifile
  779    prolog:message/3.  780
  781prolog:message(check(pass(Comment))) -->
  782    [ 'Checking ~w ...'-[Comment] ].
  783prolog:message(check(find_references(Preds))) -->
  784    { length(Preds, N)
  785    },
  786    [ 'Scanning for references to ~D possibly undefined predicates'-[N] ].
  787prolog:message(check(undefined_procedures, Grouped)) -->
  788    [ 'The predicates below are not defined. If these are defined', nl,
  789      'at runtime using assert/1, use :- dynamic Name/Arity.', nl, nl
  790    ],
  791    undefined_procedures(Grouped).
  792prolog:message(check(undefined_unreferenced_predicates)) -->
  793    [ 'The predicates below are not defined, and are not', nl,
  794      'referenced.', nl, nl
  795    ].
  796prolog:message(check(undefined_unreferenced(Pred))) -->
  797    predicate(Pred).
  798prolog:message(check(autoload(Module, Pairs))) -->
  799    { module_property(Module, file(Path))
  800    },
  801    !,
  802    [ 'Into module ~w ('-[Module] ],
  803    short_filename(Path),
  804    [ ')', nl ],
  805    autoload(Pairs).
  806prolog:message(check(autoload(Module, Pairs))) -->
  807    [ 'Into module ~w'-[Module], nl ],
  808    autoload(Pairs).
  809prolog:message(check(redefined(In, From, Pred))) -->
  810    predicate(In:Pred),
  811    redefined(In, From).
  812prolog:message(check(trivial_failures)) -->
  813    [ 'The following goals fail because there are no matching clauses.' ].
  814prolog:message(check(trivial_failure(Goal, Refs))) -->
  815    { map_list_to_pairs(sort_reference_key, Refs, Keyed),
  816      keysort(Keyed, KeySorted),
  817      pairs_values(KeySorted, SortedRefs)
  818    },
  819    goal(Goal),
  820    [ ', which is called from'-[], nl ],
  821    referenced_by(SortedRefs).
  822prolog:message(check(string_in_clause(String, Context))) -->
  823    '$messages':swi_location(Context),
  824    [ 'String ~q'-[String] ].
  825prolog:message(check(rational_in_clause(String, Context))) -->
  826    '$messages':swi_location(Context),
  827    [ 'Rational ~q'-[String] ].
  828prolog:message(check(Msg, Goal, Context)) -->
  829    '$messages':swi_location(Context),
  830    { pi_head(PI, Goal) },
  831    [ nl, '    '-[] ],
  832    predicate(PI),
  833    [ ': '-[] ],
  834    check_message(Msg).
  835prolog:message(check(void_declaration(P, Decl))) -->
  836    predicate(P),
  837    [ ' is declared as ~p, but has no clauses'-[Decl] ].
  838
  839undefined_procedures([]) -->
  840    [].
  841undefined_procedures([H|T]) -->
  842    undefined_procedure(H),
  843    undefined_procedures(T).
  844
  845undefined_procedure(Pred-Refs) -->
  846    { map_list_to_pairs(sort_reference_key, Refs, Keyed),
  847      keysort(Keyed, KeySorted),
  848      pairs_values(KeySorted, SortedRefs)
  849    },
  850    predicate(Pred),
  851    [ ', which is referenced by', nl ],
  852    referenced_by(SortedRefs).
  853
  854redefined(user, system) -->
  855    [ '~t~30| System predicate redefined globally' ].
  856redefined(_, system) -->
  857    [ '~t~30| Redefined system predicate' ].
  858redefined(_, user) -->
  859    [ '~t~30| Redefined global predicate' ].
  860
  861goal(user:Goal) -->
  862    !,
  863    [ '~p'-[Goal] ].
  864goal(Goal) -->
  865    !,
  866    [ '~p'-[Goal] ].
  867
  868predicate(Module:Name/Arity) -->
  869    { atom(Module),
  870      atom(Name),
  871      integer(Arity),
  872      functor(Head, Name, Arity),
  873      predicate_name(Module:Head, PName)
  874    },
  875    !,
  876    [ '~w'-[PName] ].
  877predicate(Module:Head) -->
  878    { atom(Module),
  879      callable(Head),
  880      predicate_name(Module:Head, PName)
  881    },
  882    !,
  883    [ '~w'-[PName] ].
  884predicate(Name/Arity) -->
  885    { atom(Name),
  886      integer(Arity)
  887    },
  888    !,
  889    predicate(user:Name/Arity).
  890
  891autoload([]) -->
  892    [].
  893autoload([Lib-Pred|T]) -->
  894    [ '    ' ],
  895    predicate(Pred),
  896    [ '~t~24| from ' ],
  897    short_filename(Lib),
  898    [ nl ],
  899    autoload(T).
 sort_reference_key(+Reference, -Key) is det
Create a stable key for sorting references to predicates.
  905sort_reference_key(Term, key(M:Name/Arity, N, ClausePos)) :-
  906    clause_ref(Term, ClauseRef, ClausePos),
  907    !,
  908    nth_clause(Pred, N, ClauseRef),
  909    strip_module(Pred, M, Head),
  910    functor(Head, Name, Arity).
  911sort_reference_key(Term, Term).
  912
  913clause_ref(clause_term_position(ClauseRef, TermPos), ClauseRef, ClausePos) :-
  914    arg(1, TermPos, ClausePos).
  915clause_ref(clause(ClauseRef), ClauseRef, 0).
  916
  917
  918referenced_by([]) -->
  919    [].
  920referenced_by([Ref|T]) -->
  921    ['\t'], prolog:message_location(Ref),
  922            predicate_indicator(Ref),
  923    [ nl ],
  924    referenced_by(T).
  925
  926predicate_indicator(clause_term_position(ClauseRef, _)) -->
  927    { nonvar(ClauseRef) },
  928    !,
  929    predicate_indicator(clause(ClauseRef)).
  930predicate_indicator(clause(ClauseRef)) -->
  931    { clause_name(ClauseRef, Name) },
  932    [ '~w'-[Name] ].
  933predicate_indicator(file_term_position(_,_)) -->
  934    [ '(initialization)' ].
  935predicate_indicator(file(_,_,_,_)) -->
  936    [ '(initialization)' ].
  937
  938
  939short_filename(Path) -->
  940    { short_filename(Path, Spec)
  941    },
  942    [ '~q'-[Spec] ].
  943
  944short_filename(Path, Spec) :-
  945    absolute_file_name('', Here),
  946    atom_concat(Here, Local0, Path),
  947    !,
  948    remove_leading_slash(Local0, Spec).
  949short_filename(Path, Spec) :-
  950    findall(LenAlias, aliased_path(Path, LenAlias), Keyed),
  951    keysort(Keyed, [_-Spec|_]).
  952short_filename(Path, Path).
  953
  954aliased_path(Path, Len-Spec) :-
  955    setof(Alias, Spec^(user:file_search_path(Alias, Spec)), Aliases),
  956    member(Alias, Aliases),
  957    Term =.. [Alias, '.'],
  958    absolute_file_name(Term,
  959                       [ file_type(directory),
  960                         file_errors(fail),
  961                         solutions(all)
  962                       ], Prefix),
  963    atom_concat(Prefix, Local0, Path),
  964    remove_leading_slash(Local0, Local),
  965    atom_length(Local, Len),
  966    Spec =.. [Alias, Local].
  967
  968remove_leading_slash(Path, Local) :-
  969    atom_concat(/, Local, Path),
  970    !.
  971remove_leading_slash(Path, Path).
  972
  973check_message(format_argc(Expected, InList)) -->
  974    [ 'Template requires ~w arguments, got ~w'-[Expected, InList] ].
  975check_message(format_template(Formal)) -->
  976    { message_to_string(error(Formal, _), Msg) },
  977    [ 'Invalid template: ~s'-[Msg] ]