View source with formatted 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)  2012-2020, VU University Amsterdam
    7                              CWI, 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(prolog_codewalk,
   37          [ prolog_walk_code/1,         % +Options
   38            prolog_program_clause/2     % -ClauseRef, +Options
   39          ]).   40:- use_module(library(record),[(record)/1, op(_,_,record)]).   41
   42:- autoload(library(apply),[maplist/2]).   43:- autoload(library(debug),[debug/3,debugging/1,assertion/1]).   44:- autoload(library(error),[must_be/2]).   45:- autoload(library(listing),[portray_clause/1]).   46:- autoload(library(lists),[member/2,nth1/3,append/3]).   47:- autoload(library(option),[meta_options/3]).   48:- autoload(library(prolog_clause),
   49	    [clause_info/4,initialization_layout/4,clause_name/2]).   50:- autoload(library(prolog_metainference),
   51	    [inferred_meta_predicate/2,infer_meta_predicate/2]).   52
   53
   54/** <module> Prolog code walker
   55
   56This module walks over  the  loaded   program,  searching  for  callable
   57predicates. It started as part of  library(prolog_autoload) and has been
   58turned into a separate module to  facilitate operations that require the
   59same reachability analysis, such as finding   references to a predicate,
   60finding unreachable code, etc.
   61
   62For example, the following  determins  the   call  graph  of  the loaded
   63program. By using source(true), The exact location   of  the call in the
   64source file is passed into _Where.
   65
   66  ==
   67  :- dynamic
   68          calls/2.
   69
   70  assert_call_graph :-
   71          retractall(calls(_, _)),
   72          prolog_walk_code([ trace_reference(_),
   73                             on_trace(assert_edge),
   74                             source(false)
   75                           ]),
   76          predicate_property(calls(_,_), number_of_clauses(N)),
   77          format('Got ~D edges~n', [N]).
   78
   79  assert_edge(Callee, Caller, _Where) :-
   80          calls(Caller, Callee), !.
   81  assert_edge(Callee, Caller, _Where) :-
   82          assertz(calls(Caller, Callee)).
   83  ==
   84*/
   85
   86:- meta_predicate
   87    prolog_walk_code(:).   88
   89:- multifile
   90    prolog:called_by/4,
   91    prolog:called_by/2.   92
   93:- predicate_options(prolog_walk_code/1, 1,
   94                     [ undefined(oneof([ignore,error,trace])),
   95                       autoload(boolean),
   96                       clauses(list),
   97                       module(atom),
   98                       module_class(list(oneof([user,system,library,
   99                                                test,development]))),
  100                       source(boolean),
  101                       trace_reference(any),
  102                       trace_condition(callable),
  103                       on_trace(callable),
  104                       on_edge(callable),
  105                       infer_meta_predicates(oneof([false,true,all])),
  106                       walk_meta_predicates(boolean),
  107                       evaluate(boolean),
  108                       verbose(boolean)
  109                     ]).  110
  111:- record
  112    walk_option(undefined:oneof([ignore,error,trace])=ignore,
  113                autoload:boolean=true,
  114                source:boolean=true,
  115                module:atom,                % Only analyse given module
  116                module_class:list(oneof([user,system,library,
  117                                         test,development]))=[user,library],
  118                infer_meta_predicates:oneof([false,true,all])=true,
  119                walk_meta_predicates:boolean=true,
  120                clauses:list,               % Walk only these clauses
  121                trace_reference:any=(-),
  122                trace_condition:callable,   % Call-back condition
  123                on_edge:callable,           % Call-back on trace hits
  124                on_trace:callable,          % Call-back on trace hits
  125                                            % private stuff
  126                clause,                     % Processed clause
  127                caller,                     % Head of the caller
  128                initialization,             % Initialization source
  129                undecided,                  % Error to throw error
  130                evaluate:boolean,           % Do partial evaluation
  131                verbose:boolean=false).     % Report progress
  132
  133:- thread_local
  134    multifile_predicate/3.          % Name, Arity, Module
  135
  136%!  prolog_walk_code(+Options) is det.
  137%
  138%   Walk over all loaded (user) Prolog code. The following code is
  139%   processed:
  140%
  141%     1. The bodies of all clauses in all user and library modules.
  142%        This steps collects, but does not scan multifile predicates
  143%        to avoid duplicate work.
  144%     2. All multi-file predicates collected.
  145%     3. All goals registered with initialization/1
  146%
  147%   Options processed:
  148%
  149%     * undefined(+Action)
  150%     Action defines what happens if the analysis finds a
  151%     definitely undefined predicate.  One of `ignore` or
  152%     `error` (default is `ignore`).
  153%
  154%     * autoload(+Boolean)
  155%     Try to autoload code while walking. This is enabled by default
  156%     to obtain as much as possible information about goals and find
  157%     references from autoloaded libraries.
  158%
  159%     * clauses(+ListOfClauseReferences)
  160%     Only process the given clauses.  Can be used to find clauses
  161%     quickly using source(false) and then process only interesting
  162%     clauses with source information.
  163%
  164%     * module(+Module)
  165%     Only process the given module
  166%
  167%     * module_class(+ModuleClassList)
  168%     Limit processing to modules of the given classes. See
  169%     module_property/2 for details on module classes.  Default
  170%     is to scan the classes =user= and =library=.
  171%
  172%     * infer_meta_predicates(+BooleanOrAll)
  173%     Use infer_meta_predicate/2 on predicates with clauses that
  174%     call known meta-predicates.  The analysis is restarted until
  175%     a fixed point is reached.  If =true= (default), analysis is
  176%     only restarted if the inferred meta-predicate contains a
  177%     callable argument.  If =all=, it will be restarted until no
  178%     more new meta-predicates can be found.
  179%
  180%     * walk_meta_predicates(Boolean)
  181%     When `false` (default `true`), do not analyse the arguments
  182%     of meta predicates.  Standard Prolog control structures are
  183%     always analysed.
  184%
  185%     * trace_reference(Callable)
  186%     Print all calls to goals that subsume Callable. Goals are
  187%     represented as Module:Callable (i.e., they are always
  188%     qualified).  See also subsumes_term/2.
  189%
  190%     * trace_condition(:Cond)
  191%     Additional filter condition applied after `trace_reference`.
  192%     Called as call(Cond, Callee, Context), where `Context` is a
  193%     dict containing the following keys:
  194%
  195%       - Context:caller
  196%         Qualified term representing the caller or the atom
  197%         '<initialization>'.
  198%       - Context:module
  199%         Module being processed
  200%       - Context:clause
  201%         If we are processing a normal clause, the clause reference
  202%         to this clause.
  203%       - Context:initialization
  204%         If we are processing an initialization/1 directive, a term
  205%         `File:Line` representing the location of the declaration.
  206%
  207%     * on_edge(:OnEdge)
  208%     If a reference to `trace_reference` is found, call
  209%     call(OnEdge, Callee, Caller, Location), where `Location` is a
  210%     dict containing a subset of the keys `clause`, `file`,
  211%     `character_count`, `line_count` and `line_position`.  If
  212%     full position information is available all keys are present.
  213%     If the clause layout is unknown the only the `clause`, `file`
  214%     and `line_count` are available and the line is the start line
  215%     of the clause.  For a dynamic clause, only the `clause` is
  216%     present.  If the position is associated to a _directive_,
  217%     the `clause` is missing.   If nothing is known the `Location`
  218%     is an empty dict.
  219%
  220%     * on_trace(:OnTrace)
  221%     As `on_edge`, but location is not translated and is one
  222%     of these:
  223%
  224%       - clause_term_position(+ClauseRef, +TermPos)
  225%       - clause(+ClauseRef)
  226%       - file_term_position(+Path, +TermPos)
  227%       - file(+File, +Line, -1, _)
  228%       - a variable (unknown)
  229%
  230%     Caller is the qualified head of the calling clause or the
  231%     atom '<initialization>'.
  232%
  233%     * source(+Boolean)
  234%     If `false` (default `true`), to not try to obtain detailed
  235%     source information for printed messages.
  236%
  237%     * verbose(+Boolean)
  238%     If `true` (default `false`), report derived meta-predicates
  239%     and iterations.
  240%
  241%     @compat OnTrace was called using Caller-Location in older
  242%             versions.
  243
  244prolog_walk_code(Options) :-
  245    meta_options(is_meta, Options, QOptions),
  246    prolog_walk_code(1, QOptions).
  247
  248prolog_walk_code(Iteration, Options) :-
  249    statistics(cputime, CPU0),
  250    make_walk_option(Options, OTerm, _),
  251    (   walk_option_clauses(OTerm, Clauses),
  252        nonvar(Clauses)
  253    ->  walk_clauses(Clauses, OTerm)
  254    ;   forall(( walk_option_module(OTerm, M0),
  255                 copy_term(M0, M),
  256                 current_module(M),
  257                 scan_module(M, OTerm)
  258               ),
  259               find_walk_from_module(M, OTerm)),
  260        walk_from_multifile(OTerm),
  261        walk_from_initialization(OTerm)
  262    ),
  263    infer_new_meta_predicates(New, OTerm),
  264    statistics(cputime, CPU1),
  265    (   New \== []
  266    ->  CPU is CPU1-CPU0,
  267        (   walk_option_verbose(OTerm, true)
  268        ->  Level = informational
  269        ;   Level = silent
  270        ),
  271        print_message(Level,
  272                      codewalk(reiterate(New, Iteration, CPU))),
  273        succ(Iteration, Iteration2),
  274        prolog_walk_code(Iteration2, Options)
  275    ;   true
  276    ).
  277
  278is_meta(on_edge).
  279is_meta(on_trace).
  280is_meta(trace_condition).
  281
  282%!  walk_clauses(+Clauses, +OTerm) is det.
  283%
  284%   Walk the given clauses.
  285
  286walk_clauses(Clauses, OTerm) :-
  287    must_be(list, Clauses),
  288    forall(member(ClauseRef, Clauses),
  289           ( user:clause(CHead, Body, ClauseRef),
  290             (   CHead = Module:Head
  291             ->  true
  292             ;   Module = user,
  293                 Head = CHead
  294             ),
  295             walk_option_clause(OTerm, ClauseRef),
  296             walk_option_caller(OTerm, Module:Head),
  297             walk_called_by_body(Body, Module, OTerm)
  298           )).
  299
  300%!  scan_module(+Module, +OTerm) is semidet.
  301%
  302%   True if we must scan Module according to OTerm.
  303
  304scan_module(M, OTerm) :-
  305    walk_option_module(OTerm, M1),
  306    nonvar(M1),
  307    !,
  308    \+ M \= M1.
  309scan_module(M, OTerm) :-
  310    walk_option_module_class(OTerm, Classes),
  311    module_property(M, class(Class)),
  312    memberchk(Class, Classes),
  313    !.
  314
  315%!  walk_from_initialization(+OTerm)
  316%
  317%   Find initialization/1,2 directives and  process   what  they are
  318%   calling.  Skip
  319%
  320%   @bug    Relies on private '$init_goal'/3 database.
  321
  322walk_from_initialization(OTerm) :-
  323    walk_option_caller(OTerm, '<initialization>'),
  324    forall(init_goal_in_scope(Goal, SourceLocation, OTerm),
  325           ( walk_option_initialization(OTerm, SourceLocation),
  326             walk_from_initialization(Goal, OTerm))).
  327
  328init_goal_in_scope(Goal, SourceLocation, OTerm) :-
  329    '$init_goal'(_When, Goal, SourceLocation),
  330    SourceLocation = File:_Line,
  331    (   walk_option_module(OTerm, M),
  332        nonvar(M)
  333    ->  module_property(M, file(File))
  334    ;   walk_option_module_class(OTerm, Classes),
  335        source_file_property(File, module(MF))
  336    ->  module_property(MF, class(Class)),
  337        memberchk(Class, Classes),
  338        walk_option_module(OTerm, MF)
  339    ;   true
  340    ).
  341
  342walk_from_initialization(M:Goal, OTerm) :-
  343    scan_module(M, OTerm),
  344    !,
  345    walk_called_by_body(Goal, M, OTerm).
  346walk_from_initialization(_, _).
  347
  348
  349%!  find_walk_from_module(+Module, +OTerm) is det.
  350%
  351%   Find undefined calls from the bodies  of all clauses that belong
  352%   to Module.
  353
  354find_walk_from_module(M, OTerm) :-
  355    debug(autoload, 'Analysing module ~q', [M]),
  356    walk_option_module(OTerm, M),
  357    forall(predicate_in_module(M, PI),
  358           walk_called_by_pred(M:PI, OTerm)).
  359
  360walk_called_by_pred(Module:Name/Arity, _) :-
  361    multifile_predicate(Name, Arity, Module),
  362    !.
  363walk_called_by_pred(Module:Name/Arity, _) :-
  364    functor(Head, Name, Arity),
  365    predicate_property(Module:Head, multifile),
  366    !,
  367    assertz(multifile_predicate(Name, Arity, Module)).
  368walk_called_by_pred(Module:Name/Arity, OTerm) :-
  369    functor(Head, Name, Arity),
  370    (   no_walk_property(Property),
  371        predicate_property(Module:Head, Property)
  372    ->  true
  373    ;   walk_option_caller(OTerm, Module:Head),
  374        walk_option_clause(OTerm, ClauseRef),
  375        forall(catch(clause(Module:Head, Body, ClauseRef), _, fail),
  376               walk_called_by_body(Body, Module, OTerm))
  377    ).
  378
  379no_walk_property(number_of_rules(0)).   % no point walking only facts
  380no_walk_property(foreign).              % cannot walk foreign code
  381
  382%!  walk_from_multifile(+OTerm)
  383%
  384%   Process registered multifile predicates.
  385
  386walk_from_multifile(OTerm) :-
  387    forall(retract(multifile_predicate(Name, Arity, Module)),
  388           walk_called_by_multifile(Module:Name/Arity, OTerm)).
  389
  390walk_called_by_multifile(Module:Name/Arity, OTerm) :-
  391    functor(Head, Name, Arity),
  392    forall(catch(clause_not_from_development(
  393                     Module:Head, Body, ClauseRef, OTerm),
  394                 _, fail),
  395           ( walk_option_clause(OTerm, ClauseRef),
  396             walk_option_caller(OTerm, Module:Head),
  397             walk_called_by_body(Body, Module, OTerm)
  398           )).
  399
  400
  401%!  clause_not_from_development(:Head, -Body, ?Ref, +Options) is nondet.
  402%
  403%   Enumerate clauses for a multifile predicate, but omit those from
  404%   a module that is specifically meant to support development.
  405
  406clause_not_from_development(Module:Head, Body, Ref, OTerm) :-
  407    clause(Module:Head, Body, Ref),
  408    \+ ( clause_property(Ref, file(File)),
  409         module_property(LoadModule, file(File)),
  410         \+ scan_module(LoadModule, OTerm)
  411       ).
  412
  413%!  walk_called_by_body(+Body, +Module, +OTerm) is det.
  414%
  415%   Check the Body term when  executed   in  the  context of Module.
  416%   Options:
  417%
  418%     - undefined(+Action)
  419%     One of =ignore=, =error=
  420
  421walk_called_by_body(True, _, _) :-
  422    True == true,
  423    !.                % quickly deal with facts
  424walk_called_by_body(Body, Module, OTerm) :-
  425    set_undecided_of_walk_option(error, OTerm, OTerm1),
  426    set_evaluate_of_walk_option(false, OTerm1, OTerm2),
  427    catch(walk_called(Body, Module, _TermPos, OTerm2),
  428          missing(Missing),
  429          walk_called_by_body(Missing, Body, Module, OTerm)),
  430    !.
  431walk_called_by_body(Body, Module, OTerm) :-
  432    format(user_error, 'Failed to analyse:~n', []),
  433    portray_clause(('<head>' :- Body)),
  434    debug_walk(Body, Module, OTerm).
  435
  436% recompile this library after `debug(codewalk(trace))` and re-try
  437% for debugging failures.
  438:- if(debugging(codewalk(trace))).  439debug_walk(Body, Module, OTerm) :-
  440    gtrace,
  441    walk_called_by_body(Body, Module, OTerm).
  442:- else.  443debug_walk(_,_,_).
  444:- endif.  445
  446%!  walk_called_by_body(+Missing, +Body, +Module, +OTerm)
  447%
  448%   Restart the analysis because  the   previous  analysis  provided
  449%   insufficient information.
  450
  451walk_called_by_body(Missing, Body, _, OTerm) :-
  452    debugging(codewalk),
  453    format(user_error, 'Retrying due to ~w (~p)~n', [Missing, OTerm]),
  454    portray_clause(('<head>' :- Body)), fail.
  455walk_called_by_body(undecided_call, Body, Module, OTerm) :-
  456    catch(forall(walk_called(Body, Module, _TermPos, OTerm),
  457                 true),
  458          missing(Missing),
  459          walk_called_by_body(Missing, Body, Module, OTerm)).
  460walk_called_by_body(subterm_positions, Body, Module, OTerm) :-
  461    (   (   walk_option_clause(OTerm, ClauseRef), nonvar(ClauseRef),
  462            clause_info(ClauseRef, _, TermPos, _NameOffset),
  463            TermPos = term_position(_,_,_,_,[_,BodyPos])
  464        ->  WBody = Body
  465        ;   walk_option_initialization(OTerm, SrcLoc),
  466            ground(SrcLoc), SrcLoc = _File:_Line,
  467            initialization_layout(SrcLoc, Module:Body, WBody, BodyPos)
  468        )
  469    ->  catch(forall(walk_called(WBody, Module, BodyPos, OTerm),
  470                     true),
  471              missing(subterm_positions),
  472              walk_called_by_body(no_positions, Body, Module, OTerm))
  473    ;   set_source_of_walk_option(false, OTerm, OTerm2),
  474        forall(walk_called(Body, Module, _BodyPos, OTerm2),
  475               true)
  476    ).
  477walk_called_by_body(no_positions, Body, Module, OTerm) :-
  478    set_source_of_walk_option(false, OTerm, OTerm2),
  479    forall(walk_called(Body, Module, _NoPos, OTerm2),
  480           true).
  481
  482
  483%!  walk_called(+Goal, +Module, +TermPos, +OTerm) is multi.
  484%
  485%   Perform abstract interpretation of Goal,  touching all sub-goals
  486%   that  are  directly  called  or  immediately  reachable  through
  487%   meta-calls.  The  actual  auto-loading  is    performed  by  the
  488%   predicate_property/2 call for meta-predicates.
  489%
  490%   If  Goal  is  disjunctive,  walk_called   succeeds  with  a
  491%   choice-point.  Backtracking  analyses  the  alternative  control
  492%   path(s).
  493%
  494%   Options:
  495%
  496%     * undecided(+Action)
  497%     How to deal with insifficiently instantiated terms in the
  498%     call-tree.  Values are:
  499%
  500%       - ignore
  501%       Silently ignore such goals
  502%       - error
  503%       Throw =undecided_call=
  504%
  505%     * evaluate(+Boolean)
  506%     If =true= (default), evaluate some goals.  Notably =/2.
  507%
  508%   @tbd    Analyse e.g. assert((Head:-Body))?
  509
  510walk_called(Term, Module, parentheses_term_position(_,_,Pos), OTerm) :-
  511    nonvar(Pos),
  512    !,
  513    walk_called(Term, Module, Pos, OTerm).
  514walk_called(Var, _, TermPos, OTerm) :-
  515    var(Var),                              % Incomplete analysis
  516    !,
  517    undecided(Var, TermPos, OTerm).
  518walk_called(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :-
  519    !,
  520    (   nonvar(M)
  521    ->  walk_called(G, M, Pos, OTerm)
  522    ;   undecided(M, MPos, OTerm)
  523    ).
  524walk_called((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  525    !,
  526    walk_called(A, M, PA, OTerm),
  527    walk_called(B, M, PB, OTerm).
  528walk_called((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  529    !,
  530    walk_called(A, M, PA, OTerm),
  531    walk_called(B, M, PB, OTerm).
  532walk_called((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  533    !,
  534    walk_called(A, M, PA, OTerm),
  535    walk_called(B, M, PB, OTerm).
  536walk_called(\+(A), M, term_position(_,_,_,_,[PA]), OTerm) :-
  537    !,
  538    \+ \+ walk_called(A, M, PA, OTerm).
  539walk_called((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  540    !,
  541    (   walk_option_evaluate(OTerm, Eval), Eval == true
  542    ->  Goal = (A;B),
  543        setof(Goal,
  544              (   walk_called(A, M, PA, OTerm)
  545              ;   walk_called(B, M, PB, OTerm)
  546              ),
  547              Alts0),
  548        variants(Alts0, Alts),
  549        member(Goal, Alts)
  550    ;   \+ \+ walk_called(A, M, PA, OTerm), % do not propagate bindings
  551        \+ \+ walk_called(B, M, PB, OTerm)
  552    ).
  553walk_called(Goal, Module, TermPos, OTerm) :-
  554    walk_option_trace_reference(OTerm, To), To \== (-),
  555    (   subsumes_term(To, Module:Goal)
  556    ->  M2 = Module
  557    ;   predicate_property(Module:Goal, imported_from(M2)),
  558        subsumes_term(To, M2:Goal)
  559    ),
  560    trace_condition(M2:Goal, TermPos, OTerm),
  561    print_reference(M2:Goal, TermPos, trace, OTerm),
  562    fail.                                   % Continue search
  563walk_called(Goal, Module, _, OTerm) :-
  564    evaluate(Goal, Module, OTerm),
  565    !.
  566walk_called(Goal, M, TermPos, OTerm) :-
  567    (   (   predicate_property(M:Goal, imported_from(IM))
  568        ->  true
  569        ;   IM = M
  570        ),
  571        prolog:called_by(Goal, IM, M, Called)
  572    ;   prolog:called_by(Goal, Called)
  573    ),
  574    Called \== [],
  575    !,
  576    walk_called_by(Called, M, Goal, TermPos, OTerm).
  577walk_called(Meta, M, term_position(_,E,_,_,ArgPosList), OTerm) :-
  578    walk_option_walk_meta_predicates(OTerm, true),
  579    (   walk_option_autoload(OTerm, false)
  580    ->  nonvar(M),
  581        '$get_predicate_attribute'(M:Meta, defined, 1)
  582    ;   true
  583    ),
  584    (   predicate_property(M:Meta, meta_predicate(Head))
  585    ;   inferred_meta_predicate(M:Meta, Head)
  586    ),
  587    !,
  588    walk_option_clause(OTerm, ClauseRef),
  589    register_possible_meta_clause(ClauseRef),
  590    walk_meta_call(1, Head, Meta, M, ArgPosList, E-E, OTerm).
  591walk_called(Closure, _, _, _) :-
  592    blob(Closure, closure),
  593    !,
  594    '$closure_predicate'(Closure, Module:Name/Arity),
  595    functor(Head, Name, Arity),
  596    '$get_predicate_attribute'(Module:Head, defined, 1).
  597walk_called(ClosureCall, _, _, _) :-
  598    compound(ClosureCall),
  599    compound_name_arity(ClosureCall, Closure, _),
  600    blob(Closure, closure),
  601    !,
  602    '$closure_predicate'(Closure, Module:Name/Arity),
  603    functor(Head, Name, Arity),
  604    '$get_predicate_attribute'(Module:Head, defined, 1).
  605walk_called(Goal, Module, _, _) :-
  606    nonvar(Module),
  607    '$get_predicate_attribute'(Module:Goal, defined, 1),
  608    !.
  609walk_called(Goal, Module, TermPos, OTerm) :-
  610    callable(Goal),
  611    !,
  612    undefined(Module:Goal, TermPos, OTerm).
  613walk_called(Goal, _Module, TermPos, OTerm) :-
  614    not_callable(Goal, TermPos, OTerm).
  615
  616%!  trace_condition(:Callee, +TermPos, +OTerm) is semidet.
  617%
  618%   Call call(Condition, Callee, Dict)
  619
  620trace_condition(Callee, TermPos, OTerm) :-
  621    walk_option_trace_condition(OTerm, Cond), nonvar(Cond),
  622    !,
  623    cond_location_context(OTerm, TermPos, Context0),
  624    walk_option_caller(OTerm, Caller),
  625    walk_option_module(OTerm, Module),
  626    put_dict(#{caller:Caller, module:Module}, Context0, Context),
  627    call(Cond, Callee, Context).
  628trace_condition(_, _, _).
  629
  630cond_location_context(OTerm, _TermPos, Context) :-
  631    walk_option_clause(OTerm, Clause), nonvar(Clause),
  632    !,
  633    Context = #{clause:Clause}.
  634cond_location_context(OTerm, _TermPos, Context) :-
  635    walk_option_initialization(OTerm, Init), nonvar(Init),
  636    !,
  637    Context = #{initialization:Init}.
  638
  639%!  undecided(+Variable, +TermPos, +OTerm)
  640
  641undecided(Var, TermPos, OTerm) :-
  642    walk_option_undecided(OTerm, Undecided),
  643    (   var(Undecided)
  644    ->  Action = ignore
  645    ;   Action = Undecided
  646    ),
  647    undecided(Action, Var, TermPos, OTerm).
  648
  649undecided(ignore, _, _, _) :- !.
  650undecided(error,  _, _, _) :-
  651    throw(missing(undecided_call)).
  652
  653%!  evaluate(Goal, Module, OTerm) is nondet.
  654
  655evaluate(Goal, Module, OTerm) :-
  656    walk_option_evaluate(OTerm, Evaluate),
  657    Evaluate \== false,
  658    evaluate(Goal, Module).
  659
  660evaluate(A=B, _) :-
  661    unify_with_occurs_check(A, B).
  662
  663%!  undefined(:Goal, +TermPos, +OTerm)
  664%
  665%   The analysis trapped a definitely undefined predicate.
  666
  667undefined(_, _, OTerm) :-
  668    walk_option_undefined(OTerm, ignore),
  669    !.
  670undefined(Goal, _, _) :-
  671    predicate_property(Goal, autoload(_)),
  672    !.
  673undefined(Goal, TermPos, OTerm) :-
  674    (   walk_option_undefined(OTerm, trace)
  675    ->  Why = trace
  676    ;   Why = undefined
  677    ),
  678    print_reference(Goal, TermPos, Why, OTerm).
  679
  680%!  not_callable(+Goal, +TermPos, +OTerm)
  681%
  682%   We found a reference to a non-callable term
  683
  684not_callable(Goal, TermPos, OTerm) :-
  685    print_reference(Goal, TermPos, not_callable, OTerm).
  686
  687
  688%!  print_reference(+Goal, +TermPos, +Why, +OTerm)
  689%
  690%   Print a reference to Goal, found at TermPos.
  691%
  692%   @arg Why is one of `trace` or `undefined`
  693
  694print_reference(Goal, TermPos, Why, OTerm) :-
  695    walk_option_clause(OTerm, Clause), nonvar(Clause),
  696    !,
  697    (   compound(TermPos),
  698        arg(1, TermPos, CharCount),
  699        integer(CharCount)          % test it is valid
  700    ->  From = clause_term_position(Clause, TermPos)
  701    ;   walk_option_source(OTerm, false)
  702    ->  From = clause(Clause)
  703    ;   From = _,
  704        throw(missing(subterm_positions))
  705    ),
  706    print_reference2(Goal, From, Why, OTerm).
  707print_reference(Goal, TermPos, Why, OTerm) :-
  708    walk_option_initialization(OTerm, Init), nonvar(Init),
  709    Init = File:Line,
  710    !,
  711    (   compound(TermPos),
  712        arg(1, TermPos, CharCount),
  713        integer(CharCount)          % test it is valid
  714    ->  From = file_term_position(File, TermPos)
  715    ;   walk_option_source(OTerm, false)
  716    ->  From = file(File, Line, -1, _)
  717    ;   From = _,
  718        throw(missing(subterm_positions))
  719    ),
  720    print_reference2(Goal, From, Why, OTerm).
  721print_reference(Goal, _, Why, OTerm) :-
  722    print_reference2(Goal, _, Why, OTerm).
  723
  724print_reference2(Goal, From, trace, OTerm) :-
  725    walk_option_on_trace(OTerm, Closure),
  726    nonvar(Closure),
  727    walk_option_caller(OTerm, Caller),
  728    call(Closure, Goal, Caller, From),
  729    !.
  730print_reference2(Goal, From, trace, OTerm) :-
  731    walk_option_on_edge(OTerm, Closure),
  732    nonvar(Closure),
  733    walk_option_caller(OTerm, Caller),
  734    translate_location(From, Dict),
  735    call(Closure, Goal, Caller, Dict),
  736    !.
  737print_reference2(Goal, From, Why, _OTerm) :-
  738    make_message(Why, Goal, From, Message, Level),
  739    print_message(Level, Message).
  740
  741
  742make_message(undefined, Goal, Context,
  743             error(existence_error(procedure, PI), Context), error) :-
  744    goal_pi(Goal, PI).
  745make_message(not_callable, Goal, Context,
  746             error(type_error(callable, Goal), Context), error).
  747make_message(trace, Goal, Context,
  748             trace_call_to(PI, Context), informational) :-
  749    goal_pi(Goal, PI).
  750
  751
  752goal_pi(Goal, M:Name/Arity) :-
  753    strip_module(Goal, M, Head),
  754    callable(Head),
  755    !,
  756    functor(Head, Name, Arity).
  757goal_pi(Goal, Goal).
  758
  759:- dynamic
  760    possible_meta_predicate/2.  761
  762%!  register_possible_meta_clause(+ClauseRef) is det.
  763%
  764%   ClausesRef contains as call  to   a  meta-predicate. Remember to
  765%   analyse this predicate. We only analyse   the predicate if it is
  766%   loaded from a user module. I.e.,  system and library modules are
  767%   trusted.
  768
  769register_possible_meta_clause(ClausesRef) :-
  770    nonvar(ClausesRef),
  771    clause_property(ClausesRef, predicate(PI)),
  772    pi_head(PI, Head, Module),
  773    module_property(Module, class(user)),
  774    \+ predicate_property(Module:Head, meta_predicate(_)),
  775    \+ inferred_meta_predicate(Module:Head, _),
  776    \+ possible_meta_predicate(Head, Module),
  777    !,
  778    assertz(possible_meta_predicate(Head, Module)).
  779register_possible_meta_clause(_).
  780
  781pi_head(Module:Name/Arity, Head, Module)  :-
  782    !,
  783    functor(Head, Name, Arity).
  784pi_head(_, _, _) :-
  785    assertion(fail).
  786
  787%!  infer_new_meta_predicates(-MetaSpecs, +OTerm) is det.
  788
  789infer_new_meta_predicates([], OTerm) :-
  790    walk_option_infer_meta_predicates(OTerm, false),
  791    !.
  792infer_new_meta_predicates(MetaSpecs, OTerm) :-
  793    findall(Module:MetaSpec,
  794            ( retract(possible_meta_predicate(Head, Module)),
  795              infer_meta_predicate(Module:Head, MetaSpec),
  796              (   walk_option_infer_meta_predicates(OTerm, all)
  797              ->  true
  798              ;   calling_metaspec(MetaSpec)
  799              )
  800            ),
  801            MetaSpecs).
  802
  803%!  calling_metaspec(+Head) is semidet.
  804%
  805%   True if this is a meta-specification  that makes a difference to
  806%   the code walker.
  807
  808calling_metaspec(Head) :-
  809    arg(_, Head, Arg),
  810    calling_metaarg(Arg),
  811    !.
  812
  813calling_metaarg(I) :- integer(I), !.
  814calling_metaarg(^).
  815calling_metaarg(//).
  816
  817
  818%!  walk_meta_call(+Index, +GoalHead, +MetaHead, +Module,
  819%!                 +ArgPosList, +EndPos, +OTerm)
  820%
  821%   Walk a call to a meta-predicate.   This walks all meta-arguments
  822%   labeled with an integer, ^ or //.
  823%
  824%   @arg    EndPos reflects the end of the term.  This is used if the
  825%           number of arguments in the compiled form exceeds the
  826%           number of arguments in the term read.
  827
  828walk_meta_call(I, Head, Meta, M, ArgPosList, EPos, OTerm) :-
  829    arg(I, Head, AS),
  830    !,
  831    (   ArgPosList = [ArgPos|ArgPosTail]
  832    ->  true
  833    ;   ArgPos = EPos,
  834        ArgPosTail = []
  835    ),
  836    (   integer(AS)
  837    ->  arg(I, Meta, MA),
  838        extend(MA, AS, Goal, ArgPos, ArgPosEx, OTerm),
  839        walk_called(Goal, M, ArgPosEx, OTerm)
  840    ;   AS == (^)
  841    ->  arg(I, Meta, MA),
  842        remove_quantifier(MA, Goal, ArgPos, ArgPosEx, M, MG, OTerm),
  843        walk_called(Goal, MG, ArgPosEx, OTerm)
  844    ;   AS == (//)
  845    ->  arg(I, Meta, DCG),
  846        walk_dcg_body(DCG, M, ArgPos, OTerm)
  847    ;   true
  848    ),
  849    succ(I, I2),
  850    walk_meta_call(I2, Head, Meta, M, ArgPosTail, EPos, OTerm).
  851walk_meta_call(_, _, _, _, _, _, _).
  852
  853remove_quantifier(Goal, _, TermPos, TermPos, M, M, OTerm) :-
  854    var(Goal),
  855    !,
  856    undecided(Goal, TermPos, OTerm).
  857remove_quantifier(_^Goal0, Goal,
  858                  term_position(_,_,_,_,[_,GPos]),
  859                  TermPos, M0, M, OTerm) :-
  860    !,
  861    remove_quantifier(Goal0, Goal, GPos, TermPos, M0, M, OTerm).
  862remove_quantifier(M1:Goal0, Goal,
  863                  term_position(_,_,_,_,[_,GPos]),
  864                  TermPos, _, M, OTerm) :-
  865    !,
  866    remove_quantifier(Goal0, Goal, GPos, TermPos, M1, M, OTerm).
  867remove_quantifier(Goal, Goal, TermPos, TermPos, M, M, _).
  868
  869
  870%!  walk_called_by(+Called:list, +Module, +Goal, +TermPos, +OTerm)
  871%
  872%   Walk code explicitly mentioned to  be   called  through the hook
  873%   prolog:called_by/2.
  874
  875walk_called_by([], _, _, _, _).
  876walk_called_by([H|T], M, Goal, TermPos, OTerm) :-
  877    (   H = G0+N
  878    ->  subterm_pos(G0, M, Goal, TermPos, G, GPos),
  879        (   extend(G, N, G2, GPos, GPosEx, OTerm)
  880        ->  walk_called(G2, M, GPosEx, OTerm)
  881        ;   true
  882        )
  883    ;   subterm_pos(H, M, Goal, TermPos, G, GPos),
  884        walk_called(G, M, GPos, OTerm)
  885    ),
  886    walk_called_by(T, M, Goal, TermPos, OTerm).
  887
  888subterm_pos(Sub, _, Term, TermPos, Sub, SubTermPos) :-
  889    subterm_pos(Sub, Term, TermPos, SubTermPos),
  890    !.
  891subterm_pos(Sub, M, Term, TermPos, G, SubTermPos) :-
  892    nonvar(Sub),
  893    Sub = M:H,
  894    !,
  895    subterm_pos(H, M, Term, TermPos, G, SubTermPos).
  896subterm_pos(Sub, _, _, _, Sub, _).
  897
  898subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  899    subterm_pos(Sub, Term, same_term, TermPos, SubTermPos),
  900    !.
  901subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  902    subterm_pos(Sub, Term, ==, TermPos, SubTermPos),
  903    !.
  904subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  905    subterm_pos(Sub, Term, =@=, TermPos, SubTermPos),
  906    !.
  907subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  908    subterm_pos(Sub, Term, subsumes_term, TermPos, SubTermPos),
  909    !.
  910
  911%!  walk_dcg_body(+Body, +Module, +TermPos, +OTerm)
  912%
  913%   Walk a DCG body that is meta-called.
  914
  915walk_dcg_body(Var, _Module, TermPos, OTerm) :-
  916    var(Var),
  917    !,
  918    undecided(Var, TermPos, OTerm).
  919walk_dcg_body([], _Module, _, _) :- !.
  920walk_dcg_body([_|_], _Module, _, _) :- !.
  921walk_dcg_body(String, _Module, _, _) :-
  922    string(String),
  923    !.
  924walk_dcg_body(!, _Module, _, _) :- !.
  925walk_dcg_body(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :-
  926    !,
  927    (   nonvar(M)
  928    ->  walk_dcg_body(G, M, Pos, OTerm)
  929    ;   undecided(M, MPos, OTerm)
  930    ).
  931walk_dcg_body((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  932    !,
  933    walk_dcg_body(A, M, PA, OTerm),
  934    walk_dcg_body(B, M, PB, OTerm).
  935walk_dcg_body((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  936    !,
  937    walk_dcg_body(A, M, PA, OTerm),
  938    walk_dcg_body(B, M, PB, OTerm).
  939walk_dcg_body((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  940    !,
  941    walk_dcg_body(A, M, PA, OTerm),
  942    walk_dcg_body(B, M, PB, OTerm).
  943walk_dcg_body((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  944    !,
  945    (   walk_dcg_body(A, M, PA, OTerm)
  946    ;   walk_dcg_body(B, M, PB, OTerm)
  947    ).
  948walk_dcg_body((A|B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  949    !,
  950    (   walk_dcg_body(A, M, PA, OTerm)
  951    ;   walk_dcg_body(B, M, PB, OTerm)
  952    ).
  953walk_dcg_body({G}, M, brace_term_position(_,_,PG), OTerm) :-
  954    !,
  955    walk_called(G, M, PG, OTerm).
  956walk_dcg_body(G, M, TermPos, OTerm) :-
  957    extend(G, 2, G2, TermPos, TermPosEx, OTerm),
  958    walk_called(G2, M, TermPosEx, OTerm).
  959
  960
  961%!  subterm_pos(+SubTerm, +Term, :Cmp,
  962%!              +TermPosition, -SubTermPos) is nondet.
  963%
  964%   True when SubTerm is a sub  term   of  Term, compared using Cmp,
  965%   TermPosition describes the term layout   of  Term and SubTermPos
  966%   describes the term layout of SubTerm.   Cmp  is typically one of
  967%   =same_term=, =|==|=, =|=@=|= or =|subsumes_term|=
  968
  969:- meta_predicate
  970    subterm_pos(+, +, 2, +, -),
  971    sublist_pos(+, +, +, +, 2, -).  972:- public
  973    subterm_pos/5.                      % used in library(check).
  974
  975subterm_pos(_, _, _, Pos, _) :-
  976    var(Pos), !, fail.
  977subterm_pos(Sub, Term, Cmp, Pos, Pos) :-
  978    call(Cmp, Sub, Term),
  979    !.
  980subterm_pos(Sub, Term, Cmp, term_position(_,_,_,_,ArgPosList), Pos) :-
  981    is_list(ArgPosList),
  982    compound(Term),
  983    nth1(I, ArgPosList, ArgPos),
  984    arg(I, Term, Arg),
  985    subterm_pos(Sub, Arg, Cmp, ArgPos, Pos).
  986subterm_pos(Sub, Term, Cmp, list_position(_,_,ElemPosList,TailPos), Pos) :-
  987    sublist_pos(ElemPosList, TailPos, Sub, Term, Cmp, Pos).
  988subterm_pos(Sub, {Arg}, Cmp, brace_term_position(_,_,ArgPos), Pos) :-
  989    subterm_pos(Sub, Arg, Cmp, ArgPos, Pos).
  990
  991sublist_pos([EP|TP], TailPos, Sub, [H|T], Cmp, Pos) :-
  992    (   subterm_pos(Sub, H, Cmp, EP, Pos)
  993    ;   sublist_pos(TP, TailPos, Sub, T, Cmp, Pos)
  994    ).
  995sublist_pos([], TailPos, Sub, Tail, Cmp, Pos) :-
  996    TailPos \== none,
  997    subterm_pos(Sub, Tail, Cmp, TailPos, Pos).
  998
  999%!  extend(+Goal, +ExtraArgs, +TermPosIn, -TermPosOut, +OTerm)
 1000%
 1001%   @bug:
 1002
 1003extend(Goal, 0, Goal, TermPos, TermPos, _) :- !.
 1004extend(Goal, _, _, TermPos, TermPos, OTerm) :-
 1005    var(Goal),
 1006    !,
 1007    undecided(Goal, TermPos, OTerm).
 1008extend(M:Goal, N, M:GoalEx,
 1009       term_position(F,T,FT,TT,[MPos,GPosIn]),
 1010       term_position(F,T,FT,TT,[MPos,GPosOut]), OTerm) :-
 1011    !,
 1012    (   var(M)
 1013    ->  undecided(N, MPos, OTerm)
 1014    ;   true
 1015    ),
 1016    extend(Goal, N, GoalEx, GPosIn, GPosOut, OTerm).
 1017extend(Goal, N, GoalEx, TermPosIn, TermPosOut, _) :-
 1018    callable(Goal),
 1019    !,
 1020    Goal =.. List,
 1021    length(Extra, N),
 1022    extend_term_pos(TermPosIn, N, TermPosOut),
 1023    append(List, Extra, ListEx),
 1024    GoalEx =.. ListEx.
 1025extend(Closure, N, M:GoalEx, TermPosIn, TermPosOut, OTerm) :-
 1026    blob(Closure, closure),             % call(Closure, A1, ...)
 1027    !,
 1028    '$closure_predicate'(Closure, M:Name/Arity),
 1029    length(Extra, N),
 1030    extend_term_pos(TermPosIn, N, TermPosOut),
 1031    GoalEx =.. [Name|Extra],
 1032    (   N =:= Arity
 1033    ->  true
 1034    ;   print_reference(Closure, TermPosIn, closure_arity_mismatch, OTerm)
 1035    ).
 1036extend(Goal, _, _, TermPos, _, OTerm) :-
 1037    print_reference(Goal, TermPos, not_callable, OTerm).
 1038
 1039extend_term_pos(Var, _, _) :-
 1040    var(Var),
 1041    !.
 1042extend_term_pos(term_position(F,T,FT,TT,ArgPosIn),
 1043                N,
 1044                term_position(F,T,FT,TT,ArgPosOut)) :-
 1045    !,
 1046    length(Extra, N),
 1047    maplist(=(0-0), Extra),
 1048    append(ArgPosIn, Extra, ArgPosOut).
 1049extend_term_pos(F-T, N, term_position(F,T,F,T,Extra)) :-
 1050    length(Extra, N),
 1051    maplist(=(0-0), Extra).
 1052
 1053
 1054%!  variants(+SortedList, -Variants) is det.
 1055
 1056variants([], []).
 1057variants([H|T], List) :-
 1058    variants(T, H, List).
 1059
 1060variants([], H, [H]).
 1061variants([H|T], V, List) :-
 1062    (   H =@= V
 1063    ->  variants(T, V, List)
 1064    ;   List = [V|List2],
 1065        variants(T, H, List2)
 1066    ).
 1067
 1068%!  predicate_in_module(+Module, ?PI) is nondet.
 1069%
 1070%   True if PI is a predicate locally defined in Module.
 1071
 1072predicate_in_module(Module, PI) :-
 1073    current_predicate(Module:PI),
 1074    PI = Name/Arity,
 1075    \+ hidden_predicate(Name, Arity),
 1076    functor(Head, Name, Arity),
 1077    \+ predicate_property(Module:Head, imported_from(_)).
 1078
 1079
 1080hidden_predicate(Name, _) :-
 1081    atom(Name),                         % []/N is not hidden
 1082    sub_atom(Name, 0, _, _, '$wrap$').
 1083
 1084
 1085                 /*******************************
 1086                 *      ENUMERATE CLAUSES       *
 1087                 *******************************/
 1088
 1089%!  prolog_program_clause(-ClauseRef, +Options) is nondet.
 1090%
 1091%   True when ClauseRef is a reference   for  clause in the program.
 1092%   Options   is   a   subset   of    the   options   processed   by
 1093%   prolog_walk_code/1. The logic for deciding   on which clauses to
 1094%   enumerate is shared with prolog_walk_code/1.
 1095%
 1096%     * module(?Module)
 1097%     * module_class(+list(Classes))
 1098
 1099prolog_program_clause(ClauseRef, Options) :-
 1100    make_walk_option(Options, OTerm, _),
 1101    setup_call_cleanup(
 1102        true,
 1103        (   current_module(Module),
 1104            scan_module(Module, OTerm),
 1105            module_clause(Module, ClauseRef, OTerm)
 1106        ;   retract(multifile_predicate(Name, Arity, MM)),
 1107            multifile_clause(ClauseRef, MM:Name/Arity, OTerm)
 1108        ;   initialization_clause(ClauseRef, OTerm)
 1109        ),
 1110        retractall(multifile_predicate(_,_,_))).
 1111
 1112
 1113module_clause(Module, ClauseRef, _OTerm) :-
 1114    predicate_in_module(Module, Name/Arity),
 1115    \+ multifile_predicate(Name, Arity, Module),
 1116    functor(Head, Name, Arity),
 1117    (   predicate_property(Module:Head, multifile)
 1118    ->  assertz(multifile_predicate(Name, Arity, Module)),
 1119        fail
 1120    ;   predicate_property(Module:Head, Property),
 1121        no_enum_property(Property)
 1122    ->  fail
 1123    ;   catch(nth_clause(Module:Head, _, ClauseRef), _, fail)
 1124    ).
 1125
 1126no_enum_property(foreign).
 1127
 1128multifile_clause(ClauseRef, M:Name/Arity, OTerm) :-
 1129    functor(Head, Name, Arity),
 1130    catch(clauseref_not_from_development(M:Head, ClauseRef, OTerm),
 1131          _, fail).
 1132
 1133clauseref_not_from_development(Module:Head, Ref, OTerm) :-
 1134    nth_clause(Module:Head, _N, Ref),
 1135    \+ ( clause_property(Ref, file(File)),
 1136         module_property(LoadModule, file(File)),
 1137         \+ scan_module(LoadModule, OTerm)
 1138       ).
 1139
 1140initialization_clause(ClauseRef, OTerm) :-
 1141    catch(clause(system:'$init_goal'(_File, M:_Goal, SourceLocation),
 1142                 true, ClauseRef),
 1143          _, fail),
 1144    walk_option_initialization(OTerm, SourceLocation),
 1145    scan_module(M, OTerm).
 1146
 1147
 1148%!  translate_location(+Loc, -Dict) is det.
 1149
 1150translate_location(clause_term_position(ClauseRef, TermPos), Dict),
 1151    clause_property(ClauseRef, file(File)) =>
 1152    arg(1, TermPos, CharCount),
 1153    filepos_line(File, CharCount, Line, LinePos),
 1154    Dict = _{ clause: ClauseRef,
 1155              file: File,
 1156              character_count: CharCount,
 1157              line_count: Line,
 1158              line_position: LinePos
 1159            }.
 1160translate_location(clause(ClauseRef), Dict),
 1161    clause_property(ClauseRef, file(File)),
 1162    clause_property(ClauseRef, line_count(Line)) =>
 1163    Dict = _{ clause: ClauseRef,
 1164              file: File,
 1165              line_count: Line
 1166            }.
 1167translate_location(clause(ClauseRef), Dict) =>
 1168    Dict = _{ clause: ClauseRef
 1169            }.
 1170translate_location(file_term_position(Path, TermPos), Dict) =>
 1171    arg(1, TermPos, CharCount),
 1172    filepos_line(Path, CharCount, Line, LinePos),
 1173    Dict = _{ file: Path,
 1174              character_count: CharCount,
 1175              line_count: Line,
 1176              line_position: LinePos
 1177            }.
 1178translate_location(Var, Dict), var(Var) =>
 1179    Dict = _{}.
 1180
 1181                 /*******************************
 1182                 *            MESSAGES          *
 1183                 *******************************/
 1184
 1185:- multifile
 1186    prolog:message//1,
 1187    prolog:message_location//1. 1188
 1189prolog:message(trace_call_to(PI, Context)) -->
 1190    [ 'Call to ~q at '-[PI] ],
 1191    '$messages':swi_location(Context).
 1192
 1193prolog:message_location(clause_term_position(ClauseRef, TermPos)) -->
 1194    { clause_property(ClauseRef, file(File)) },
 1195    message_location_file_term_position(File, TermPos).
 1196prolog:message_location(clause(ClauseRef)) -->
 1197    { clause_property(ClauseRef, file(File)),
 1198      clause_property(ClauseRef, line_count(Line))
 1199    },
 1200    !,
 1201    [ url(File:Line), ': ' ].
 1202prolog:message_location(clause(ClauseRef)) -->
 1203    { clause_name(ClauseRef, Name) },
 1204    [ '~w: '-[Name] ].
 1205prolog:message_location(file_term_position(Path, TermPos)) -->
 1206    message_location_file_term_position(Path, TermPos).
 1207prolog:message(codewalk(reiterate(New, Iteration, CPU))) -->
 1208    [ 'Found new meta-predicates in iteration ~w (~3f sec)'-
 1209      [Iteration, CPU], nl ],
 1210    meta_decls(New),
 1211    [ 'Restarting analysis ...'-[], nl ].
 1212
 1213meta_decls([]) --> [].
 1214meta_decls([H|T]) -->
 1215    [ ':- meta_predicate ~q.'-[H], nl ],
 1216    meta_decls(T).
 1217
 1218message_location_file_term_position(File, TermPos) -->
 1219    { arg(1, TermPos, CharCount),
 1220      filepos_line(File, CharCount, Line, LinePos)
 1221    },
 1222    [ url(File:Line:LinePos), ': ' ].
 1223
 1224%!  filepos_line(+File, +CharPos, -Line, -Column) is det.
 1225%
 1226%   @arg CharPos is 0-based character offset in the file.
 1227%   @arg Column is the current column, counting tabs as 8 spaces.
 1228
 1229filepos_line(File, CharPos, Line, LinePos) :-
 1230    setup_call_cleanup(
 1231        ( open(File, read, In),
 1232          open_null_stream(Out)
 1233        ),
 1234        ( copy_stream_data(In, Out, CharPos),
 1235          stream_property(In, position(Pos)),
 1236          stream_position_data(line_count, Pos, Line),
 1237          stream_position_data(line_position, Pos, LinePos)
 1238        ),
 1239        ( close(Out),
 1240          close(In)
 1241        ))