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