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-2023, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(check, 39 [ check/0, % run all checks 40 list_undefined/0, % list undefined predicates 41 list_undefined/1, % +Options 42 list_autoload/0, % list predicates that need autoloading 43 list_redefined/0, % list redefinitions 44 list_cross_module_calls/0, % List Module:Goal usage 45 list_cross_module_calls/1, % +Options 46 list_void_declarations/0, % list declarations with no clauses 47 list_trivial_fails/0, % list goals that trivially fail 48 list_trivial_fails/1, % +Options 49 list_format_errors/0, % list calls to format with wrong args 50 list_format_errors/1, % +Options 51 list_strings/0, % list string objects in clauses 52 list_strings/1, % +Options 53 list_rationals/0, % list rational objects in clauses 54 list_rationals/1 % +Options 55 ]). 56:- autoload(library(apply),[maplist/2]). 57:- autoload(library(lists),[member/2,append/3]). 58:- autoload(library(occurs),[sub_term/2]). 59:- autoload(library(option),[merge_options/3,option/3]). 60:- autoload(library(pairs), 61 [group_pairs_by_key/2,map_list_to_pairs/3,pairs_values/2]). 62:- autoload(library(prolog_clause), 63 [clause_info/4,predicate_name/2,clause_name/2]). 64:- autoload(library(prolog_code),[pi_head/2]). 65:- autoload(library(prolog_codewalk), 66 [prolog_walk_code/1,prolog_program_clause/2]). 67:- autoload(library(prolog_format),[format_types/2]). 68:- autoload(library(predicate_options), [check_predicate_options/0]). 69 70:- set_prolog_flag(generate_debug_info, false). 71 72:- multifile 73 trivial_fail_goal/1, 74 string_predicate/1, 75 valid_string_goal/1, 76 checker/2. 77 78:- dynamic checker/2.
93:- predicate_options(list_undefined/1, 1,
94 [ module_class(list(oneof([user,library,system])))
95 ]).
The checker can be expanded or restricted by modifying the dynamic multifile hook checker/2.
The checker may be used in batch, e.g., for CI workflows by calling
SWI-Prolog as below. Note that by using -l
to load the program,
the program is not started if it used initialization/2 of type
main
to start the program.
swipl -q --on-warning=status --on-error=status \ -g check -t halt -l myprogram.pl
132check :- 133 checker(Checker, Message), 134 print_message(informational,check(pass(Message))), 135 catch(Checker,E,print_message(error,E)), 136 fail. 137check.
[user]
. For example, to include the
libraries into the examination, use [user,library]
.154:- thread_local 155 undef/2. 156 157list_undefined :- 158 list_undefined([]). 159 160list_undefined(Options) :- 161 merge_options(Options, 162 [ module_class([user]) 163 ], 164 WalkOptions), 165 call_cleanup( 166 prolog_walk_code([ undefined(trace), 167 on_trace(found_undef) 168 | WalkOptions 169 ]), 170 collect_undef(Grouped)), 171 ( Grouped == [] 172 -> true 173 ; print_message(warning, check(undefined_procedures, Grouped)) 174 ). 175 176% The following predicates are used from library(prolog_autoload). 177 178:- public 179 found_undef/3, 180 collect_undef/1. 181 182collect_undef(Grouped) :- 183 findall(PI-From, retract(undef(PI, From)), Pairs), 184 keysort(Pairs, Sorted), 185 group_pairs_by_key(Sorted, Grouped). 186 187found_undef(To, _Caller, From) :- 188 goal_pi(To, PI), 189 ( undef(PI, From) 190 -> true 191 ; compiled(PI) 192 -> true 193 ; not_always_present(PI) 194 -> true 195 ; assertz(undef(PI,From)) 196 ). 197 198compiled(system:'$call_cleanup'/0). % compiled to VM instructions 199compiled(system:'$catch'/0). 200compiled(system:'$cut'/0). 201compiled(system:'$reset'/0). 202compiled(system:'$call_continuation'/1). 203compiled(system:'$shift'/1). 204compiled(system:'$shift_for_copy'/1). 205compiled('$engines':'$yield'/0).
212not_always_present(_:win_folder/2) :- 213 \+ current_prolog_flag(windows, true). 214not_always_present(_:win_add_dll_directory/2) :- 215 \+ current_prolog_flag(windows, true). 216not_always_present(_:opt_help/2). 217not_always_present(_:opt_type/3). 218not_always_present(_:opt_meta/2). 219 220goal_pi(M:Head, M:Name/Arity) :- 221 functor(Head, Name, Arity).
234list_autoload :- 235 setup_call_cleanup( 236 ( current_prolog_flag(access_level, OldLevel), 237 current_prolog_flag(autoload, OldAutoLoad), 238 set_prolog_flag(access_level, system), 239 set_prolog_flag(autoload, false) 240 ), 241 list_autoload_(OldLevel), 242 ( set_prolog_flag(access_level, OldLevel), 243 set_prolog_flag(autoload, OldAutoLoad) 244 )). 245 246list_autoload_(SystemMode) :- 247 ( setof(Lib-Pred, 248 autoload_predicate(Module, Lib, Pred, SystemMode), 249 Pairs), 250 print_message(informational, 251 check(autoload(Module, Pairs))), 252 fail 253 ; true 254 ). 255 256autoload_predicate(Module, Library, Name/Arity, SystemMode) :- 257 predicate_property(Module:Head, undefined), 258 check_module_enabled(Module, SystemMode), 259 ( \+ predicate_property(Module:Head, imported_from(_)), 260 functor(Head, Name, Arity), 261 '$find_library'(Module, Name, Arity, _LoadModule, Library), 262 referenced(Module:Head, Module, _) 263 -> true 264 ). 265 266check_module_enabled(_, system) :- !. 267check_module_enabled(Module, _) :- 268 \+ import_module(Module, system).
274referenced(Term, Module, Ref) :-
275 Goal = Module:_Head,
276 current_predicate(_, Goal),
277 '$get_predicate_attribute'(Goal, system, 0),
278 \+ '$get_predicate_attribute'(Goal, imported, _),
279 nth_clause(Goal, _, Ref),
280 '$xr_member'(Ref, Term).
user
as
well as in a normal module; that is, predicates for which the
local definition overrules the global default definition.288list_redefined :- 289 setup_call_cleanup( 290 ( current_prolog_flag(access_level, OldLevel), 291 set_prolog_flag(access_level, system) 292 ), 293 list_redefined_, 294 set_prolog_flag(access_level, OldLevel)). 295 296list_redefined_ :- 297 current_module(Module), 298 Module \== system, 299 current_predicate(_, Module:Head), 300 \+ predicate_property(Module:Head, imported_from(_)), 301 ( global_module(Super), 302 Super \== Module, 303 '$c_current_predicate'(_, Super:Head), 304 \+ redefined_ok(Head), 305 '$syspreds':'$defined_predicate'(Super:Head), 306 \+ predicate_property(Super:Head, (dynamic)), 307 \+ predicate_property(Super:Head, imported_from(Module)), 308 functor(Head, Name, Arity) 309 -> print_message(informational, 310 check(redefined(Module, Super, Name/Arity))) 311 ), 312 fail. 313list_redefined_. 314 315redefined_ok('$mode'(_,_)). 316redefined_ok('$pldoc'(_,_,_,_)). 317redefined_ok('$pred_option'(_,_,_,_)). 318redefined_ok('$table_mode'(_,_,_)). 319redefined_ok('$tabled'(_,_)). 320redefined_ok('$exported_op'(_,_,_)). 321redefined_ok('$autoload'(_,_,_)). 322 323global_module(user). 324global_module(system).
332list_cross_module_calls :- 333 list_cross_module_calls([]). 334 335list_cross_module_calls(Options) :- 336 call_cleanup( 337 list_cross_module_calls_guarded(Options), 338 retractall(cross_module_call(_,_,_))). 339 340list_cross_module_calls_guarded(Options) :- 341 merge_options(Options, 342 [ module_class([user]) 343 ], 344 WalkOptions), 345 prolog_walk_code([ trace_reference(_), 346 trace_condition(cross_module_call), 347 on_trace(write_call) 348 | WalkOptions 349 ]). 350 351:- thread_local 352 cross_module_call/3. 353 354:- public 355 cross_module_call/2, 356 write_call/3. 357 358cross_module_call(Callee, Context) :- 359 \+ same_module_call(Callee, Context). 360 361same_module_call(Callee, Context) :- 362 caller_module(Context, MCaller), 363 Callee = (MCallee:_), 364 ( ( MCaller = MCallee 365 ; predicate_property(Callee, exported) 366 ; predicate_property(Callee, built_in) 367 ; predicate_property(Callee, public) 368 ; clause_property(Context.get(clause), module(MCallee)) 369 ; predicate_property(Callee, multifile) 370 ) 371 -> true 372 ). 373 374caller_module(Context, MCaller) :- 375 Caller = Context.caller, 376 ( Caller = (MCaller:_) 377 -> true 378 ; Caller == '<initialization>', 379 MCaller = Context.module 380 ). 381 382write_call(Callee, Caller, Position) :- 383 cross_module_call(Callee, Caller, Position), 384 !. 385write_call(Callee, Caller, Position) :- 386 ( cross_module_call(_,_,_) 387 -> true 388 ; print_message(warning, check(cross_module_calls)) 389 ), 390 asserta(cross_module_call(Callee, Caller, Position)), 391 print_message(warning, 392 check(cross_module_call(Callee, Caller, Position))).
398list_void_declarations :- 399 P = _:_, 400 ( predicate_property(P, undefined), 401 ( '$get_predicate_attribute'(P, meta_predicate, Pattern), 402 print_message(warning, 403 check(void_declaration(P, meta_predicate(Pattern)))) 404 ; void_attribute(Attr), 405 '$get_predicate_attribute'(P, Attr, 1), 406 print_message(warning, 407 check(void_declaration(P, Attr))) 408 ), 409 fail 410 ; predicate_property(P, discontiguous), 411 \+ (predicate_property(P, number_of_clauses(N)), N > 0), 412 print_message(warning, 413 check(void_declaration(P, discontiguous))), 414 fail 415 ; true 416 ). 417 418void_attribute(public). 419void_attribute(volatile). 420void_attribute(det).
[user]
. For example, to include the
libraries into the examination, use [user,library]
.433:- thread_local 434 trivial_fail/2. 435 436list_trivial_fails :- 437 list_trivial_fails([]). 438 439list_trivial_fails(Options) :- 440 merge_options(Options, 441 [ module_class([user]), 442 infer_meta_predicates(false), 443 autoload(false), 444 evaluate(false), 445 trace_reference(_), 446 on_trace(check_trivial_fail) 447 ], 448 WalkOptions), 449 450 prolog_walk_code([ source(false) 451 | WalkOptions 452 ]), 453 findall(CRef, retract(trivial_fail(clause(CRef), _)), Clauses), 454 ( Clauses == [] 455 -> true 456 ; print_message(warning, check(trivial_failures)), 457 prolog_walk_code([ clauses(Clauses) 458 | WalkOptions 459 ]), 460 findall(Goal-From, retract(trivial_fail(From, Goal)), Pairs), 461 keysort(Pairs, Sorted), 462 group_pairs_by_key(Sorted, Grouped), 463 maplist(report_trivial_fail, Grouped) 464 ).
471trivial_fail_goal(pce_expansion:pce_class(_, _, template, _, _, _)). 472trivial_fail_goal(pce_host:property(system_source_prefix(_))). 473 474:- public 475 check_trivial_fail/3. 476 477check_trivial_fail(MGoal0, _Caller, From) :- 478 ( MGoal0 = M:Goal, 479 atom(M), 480 callable(Goal), 481 predicate_property(MGoal0, interpreted), 482 \+ predicate_property(MGoal0, dynamic), 483 \+ predicate_property(MGoal0, multifile), 484 \+ trivial_fail_goal(MGoal0) 485 -> ( predicate_property(MGoal0, meta_predicate(Meta)) 486 -> qualify_meta_goal(MGoal0, Meta, MGoal) 487 ; MGoal = MGoal0 488 ), 489 ( clause(MGoal, _) 490 -> true 491 ; assertz(trivial_fail(From, MGoal)) 492 ) 493 ; true 494 ). 495 496report_trivial_fail(Goal-FromList) :- 497 print_message(warning, check(trivial_failure(Goal, FromList))).
503qualify_meta_goal(M:Goal0, Meta, M:Goal) :- 504 functor(Goal0, F, N), 505 functor(Goal, F, N), 506 qualify_meta_goal(1, M, Meta, Goal0, Goal). 507 508qualify_meta_goal(N, M, Meta, Goal0, Goal) :- 509 arg(N, Meta, ArgM), 510 !, 511 arg(N, Goal0, Arg0), 512 arg(N, Goal, Arg), 513 N1 is N + 1, 514 ( module_qualified(ArgM) 515 -> add_module(Arg0, M, Arg) 516 ; Arg = Arg0 517 ), 518 meta_goal(N1, Meta, Goal0, Goal). 519meta_goal(_, _, _, _). 520 521add_module(Arg, M, M:Arg) :- 522 var(Arg), 523 !. 524add_module(M:Arg, _, MArg) :- 525 !, 526 add_module(Arg, M, MArg). 527add_module(Arg, M, M:Arg). 528 529module_qualified(N) :- integer(N), !. 530module_qualified(:). 531module_qualified(^).
double_quotes
from codes
to string
, creating packed string
objects. Warnings may be suppressed using the following
multifile hooks:
549list_strings :- 550 list_strings([module_class([user])]). 551 552list_strings(Options) :- 553 ( prolog_program_clause(ClauseRef, Options), 554 clause(Head, Body, ClauseRef), 555 \+ ( predicate_indicator(Head, PI), 556 string_predicate(PI) 557 ), 558 make_clause(Head, Body, Clause), 559 findall(T, 560 ( sub_term(T, Head), 561 string(T) 562 ; Head = M:_, 563 goal_in_body(Goal, M, Body), 564 ( valid_string_goal(Goal) 565 -> fail 566 ; sub_term(T, Goal), 567 string(T) 568 ) 569 ), Ts0), 570 sort(Ts0, Ts), 571 member(T, Ts), 572 message_context(ClauseRef, T, Clause, Context), 573 print_message(warning, 574 check(string_in_clause(T, Context))), 575 fail 576 ; true 577 ). 578 579make_clause(Head, true, Head) :- !. 580make_clause(Head, Body, (Head:-Body)).
rational_syntax
to natural
, creating rational numbers from
<integer>/<nonneg>. Options:
true
(default false
) also warn on rationals appearing
in arithmetic expressions.599list_rationals :- 600 list_rationals([module_class([user])]). 601 602list_rationals(Options) :- 603 ( option(arithmetic(DoArith), Options, false), 604 prolog_program_clause(ClauseRef, Options), 605 clause(Head, Body, ClauseRef), 606 make_clause(Head, Body, Clause), 607 findall(T, 608 ( sub_term(T, Head), 609 rational(T), 610 \+ integer(T) 611 ; Head = M:_, 612 goal_in_body(Goal, M, Body), 613 nonvar(Goal), 614 ( DoArith == false, 615 valid_rational_goal(Goal) 616 -> fail 617 ; sub_term(T, Goal), 618 rational(T), 619 \+ integer(T) 620 ) 621 ), Ts0), 622 sort(Ts0, Ts), 623 member(T, Ts), 624 message_context(ClauseRef, T, Clause, Context), 625 print_message(warning, 626 check(rational_in_clause(T, Context))), 627 fail 628 ; true 629 ). 630 631 632valid_rational_goal(_ is _). 633valid_rational_goal(_ =:= _). 634valid_rational_goal(_ < _). 635valid_rational_goal(_ > _). 636valid_rational_goal(_ =< _). 637valid_rational_goal(_ >= _).
645list_format_errors :- 646 list_format_errors([module_class([user])]). 647 648list_format_errors(Options) :- 649 ( prolog_program_clause(ClauseRef, Options), 650 clause(Head, Body, ClauseRef), 651 make_clause(Head, Body, Clause), 652 Head = M:_, 653 goal_in_body(Goal, M, Body), 654 format_warning(Goal, Msg), 655 message_context(ClauseRef, Goal, Clause, Context), 656 print_message(warning, check(Msg, Goal, Context)), 657 fail 658 ; true 659 ). 660 661format_warning(system:format(Format, Args), Msg) :- 662 nonvar(Format), 663 nonvar(Args), 664 \+ is_list(Args), 665 Msg = format_argv(Args). 666format_warning(system:format(Format, Args), Msg) :- 667 ground(Format), 668 ( is_list(Args) 669 -> length(Args, ArgC) 670 ; nonvar(Args) 671 -> ArgC = 1 672 ), 673 E = error(Formal,_), 674 catch(format_types(Format, Types), E, true), 675 ( var(Formal) 676 -> length(Types, TypeC), 677 TypeC =\= ArgC, 678 Msg = format_argc(TypeC, ArgC) 679 ; Msg = format_template(Formal) 680 ). 681format_warning(system:format(_Stream, Format, Args), Msg) :- 682 format_warning(system:format(Format, Args), Msg). 683format_warning(prolog_debug:debug(_Channel, Format, Args), Msg) :- 684 format_warning(system:format(Format, Args), Msg).
691goal_in_body(M:G, M, G) :- 692 var(G), 693 !. 694goal_in_body(G, _, M:G0) :- 695 atom(M), 696 !, 697 goal_in_body(G, M, G0). 698goal_in_body(G, M, Control) :- 699 nonvar(Control), 700 control(Control, Subs), 701 !, 702 member(Sub, Subs), 703 goal_in_body(G, M, Sub). 704goal_in_body(G, M, G0) :- 705 callable(G0), 706 ( atom(M) 707 -> TM = M 708 ; TM = system 709 ), 710 predicate_property(TM:G0, meta_predicate(Spec)), 711 !, 712 ( strip_goals(G0, Spec, G1), 713 simple_goal_in_body(G, M, G1) 714 ; arg(I, Spec, Meta), 715 arg(I, G0, G1), 716 extend(Meta, G1, G2), 717 goal_in_body(G, M, G2) 718 ). 719goal_in_body(G, M, G0) :- 720 simple_goal_in_body(G, M, G0). 721 722simple_goal_in_body(G, M, G0) :- 723 ( atom(M), 724 callable(G0), 725 predicate_property(M:G0, imported_from(M2)) 726 -> G = M2:G0 727 ; G = M:G0 728 ). 729 730control((A,B), [A,B]). 731control((A;B), [A,B]). 732control((A->B), [A,B]). 733control((A*->B), [A,B]). 734control((\+A), [A]). 735 736strip_goals(G0, Spec, G) :- 737 functor(G0, Name, Arity), 738 functor(G, Name, Arity), 739 strip_goal_args(1, G0, Spec, G). 740 741strip_goal_args(I, G0, Spec, G) :- 742 arg(I, G0, A0), 743 !, 744 arg(I, Spec, M), 745 ( extend(M, A0, _) 746 -> arg(I, G, '<meta-goal>') 747 ; arg(I, G, A0) 748 ), 749 I2 is I + 1, 750 strip_goal_args(I2, G0, Spec, G). 751strip_goal_args(_, _, _, _). 752 753extend(I, G0, G) :- 754 callable(G0), 755 integer(I), I>0, 756 !, 757 length(L, I), 758 extend_list(G0, L, G). 759extend(0, G, G). 760extend(^, G, G). 761 762extend_list(M:G0, L, M:G) :- 763 !, 764 callable(G0), 765 extend_list(G0, L, G). 766extend_list(G0, L, G) :- 767 G0 =.. List, 768 append(List, L, All), 769 G =.. All.
776message_context(ClauseRef, Term, Clause, file_term_position(File, TermPos)) :- 777 clause_info(ClauseRef, File, Layout, _Vars), 778 ( Term = _:Goal, 779 prolog_codewalk:subterm_pos(Goal, Clause, ==, Layout, TermPos) 780 ; prolog_codewalk:subterm_pos(Term, Clause, ==, Layout, TermPos) 781 ), 782 !. 783message_context(ClauseRef, _String, _Clause, file(File, Line, -1, _)) :- 784 clause_property(ClauseRef, file(File)), 785 clause_property(ClauseRef, line_count(Line)), 786 !. 787message_context(ClauseRef, _String, _Clause, clause(ClauseRef)). 788 789 790:- meta_predicate 791 predicate_indicator( , ). 792 793predicate_indicator(Module:Head, Module:Name/Arity) :- 794 functor(Head, Name, Arity). 795predicate_indicator(Module:Head, Module:Name//DCGArity) :- 796 functor(Head, Name, Arity), 797 DCGArity is Arity-2.
804string_predicate(_:'$pldoc'/4). 805string_predicate(pce_principal:send_implementation/3). 806string_predicate(pce_principal:pce_lazy_get_method/3). 807string_predicate(pce_principal:pce_lazy_send_method/3). 808string_predicate(pce_principal:pce_class/6). 809string_predicate(prolog_xref:pred_comment/4). 810string_predicate(prolog_xref:module_comment/3). 811string_predicate(pldoc_process:structured_comment//2). 812string_predicate(pldoc_process:structured_command_start/3). 813string_predicate(pldoc_process:separator_line//0). 814string_predicate(pldoc_register:mydoc/3). 815string_predicate(http_header:separators/1).
format("Hello world~n")
is considered proper use of
string constants.823% system predicates 824valid_string_goal(system:format(S)) :- string(S). 825valid_string_goal(system:format(S,_)) :- string(S). 826valid_string_goal(system:format(_,S,_)) :- string(S). 827valid_string_goal(system:string_codes(S,_)) :- string(S). 828valid_string_goal(system:string_code(_,S,_)) :- string(S). 829valid_string_goal(system:throw(msg(S,_))) :- string(S). 830valid_string_goal('$dcg':phrase(S,_,_)) :- string(S). 831valid_string_goal('$dcg':phrase(S,_)) :- string(S). 832valid_string_goal(system: is(_,_)). % arithmetic allows for "x" 833valid_string_goal(system: =:=(_,_)). 834valid_string_goal(system: >(_,_)). 835valid_string_goal(system: <(_,_)). 836valid_string_goal(system: >=(_,_)). 837valid_string_goal(system: =<(_,_)). 838% library stuff 839valid_string_goal(dcg_basics:string_without(S,_,_,_)) :- string(S). 840valid_string_goal(git:read_url(S,_,_)) :- string(S). 841valid_string_goal(tipc:tipc_subscribe(_,_,_,_,S)) :- string(S). 842valid_string_goal(charsio:format_to_chars(Format,_,_)) :- string(Format). 843valid_string_goal(charsio:format_to_chars(Format,_,_,_)) :- string(Format). 844valid_string_goal(codesio:format_to_codes(Format,_,_)) :- string(Format). 845valid_string_goal(codesio:format_to_codes(Format,_,_,_)) :- string(Format). 846 847 848 /******************************* 849 * EXTENSION HOOKS * 850 *******************************/
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,_)).
872checker(list_undefined, 'undefined predicates'). 873checker(list_trivial_fails, 'trivial failures'). 874checker(list_format_errors, 'format/2,3 and debug/3 templates'). 875checker(list_redefined, 'redefined system and global predicates'). 876checker(list_void_declarations, 'predicates with declarations but without clauses'). 877checker(list_autoload, 'predicates that need autoloading'). 878checker(check_predicate_options, 'predicate options lists'). 879 880 881 /******************************* 882 * MESSAGES * 883 *******************************/ 884 885:- multifile 886 prolog:message/3. 887 888prologmessage(check(pass(Comment))) --> 889 [ 'Checking ~w ...'-[Comment] ]. 890prologmessage(check(find_references(Preds))) --> 891 { length(Preds, N) 892 }, 893 [ 'Scanning for references to ~D possibly undefined predicates'-[N] ]. 894prologmessage(check(undefined_procedures, Grouped)) --> 895 [ 'The predicates below are not defined. If these are defined', nl, 896 'at runtime using assert/1, use :- dynamic Name/Arity.', nl, nl 897 ], 898 undefined_procedures(Grouped). 899prologmessage(check(undefined_unreferenced_predicates)) --> 900 [ 'The predicates below are not defined, and are not', nl, 901 'referenced.', nl, nl 902 ]. 903prologmessage(check(undefined_unreferenced(Pred))) --> 904 predicate(Pred). 905prologmessage(check(autoload(Module, Pairs))) --> 906 { module_property(Module, file(Path)) 907 }, 908 !, 909 [ 'Into module ~w ('-[Module] ], 910 short_filename(Path), 911 [ ')', nl ], 912 autoload(Pairs). 913prologmessage(check(autoload(Module, Pairs))) --> 914 [ 'Into module ~w'-[Module], nl ], 915 autoload(Pairs). 916prologmessage(check(redefined(In, From, Pred))) --> 917 predicate(In:Pred), 918 redefined(In, From). 919prologmessage(check(cross_module_calls)) --> 920 [ 'Qualified calls to private predicates'-[] ]. 921prologmessage(check(cross_module_call(Callee, _Caller, Location))) --> 922 { pi_head(PI, Callee) }, 923 [ ' '-[] ], 924 '$messages':swi_location(Location), 925 [ 'Cross-module call to ~p'-[PI] ]. 926prologmessage(check(trivial_failures)) --> 927 [ 'The following goals fail because there are no matching clauses.' ]. 928prologmessage(check(trivial_failure(Goal, Refs))) --> 929 { map_list_to_pairs(sort_reference_key, Refs, Keyed), 930 keysort(Keyed, KeySorted), 931 pairs_values(KeySorted, SortedRefs) 932 }, 933 goal(Goal), 934 [ ', which is called from'-[], nl ], 935 referenced_by(SortedRefs). 936prologmessage(check(string_in_clause(String, Context))) --> 937 '$messages':swi_location(Context), 938 [ 'String ~q'-[String] ]. 939prologmessage(check(rational_in_clause(String, Context))) --> 940 '$messages':swi_location(Context), 941 [ 'Rational ~q'-[String] ]. 942prologmessage(check(Msg, Goal, Context)) --> 943 '$messages':swi_location(Context), 944 { pi_head(PI, Goal) }, 945 [ nl, ' '-[] ], 946 predicate(PI), 947 [ ': '-[] ], 948 check_message(Msg). 949prologmessage(check(void_declaration(P, Decl))) --> 950 predicate(P), 951 [ ' is declared as ~p, but has no clauses'-[Decl] ]. 952 953undefined_procedures([]) --> 954 []. 955undefined_procedures([H|T]) --> 956 undefined_procedure(H), 957 undefined_procedures(T). 958 959undefined_procedure(Pred-Refs) --> 960 { map_list_to_pairs(sort_reference_key, Refs, Keyed), 961 keysort(Keyed, KeySorted), 962 pairs_values(KeySorted, SortedRefs) 963 }, 964 predicate(Pred), 965 [ ', which is referenced by', nl ], 966 referenced_by(SortedRefs). 967 968redefined(user, system) --> 969 [ '~t~30| System predicate redefined globally' ]. 970redefined(_, system) --> 971 [ '~t~30| Redefined system predicate' ]. 972redefined(_, user) --> 973 [ '~t~30| Redefined global predicate' ]. 974 975goal(user:Goal) --> 976 !, 977 [ '~p'-[Goal] ]. 978goal(Goal) --> 979 !, 980 [ '~p'-[Goal] ]. 981 982predicate(Module:Name/Arity) --> 983 { atom(Module), 984 atom(Name), 985 integer(Arity), 986 functor(Head, Name, Arity), 987 predicate_name(Module:Head, PName) 988 }, 989 !, 990 [ '~w'-[PName] ]. 991predicate(Module:Head) --> 992 { atom(Module), 993 callable(Head), 994 predicate_name(Module:Head, PName) 995 }, 996 !, 997 [ '~w'-[PName] ]. 998predicate(Name/Arity) --> 999 { atom(Name), 1000 integer(Arity) 1001 }, 1002 !, 1003 predicate(user:Name/Arity). 1004 1005autoload([]) --> 1006 []. 1007autoload([Lib-Pred|T]) --> 1008 [ ' ' ], 1009 predicate(Pred), 1010 [ '~t~24| from ' ], 1011 short_filename(Lib), 1012 [ nl ], 1013 autoload(T).
1019sort_reference_key(Term, key(M:Name/Arity, N, ClausePos)) :- 1020 clause_ref(Term, ClauseRef, ClausePos), 1021 !, 1022 nth_clause(Pred, N, ClauseRef), 1023 strip_module(Pred, M, Head), 1024 functor(Head, Name, Arity). 1025sort_reference_key(Term, Term). 1026 1027clause_ref(clause_term_position(ClauseRef, TermPos), ClauseRef, ClausePos) :- 1028 arg(1, TermPos, ClausePos). 1029clause_ref(clause(ClauseRef), ClauseRef, 0). 1030 1031 1032referenced_by([]) --> 1033 []. 1034referenced_by([Ref|T]) --> 1035 ['\t'], prolog:message_location(Ref), 1036 predicate_indicator(Ref), 1037 [ nl ], 1038 referenced_by(T). 1039 1040predicate_indicator(clause_term_position(ClauseRef, _)) --> 1041 { nonvar(ClauseRef) }, 1042 !, 1043 predicate_indicator(clause(ClauseRef)). 1044predicate_indicator(clause(ClauseRef)) --> 1045 { clause_name(ClauseRef, Name) }, 1046 [ '~w'-[Name] ]. 1047predicate_indicator(file_term_position(_,_)) --> 1048 [ '(initialization)' ]. 1049predicate_indicator(file(_,_,_,_)) --> 1050 [ '(initialization)' ]. 1051 1052 1053short_filename(Path) --> 1054 { short_filename(Path, Spec) 1055 }, 1056 [ '~q'-[Spec] ]. 1057 1058short_filename(Path, Spec) :- 1059 absolute_file_name('', Here), 1060 atom_concat(Here, Local0, Path), 1061 !, 1062 remove_leading_slash(Local0, Spec). 1063short_filename(Path, Spec) :- 1064 findall(LenAlias, aliased_path(Path, LenAlias), Keyed), 1065 keysort(Keyed, [_-Spec|_]). 1066short_filename(Path, Path). 1067 1068aliased_path(Path, Len-Spec) :- 1069 setof(Alias, Spec^(user:file_search_path(Alias, Spec)), Aliases), 1070 member(Alias, Aliases), 1071 Term =.. [Alias, '.'], 1072 absolute_file_name(Term, 1073 [ file_type(directory), 1074 file_errors(fail), 1075 solutions(all) 1076 ], Prefix), 1077 atom_concat(Prefix, Local0, Path), 1078 remove_leading_slash(Local0, Local), 1079 atom_length(Local, Len), 1080 Spec =.. [Alias, Local]. 1081 1082remove_leading_slash(Path, Local) :- 1083 atom_concat(/, Local, Path), 1084 !. 1085remove_leading_slash(Path, Path). 1086 1087check_message(format_argc(Expected, InList)) --> 1088 [ 'Template requires ~w arguments, got ~w'-[Expected, InList] ]. 1089check_message(format_template(Formal)) --> 1090 { message_to_string(error(Formal, _), Msg) }, 1091 [ 'Invalid template: ~s'-[Msg] ]. 1092check_message(format_argv(Args)) --> 1093 [ 'Arguments are not in a list (deprecated): ~p'-[Args] ]
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.