パンダ可愛い。毎日可愛い。すごい。
C 言語の時、リファクタリングなどを通して、ある関数が使われている / いないをチェックする方法が欲しいことは時々あります。static 関数であれば、コンパイラが「誰も使っていない static 関数があるけど、いいの?」と warning を出してくれますが、static でなければ、別のファイルが利用するかもしれないので、特に何も言いません。リンク時に、visibility が default でないもの(ELF とかで、外部から見えないシンボル)なのに、参照がない場合に警告でも出してくれればいいんですが、今ちょっと見てみても、そういうオプションは無さそう。
というわけで、調べてみようと思ったんだけど、多分必要なのは、「ある関数から呼ばれた関数一覧」があれば、どの関数が参照を持っていないかわかるんじゃないか、というもの。まぁ、やれば出来るんだろうけど、面倒なので、もちろん書きたくないし、ソフトとかインストールしたくない。
Twitter で聞いてみたところ、https://searchcode.com/codesearch/view/17596782/ を教えてもらいました。要点は次の通り:
(1) nm を使うことで、「あるファイルから呼ばれる、他のファイルの関数一覧」一覧を得ることが出来る。 (2) 「あるファイルにある export された関数一覧」を得ることができる。
これを比較すれば、「export された関数一覧だが、外から誰も参照していない関数」、つまり「static でいいんじゃないの?」という提案ができる、と。そして、static 化すれば、その後でもし使っていなければ、「誰も使ってないんじゃ無いの?」ということがわかる。まぁ、その前に grep してしまえばいいのですが。
誰も使ってないけど、拡張ライブラリのために一応置いてある関数もあるので、一概に「誰も使ってない」とも言えないのだけど、まぁ grep すればいいのでヒントにはなる。
で、Perl script は動かなかったので、Ruby でちょろっと書き直した。
# # This tool is inspired by https://searchcode.com/codesearch/view/17596782/ funcs = Hash.new{|h, k| h[k] = Hash.new(0)} # [type => [name]] ARGV.each{|file| `nm #{file}`.each_line{|line| # 0000000000000190 t call_cfunc_5 if /................ (.) (.+)/ =~ line type = $1 func = $2 funcs[type][func] += 1 else raise line.dump end } } require 'pp' exported_functions = funcs['T'].keys undefined_functions = funcs['U'] include_file_data_ary = Dir.glob(File.join(__dir__, '../include/ruby/*.h')).map{|include_file| [include_file, File.read(include_file)] } ref_func = [] found_func = [] unfound_func = [] exported_functions.each{|f| if undefined_functions.has_key? f ref_func << [undefined_functions[f], f] else found = false include_file_data_ary.each{|(include_file, data)| if !data.scan("#{f}(").empty? found_func << "#{f} in #{include_file}" found = true; break end } unless found unfound_func << "static?: #{f}" end end } ref_func.sort_by{|(n, f)| -n}.each{|(n, f)| puts "#{f} is reffered from #{n} files" } puts found_func puts unfound_func
便利。最後のほう、include/ にあれば公開関数に違いない、と想像している。