1/*  Part of Extended Tools for SWI-Prolog
    2
    3    Author:        Edison Mera Menendez
    4    E-mail:        efmera@gmail.com
    5    WWW:           https://github.com/edisonm/xtools
    6    Copyright (C): 2015, Process Design Center, Breda, The Netherlands.
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(check_imports, []).   36
   37:- use_module(library(apply)).   38:- use_module(library(lists)).   39:- use_module(library(checkers/checker)).   40:- use_module(library(clambda)).   41:- use_module(library(expansion_module)).   42:- use_module(library(codewalk)).   43:- use_module(library(extra_location)).   44:- use_module(library(from_utils)).   45:- use_module(library(option_utils)).   46:- use_module(library(location_utils)).   47:- use_module(library(module_files)).   48
   49:- multifile
   50    prolog:message//1.   51
   52prolog:message(acheck(imports)) -->
   53    ['Unused Imports',nl,
   54     '--------------',nl,
   55     'The predicates or modules below has been imported, however they', nl,
   56     'are never used in the importing module, or they do not implement', nl,
   57     'new clauses for multifile predicates.  Note that modules that', nl,
   58     'export operators, or that do not export any predicate are not', nl,
   59     'reported.', nl,
   60     'You can silent the warnings by declaring use_module/2 with an',nl,
   61     'empty import list. If they have desirable side effects and still', nl,
   62     'needs to be imported, you can refactorize your program so that', nl,
   63     'such side effects are not required anymore.', nl, nl].
   64prolog:message(acheck(imports, c(Class, Type, Name)-LocElemL)) -->
   65    ['~w ~w have unused ~w:'-[Class, Name, Type], nl],
   66    foldl(unused_import, LocElemL).
   67
   68unused_import(Loc/Elem) -->
   69    Loc,
   70    ['unused ~w'-[Elem], nl].
   71
   72:- dynamic
   73    used_import/1,
   74    used_usemod/2.   75
   76checker:check(imports, Result, Options) :-
   77    check_imports(Options, Result).
   78
   79check_imports(Options, Pairs) :-
   80    option_module_files(Options, MFileD),
   81    walk_code([source(false),
   82               module_files(MFileD),
   83               on_trace(collect_imports_wc)|Options]),
   84    collect_imports(MFileD, Pairs, Tail),
   85    collect_usemods(MFileD, Tail, []),
   86    cleanup_imports.
   87
   88:- public collect_imports_wc/3.   89collect_imports_wc(M:Goal, Caller, From) :-
   90    record_location_meta(M:Goal, _, From, all_call_refs, mark_import),
   91    ( nonvar(Caller),
   92      caller_module(Caller, From, MC),
   93      M \= MC,
   94      \+ used_usemod(M, MC)
   95    ->assertz(used_usemod(M, MC))
   96    ; true
   97    ).
   98
   99caller_module(M:_,                _, M) :- !.
  100caller_module('<assertion>'(M:_), _, M) :- !.
  101caller_module(_, clause(Ptr), M) :- clause_property(Ptr, module(M)).
  102
  103collect_imports(MFileD, Pairs, Tail) :-
  104    findall(warning-(c(use_module, import, U)-(Loc/(F/A))),
  105            current_unused_import(MFileD, U, Loc, F, A),
  106            Pairs, Tail).
  107
  108current_unused_import(MFileD, U, Loc, F, A) :-
  109    get_dict(M, MFileD, FileD),
  110    clause(loc_declaration(Head, M, import(U), From), _, CRef),
  111    M \= user,
  112    from_to_file(From, File),
  113    get_dict(File, FileD, _),
  114    \+ memberchk(Head, [term_expansion(_,_),
  115                        term_expansion(_,_,_,_),
  116                        goal_expansion(_,_),
  117                        goal_expansion(_,_,_,_),
  118                        except(_)
  119                       ]),
  120    \+ used_import(CRef),
  121    \+ loc_declaration(Head, M, goal, _),
  122    module_property(M, class(Class)),
  123    memberchk(Class, [user]),
  124    functor(Head, F, A),
  125    from_location(From, Loc).
  126
  127:- multifile ignore_import/2.  128
  129ignore_import(_, rtchecks_rt).
  130ignore_import(_, IM) :- is_expansion_module(IM).
  131ignore_import(_, IM) :-
  132    '$def_modules'([goal_expansion/4,
  133                    goal_expansion/2,
  134                    term_expansion/4,
  135                    term_expansion/2],
  136                   MList),
  137    member(M-PIL, MList),
  138    member(F/A, PIL),
  139    functor(H, F, A),
  140    clause(M:H, _, Ref),
  141    clause_property(Ref, file(File)),
  142    module_file(IM, File).
  143ignore_import(M, IM) :-
  144    http_dispatch:handler(_, M:H2, _, _),
  145    functor(H2, Name, A2),
  146    succ(A2, A),
  147    functor(H, Name, A),
  148    predicate_property(M:H, implementation_module(IM)).
  149
  150collect_usemods(MFileD, Pairs, Tail) :-
  151    findall(warning-(c(module, use_module, M)-(Loc/U)),
  152            ( current_used_use_module(MFileD, U, M, From),
  153              from_location(From, Loc)
  154            ), Pairs, Tail).
  155
  156current_used_use_module(MFileD, UE, M, From) :-
  157    get_dict(M, MFileD, FileD),
  158    M \= user,
  159    module_property(M, class(Class)),
  160    memberchk(Class, [user]),
  161    ( UE = use_module(U),
  162      loc_declaration(U, M, use_module, From),
  163      ExL = []
  164    ; UE = use_module(U, except(ExL)),
  165      loc_declaration(UE, M, use_module_2, From)
  166    ),
  167    from_to_file(From, File),
  168    get_dict(File, FileD, _),
  169    \+ findall(I, source_file_property(File, included_in(I, _)),
  170               [_, _|_]),
  171    absolute_file_name(U, UFile, [file_type(prolog), access(exist),
  172                                  file_errors(fail)]),
  173    module_property(UM, file(UFile)),
  174    \+ ignore_import(M, UM),
  175    module_property(UM, exports(EL)),
  176    EL \= [],
  177    subtract(EL, ExL, PIL),
  178    \+ ( module_property(UM, exported_operators(OL)),
  179         OL \= []
  180       ),
  181    \+ ( member(F/A, PIL),
  182         functor(Head, F, A),
  183         MHead = UM:Head,
  184         predicate_property(MHead, implementation_module(IM)),
  185         ( used_usemod(M, IM)                        % is used
  186         ; predicate_property(MHead, multifile),   % is extended
  187           clause(MHead, _, Ref),
  188           clause_property(Ref, file(File))
  189         )
  190       ).
  191
  192mark_import(Head, CM, _, _, _, _) :-
  193    nonvar(CM),
  194    callable(Head),
  195    predicate_property(CM:Head, implementation_module(M)),
  196    mark_import(Head, M, CM).
  197
  198mark_import(Head, M, CM) :-
  199    forall(( clause(loc_declaration(Head, CM, import(_), _), _, CRef),
  200             \+ used_import(CRef)),
  201           assertz(used_import(CRef))),
  202    ( M \= CM,
  203      \+used_usemod(CM, M)
  204    ->assertz(used_usemod(CM, M))
  205    ; true
  206    ).
  207
  208cleanup_imports :-
  209    retractall(used_import(_)),
  210    retractall(used_usemod(_, _))