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-2012, University of Amsterdam 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(check, 37 [ check/0, % run all checks 38 list_undefined/0, % list undefined predicates 39 list_undefined/1, % +Options 40 list_autoload/0, % list predicates that need autoloading 41 list_redefined/0, % list redefinitions 42 list_void_declarations/0, % list declarations with no clauses 43 list_trivial_fails/0, % list goals that trivially fail 44 list_trivial_fails/1, % +Options 45 list_strings/0, % list string objects in clauses 46 list_strings/1 % +Options 47 ]). 48:- use_module(library(lists)). 49:- use_module(library(pairs)). 50:- use_module(library(option)). 51:- use_module(library(apply)). 52:- use_module(library(prolog_codewalk)). 53:- use_module(library(occurs)). 54 55:- set_prolog_flag(generate_debug_info, false). 56 57:- multifile 58 trivial_fail_goal/1, 59 string_predicate/1, 60 valid_string_goal/1, 61 checker/2. 62 63:- dynamic checker/2.
78:- predicate_options(list_undefined/1, 1,
79 [ module_class(list(oneof([user,library])))
80 ]).
96check :- 97 checker(Checker, Message), 98 print_message(informational,check(pass(Message))), 99 catch(,E,print_message(error,E)), 100 fail. 101check.
[user]
. For example, to include the
libraries into the examination, use [user,library]
.118:- thread_local 119 undef/2. 120 121list_undefined :- 122 list_undefined([]). 123 124list_undefined(Options) :- 125 merge_options(Options, 126 [ module_class([user]) 127 ], 128 WalkOptions), 129 prolog_walk_code([ undefined(trace), 130 on_trace(found_undef) 131 | WalkOptions 132 ]), 133 findall(PI-From, retract(undef(PI, From)), Pairs), 134 ( Pairs == [] 135 -> true 136 ; print_message(warning, check(undefined_predicates)), 137 keysort(Pairs, Sorted), 138 group_pairs_by_key(Sorted, Grouped), 139 maplist(report_undefined, Grouped) 140 ). 141 142:- public found_undef/3. 143 144found_undef(To, _Caller, From) :- 145 goal_pi(To, PI), 146 ( undef(PI, From) 147 -> true 148 ; compiled(PI) 149 -> true 150 ; assertz(undef(PI,From)) 151 ). 152 153compiled(system:'$call_cleanup'/0). % compiled to VM instructions 154compiled(system:'$catch'/0). 155compiled(system:'$cut'/0). 156compiled(system:'$reset'/0). 157compiled(system:'$call_continuation'/1). 158compiled(system:'$shift'/1). 159 160goal_pi(M:Head, M:Name/Arity) :- 161 functor(Head, Name, Arity). 162 163report_undefined(PI-FromList) :- 164 print_message(warning, check(undefined(PI, FromList))).
178list_autoload :- 179 setup_call_cleanup( 180 ( current_prolog_flag(access_level, OldLevel), 181 current_prolog_flag(autoload, OldAutoLoad), 182 set_prolog_flag(access_level, system), 183 set_prolog_flag(autoload, false) 184 ), 185 list_autoload_(OldLevel), 186 ( set_prolog_flag(access_level, OldLevel), 187 set_prolog_flag(autoload, OldAutoLoad) 188 )). 189 190list_autoload_(SystemMode) :- 191 ( setof(Lib-Pred, 192 autoload_predicate(Module, Lib, Pred, SystemMode), 193 Pairs), 194 print_message(informational, 195 check(autoload(Module, Pairs))), 196 fail 197 ; true 198 ). 199 200autoload_predicate(Module, Library, Name/Arity, SystemMode) :- 201 predicate_property(Module:Head, undefined), 202 check_module_enabled(Module, SystemMode), 203 ( \+ predicate_property(Module:Head, imported_from(_)), 204 functor(Head, Name, Arity), 205 '$find_library'(Module, Name, Arity, _LoadModule, Library), 206 referenced(Module:Head, Module, _) 207 -> true 208 ). 209 210check_module_enabled(_, system) :- !. 211check_module_enabled(Module, _) :- 212 \+ import_module(Module, system).
218referenced(Term, Module, Ref) :-
219 Goal = Module:_Head,
220 current_predicate(_, Goal),
221 '$get_predicate_attribute'(Goal, system, 0),
222 \+ '$get_predicate_attribute'(Goal, imported, _),
223 nth_clause(Goal, _, Ref),
224 '$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.232list_redefined :- 233 setup_call_cleanup( 234 ( current_prolog_flag(access_level, OldLevel), 235 set_prolog_flag(access_level, system) 236 ), 237 list_redefined_, 238 set_prolog_flag(access_level, OldLevel)). 239 240list_redefined_ :- 241 current_module(Module), 242 Module \== system, 243 current_predicate(_, Module:Head), 244 \+ predicate_property(Module:Head, imported_from(_)), 245 ( global_module(Super), 246 Super \== Module, 247 '$c_current_predicate'(_, Super:Head), 248 \+ redefined_ok(Head), 249 '$syspreds':'$defined_predicate'(Super:Head), 250 \+ predicate_property(Super:Head, (dynamic)), 251 \+ predicate_property(Super:Head, imported_from(Module)), 252 functor(Head, Name, Arity) 253 -> print_message(informational, 254 check(redefined(Module, Super, Name/Arity))) 255 ), 256 fail. 257list_redefined_. 258 259redefined_ok('$mode'(_,_)). 260redefined_ok('$pldoc'(_,_,_,_)). 261redefined_ok('$pred_option'(_,_,_,_)). 262 263global_module(user). 264global_module(system).
270list_void_declarations :- 271 P = _:_, 272 ( predicate_property(P, undefined), 273 ( '$get_predicate_attribute'(P, meta_predicate, Pattern), 274 print_message(warning, 275 check(void_declaration(P, meta_predicate(Pattern)))) 276 ; void_attribute(Attr), 277 '$get_predicate_attribute'(P, Attr, 1), 278 print_message(warning, 279 check(void_declaration(P, Attr))) 280 ), 281 fail 282 ; true 283 ). 284 285void_attribute(public). 286void_attribute(volatile).
[user]
. For example, to include the
libraries into the examination, use [user,library]
.299:- thread_local 300 trivial_fail/2. 301 302list_trivial_fails :- 303 list_trivial_fails([]). 304 305list_trivial_fails(Options) :- 306 merge_options(Options, 307 [ module_class([user]), 308 infer_meta_predicates(false), 309 autoload(false), 310 evaluate(false), 311 trace_reference(_), 312 on_trace(check_trivial_fail) 313 ], 314 WalkOptions), 315 316 prolog_walk_code([ source(false) 317 | WalkOptions 318 ]), 319 findall(CRef, retract(trivial_fail(clause(CRef), _)), Clauses), 320 ( Clauses == [] 321 -> true 322 ; print_message(warning, check(trivial_failures)), 323 prolog_walk_code([ clauses(Clauses) 324 | WalkOptions 325 ]), 326 findall(Goal-From, retract(trivial_fail(From, Goal)), Pairs), 327 keysort(Pairs, Sorted), 328 group_pairs_by_key(Sorted, Grouped), 329 maplist(report_trivial_fail, Grouped) 330 ).
337trivial_fail_goal(pce_expansion:pce_class(_, _, template, _, _, _)). 338trivial_fail_goal(pce_host:property(system_source_prefix(_))). 339 340:- public 341 check_trivial_fail/3. 342 343check_trivial_fail(MGoal0, _Caller, From) :- 344 ( MGoal0 = M:Goal, 345 atom(M), 346 callable(Goal), 347 predicate_property(MGoal0, interpreted), 348 \+ predicate_property(MGoal0, dynamic), 349 \+ predicate_property(MGoal0, multifile), 350 \+ trivial_fail_goal(MGoal0) 351 -> ( predicate_property(MGoal0, meta_predicate(Meta)) 352 -> qualify_meta_goal(MGoal0, Meta, MGoal) 353 ; MGoal = MGoal0 354 ), 355 ( clause(MGoal, _) 356 -> true 357 ; assertz(trivial_fail(From, MGoal)) 358 ) 359 ; true 360 ). 361 362report_trivial_fail(Goal-FromList) :- 363 print_message(warning, check(trivial_failure(Goal, FromList))).
369qualify_meta_goal(M:Goal0, Meta, M:Goal) :- 370 functor(Goal0, F, N), 371 functor(Goal, F, N), 372 qualify_meta_goal(1, M, Meta, Goal0, Goal). 373 374qualify_meta_goal(N, M, Meta, Goal0, Goal) :- 375 arg(N, Meta, ArgM), 376 !, 377 arg(N, Goal0, Arg0), 378 arg(N, Goal, Arg), 379 N1 is N + 1, 380 ( module_qualified(ArgM) 381 -> add_module(Arg0, M, Arg) 382 ; Arg = Arg0 383 ), 384 meta_goal(N1, Meta, Goal0, Goal). 385meta_goal(_, _, _, _). 386 387add_module(Arg, M, M:Arg) :- 388 var(Arg), 389 !. 390add_module(M:Arg, _, MArg) :- 391 !, 392 add_module(Arg, M, MArg). 393add_module(Arg, M, M:Arg). 394 395module_qualified(N) :- integer(N), !. 396module_qualified(:). 397module_qualified(^).
double_quotes
from codes
to string
, creating packed string
objects. Warnings may be suppressed using the following
multifile hooks:
415list_strings :- 416 list_strings([module_class([user])]). 417 418list_strings(Options) :- 419 ( prolog_program_clause(ClauseRef, Options), 420 clause(Head, Body, ClauseRef), 421 \+ ( predicate_indicator(Head, PI), 422 string_predicate(PI) 423 ), 424 make_clause(Head, Body, Clause), 425 findall(T, 426 ( sub_term(T, Head), 427 string(T) 428 ; Head = M:_, 429 goal_in_body(Goal, M, Body), 430 ( valid_string_goal(Goal) 431 -> fail 432 ; sub_term(T, Goal), 433 string(T) 434 ) 435 ), Ts0), 436 sort(Ts0, Ts), 437 member(T, Ts), 438 message_context(ClauseRef, T, Clause, Context), 439 print_message(warning, 440 check(string_in_clause(T, Context))), 441 fail 442 ; true 443 ). 444 445make_clause(Head, true, Head) :- !. 446make_clause(Head, Body, (Head:-Body)).
452goal_in_body(M:G, M, G) :- 453 var(G), 454 !. 455goal_in_body(G, _, M:G0) :- 456 atom(M), 457 !, 458 goal_in_body(G, M, G0). 459goal_in_body(G, M, Control) :- 460 nonvar(Control), 461 control(Control, Subs), 462 !, 463 member(Sub, Subs), 464 goal_in_body(G, M, Sub). 465goal_in_body(G, M, G0) :- 466 callable(G0), 467 ( atom(M) 468 -> TM = M 469 ; TM = system 470 ), 471 predicate_property(TM:G0, meta_predicate(Spec)), 472 !, 473 ( strip_goals(G0, Spec, G1), 474 simple_goal_in_body(G, M, G1) 475 ; arg(I, Spec, Meta), 476 arg(I, G0, G1), 477 extend(Meta, G1, G2), 478 goal_in_body(G, M, G2) 479 ). 480goal_in_body(G, M, G0) :- 481 simple_goal_in_body(G, M, G0). 482 483simple_goal_in_body(G, M, G0) :- 484 ( atom(M), 485 callable(G0), 486 predicate_property(M:G0, imported_from(M2)) 487 -> G = M2:G0 488 ; G = M:G0 489 ). 490 491control((A,B), [A,B]). 492control((A;B), [A,B]). 493control((A->B), [A,B]). 494control((A*->B), [A,B]). 495control((\+A), [A]). 496 497strip_goals(G0, Spec, G) :- 498 functor(G0, Name, Arity), 499 functor(G, Name, Arity), 500 strip_goal_args(1, G0, Spec, G). 501 502strip_goal_args(I, G0, Spec, G) :- 503 arg(I, G0, A0), 504 !, 505 arg(I, Spec, M), 506 ( extend(M, A0, _) 507 -> arg(I, G, '<meta-goal>') 508 ; arg(I, G, A0) 509 ), 510 I2 is I + 1, 511 strip_goal_args(I2, G0, Spec, G). 512strip_goal_args(_, _, _, _). 513 514extend(I, G0, G) :- 515 callable(G0), 516 integer(I), I>0, 517 !, 518 length(L, I), 519 extend_list(G0, L, G). 520extend(0, G, G). 521extend(^, G, G). 522 523extend_list(M:G0, L, M:G) :- 524 !, 525 callable(G0), 526 extend_list(G0, L, G). 527extend_list(G0, L, G) :- 528 G0 =.. List, 529 append(List, L, All), 530 G =.. All. 531 532 533message_context(ClauseRef, String, Clause, file_term_position(File, StringPos)) :- 534 clause_info(ClauseRef, File, TermPos, _Vars), 535 prolog_codewalk:subterm_pos(String, Clause, ==, TermPos, StringPos), 536 !. 537message_context(ClauseRef, _String, _Clause, file(File, Line, -1, _)) :- 538 clause_property(ClauseRef, file(File)), 539 clause_property(ClauseRef, line_count(Line)), 540 !. 541message_context(ClauseRef, _String, _Clause, clause(ClauseRef)). 542 543 544:- meta_predicate 545 predicate_indicator( , ). 546 547predicate_indicator(Module:Head, Module:Name/Arity) :- 548 functor(Head, Name, Arity). 549predicate_indicator(Module:Head, Module:Name//DCGArity) :- 550 functor(Head, Name, Arity), 551 DCGArity is Arity-2.
558string_predicate(_:'$pldoc'/4). 559string_predicate(pce_principal:send_implementation/3). 560string_predicate(pce_principal:pce_lazy_get_method/3). 561string_predicate(pce_principal:pce_lazy_send_method/3). 562string_predicate(pce_principal:pce_class/6). 563string_predicate(prolog_xref:pred_comment/4). 564string_predicate(prolog_xref:module_comment/3). 565string_predicate(pldoc_process:structured_comment//2). 566string_predicate(pldoc_process:structured_command_start/3). 567string_predicate(pldoc_process:separator_line//0). 568string_predicate(pldoc_register:mydoc/3). 569string_predicate(http_header:separators/1).
format("Hello world~n")
is considered proper use of
string constants.577% system predicates 578valid_string_goal(system:format(S)) :- string(S). 579valid_string_goal(system:format(S,_)) :- string(S). 580valid_string_goal(system:format(_,S,_)) :- string(S). 581valid_string_goal(system:string_codes(S,_)) :- string(S). 582valid_string_goal(system:string_code(_,S,_)) :- string(S). 583valid_string_goal(system:throw(msg(S,_))) :- string(S). 584valid_string_goal('$dcg':phrase(S,_,_)) :- string(S). 585valid_string_goal('$dcg':phrase(S,_)) :- string(S). 586valid_string_goal(system: is(_,_)). % arithmetic allows for "x" 587valid_string_goal(system: =:=(_,_)). 588valid_string_goal(system: >(_,_)). 589valid_string_goal(system: <(_,_)). 590valid_string_goal(system: >=(_,_)). 591valid_string_goal(system: =<(_,_)). 592% library stuff 593valid_string_goal(dcg_basics:string_without(S,_,_,_)) :- string(S). 594valid_string_goal(git:read_url(S,_,_)) :- string(S). 595valid_string_goal(tipc:tipc_subscribe(_,_,_,_,S)) :- string(S). 596valid_string_goal(charsio:format_to_chars(Format,_,_)) :- string(Format). 597valid_string_goal(charsio:format_to_chars(Format,_,_,_)) :- string(Format). 598valid_string_goal(codesio:format_to_codes(Format,_,_)) :- string(Format). 599valid_string_goal(codesio:format_to_codes(Format,_,_,_)) :- string(Format). 600 601 602 /******************************* 603 * EXTENSION HOOKS * 604 *******************************/
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,_)).
626checker(list_undefined, 'undefined predicates'). 627checker(list_trivial_fails, 'trivial failures'). 628checker(list_redefined, 'redefined system and global predicates'). 629checker(list_void_declarations, 'predicates with declarations but without clauses'). 630checker(list_autoload, 'predicates that need autoloading'). 631 632 633 /******************************* 634 * MESSAGES * 635 *******************************/ 636 637:- multifile 638 prolog:message/3. 639 640prologmessage(check(pass(Comment))) --> 641 [ 'Checking ~w ...'-[Comment] ]. 642prologmessage(check(find_references(Preds))) --> 643 { length(Preds, N) 644 }, 645 [ 'Scanning for references to ~D possibly undefined predicates'-[N] ]. 646prologmessage(check(undefined_predicates)) --> 647 [ 'The predicates below are not defined. If these are defined', nl, 648 'at runtime using assert/1, use :- dynamic Name/Arity.', nl, nl 649 ]. 650prologmessage(check(undefined(Pred, Refs))) --> 651 { map_list_to_pairs(sort_reference_key, Refs, Keyed), 652 keysort(Keyed, KeySorted), 653 pairs_values(KeySorted, SortedRefs) 654 }, 655 predicate(Pred), 656 [ ', which is referenced by', nl ], 657 referenced_by(SortedRefs). 658prologmessage(check(undefined_unreferenced_predicates)) --> 659 [ 'The predicates below are not defined, and are not', nl, 660 'referenced.', nl, nl 661 ]. 662prologmessage(check(undefined_unreferenced(Pred))) --> 663 predicate(Pred). 664prologmessage(check(autoload(Module, Pairs))) --> 665 { module_property(Module, file(Path)) 666 }, 667 !, 668 [ 'Into module ~w ('-[Module] ], 669 short_filename(Path), 670 [ ')', nl ], 671 autoload(Pairs). 672prologmessage(check(autoload(Module, Pairs))) --> 673 [ 'Into module ~w'-[Module], nl ], 674 autoload(Pairs). 675prologmessage(check(redefined(In, From, Pred))) --> 676 predicate(In:Pred), 677 redefined(In, From). 678prologmessage(check(trivial_failures)) --> 679 [ 'The following goals fail because there are no matching clauses.' ]. 680prologmessage(check(trivial_failure(Goal, Refs))) --> 681 { map_list_to_pairs(sort_reference_key, Refs, Keyed), 682 keysort(Keyed, KeySorted), 683 pairs_values(KeySorted, SortedRefs) 684 }, 685 goal(Goal), 686 [ ', which is called from'-[], nl ], 687 referenced_by(SortedRefs). 688prologmessage(check(string_in_clause(String, Context))) --> 689 prolog:message_location(Context), 690 [ 'String ~q'-[String] ]. 691prologmessage(check(void_declaration(P, Decl))) --> 692 predicate(P), 693 [ ' is declared as ~p, but has no clauses'-[Decl] ]. 694 695 696redefined(user, system) --> 697 [ '~t~30| System predicate redefined globally' ]. 698redefined(_, system) --> 699 [ '~t~30| Redefined system predicate' ]. 700redefined(_, user) --> 701 [ '~t~30| Redefined global predicate' ]. 702 703goal(user:Goal) --> 704 !, 705 [ '~p'-[Goal] ]. 706goal(Goal) --> 707 !, 708 [ '~p'-[Goal] ]. 709 710predicate(Module:Name/Arity) --> 711 { atom(Module), 712 atom(Name), 713 integer(Arity), 714 functor(Head, Name, Arity), 715 predicate_name(Module:Head, PName) 716 }, 717 !, 718 [ '~w'-[PName] ]. 719predicate(Module:Head) --> 720 { atom(Module), 721 callable(Head), 722 predicate_name(Module:Head, PName) 723 }, 724 !, 725 [ '~w'-[PName] ]. 726predicate(Name/Arity) --> 727 { atom(Name), 728 integer(Arity) 729 }, 730 !, 731 predicate(user:Name/Arity). 732 733autoload([]) --> 734 []. 735autoload([Lib-Pred|T]) --> 736 [ ' ' ], 737 predicate(Pred), 738 [ '~t~24| from ' ], 739 short_filename(Lib), 740 [ nl ], 741 autoload(T).
747sort_reference_key(Term, key(M:Name/Arity, N, ClausePos)) :- 748 clause_ref(Term, ClauseRef, ClausePos), 749 !, 750 nth_clause(Pred, N, ClauseRef), 751 strip_module(Pred, M, Head), 752 functor(Head, Name, Arity). 753sort_reference_key(Term, Term). 754 755clause_ref(clause_term_position(ClauseRef, TermPos), ClauseRef, ClausePos) :- 756 arg(1, TermPos, ClausePos). 757clause_ref(clause(ClauseRef), ClauseRef, 0). 758 759 760referenced_by([]) --> 761 []. 762referenced_by([Ref|T]) --> 763 ['\t'], prolog:message_location(Ref), 764 predicate_indicator(Ref), 765 [ nl ], 766 referenced_by(T). 767 768predicate_indicator(clause_term_position(ClauseRef, _)) --> 769 { nonvar(ClauseRef) }, 770 !, 771 predicate_indicator(clause(ClauseRef)). 772predicate_indicator(clause(ClauseRef)) --> 773 { clause_name(ClauseRef, Name) }, 774 [ '~w'-[Name] ]. 775predicate_indicator(file_term_position(_,_)) --> 776 [ '(initialization)' ]. 777predicate_indicator(file(_,_,_,_)) --> 778 [ '(initialization)' ]. 779 780 781short_filename(Path) --> 782 { short_filename(Path, Spec) 783 }, 784 [ '~q'-[Spec] ]. 785 786short_filename(Path, Spec) :- 787 absolute_file_name('', Here), 788 atom_concat(Here, Local0, Path), 789 !, 790 remove_leading_slash(Local0, Spec). 791short_filename(Path, Spec) :- 792 findall(LenAlias, aliased_path(Path, LenAlias), Keyed), 793 keysort(Keyed, [_-Spec|_]). 794short_filename(Path, Path). 795 796aliased_path(Path, Len-Spec) :- 797 setof(Alias, Spec^(user:file_search_path(Alias, Spec)), Aliases), 798 member(Alias, Aliases), 799 Term =.. [Alias, '.'], 800 absolute_file_name(Term, 801 [ file_type(directory), 802 file_errors(fail), 803 solutions(all) 804 ], Prefix), 805 atom_concat(Prefix, Local0, Path), 806 remove_leading_slash(Local0, Local), 807 atom_length(Local, Len), 808 Spec =.. [Alias, Local]. 809 810remove_leading_slash(Path, Local) :- 811 atom_concat(/, Local, Path), 812 !. 813remove_leading_slash(Path, Path)
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.
library(prolog_xref)
implements `offline' cross-referencinglibrary(prolog_codewalk)
implements `online' analysis */