kazurayam / vba-callgraph

Generating a Call Graph for Excel VBA workbooks
https://kazurayam.github.io/VBACallGraph/
Apache License 2.0
1 stars 0 forks source link

参照されるがわのProcedure名をキーとして参照する側のProcedueのVBAソースコードを正規表現でパターンマッチングする処理が精密さに欠けていて、余計なマッチを拾ってしまう #13

Closed kazurayam closed 3 months ago

kazurayam commented 3 months ago

v0.1.3の https://github.com/kazurayam/VBAProcedureUsageAnalyzer/blob/0.1.3/lib/src/test/java/com/kazurayam/vba/FindUsagesAppTest.java を実行したらpuファイルが生成された。その一部がこうなっている。

386行目あたり

...
会費納入状況チェック.Environment o-- Backboneライブラリ.KzUtil : KzCls
会費納入状況チェック.Environment o-- Backboneライブラリ.KzUtil : KzResolveExternalFilePath
会費納入状況チェック.Environment o-- Backboneライブラリ.Test_DocTransformer : Test
...

この3行のうち上の2行は妥当だ。しかし3行目の

会費納入状況チェック.Environment o-- Backboneライブラリ.Test_DocTransformer : Test

これは間違っている。この行は出力されるべきでない。

kazurayam commented 3 months ago
会費納入状況チェック.Environment o-- Backboneライブラリ.Test_DocTransformer : Test

この行が表示されてしまうのは何故か?

出発点になるのは参照される側のプロシージャーである。すなわち

package Backboneライブラリ {
  stereotype Test_DocTransformer {
    {method} Test
  } 

プロシージャ名 Test を手掛かりとして、他のVBAソースコードの中をスキャンする。Indexerクラスがそれをやる。Indexerの125行目

                VBASource moduleSource = module.getVBASource();
                // let's scan the VBASource to see if it mentions the referee
                List<VBASourceLine> linesFound = moduleSource.find(referee.getProcedureName());

VBASourceクラスのfindメソッドが文字列を走査している。

    public List<VBASourceLine> find(String pattern) {
        Pattern ptn = Pattern.compile(escapeAsRegex(pattern));
        List<VBASourceLine> linesFound = new ArrayList<>();
        cache();
        for (int i = 0; i < code.size(); i++) {
            String line = code.get(i);
            Matcher m = ptn.matcher(line);
            if (m.find()) {
                VBASourceLine vbaSourceLine = new VBASourceLine(i, line);
                vbaSourceLine.setMatcher(m);
                vbaSourceLine.setFound(true);
                linesFound.add(vbaSourceLine);
            }
        }
        return linesFound;
    }

Refereeのモジュール名をパターンとして真っ正直に操作している。

kazurayam commented 3 months ago

さて、[会費納入状況チェック.Environment]()モジュールのVBAソースコードがどうなっているだろうか。"Test"という文字が見つかるだろうか?

https://github.com/kazurayam/VBAProcedureUsageAnalyzer/blob/0.1.3/lib/src/test/fixture/hub/aogan-jimukyoku/office/exported-vba-source/%E4%BC%9A%E8%B2%BB%E7%B4%8D%E5%85%A5%E7%8A%B6%E6%B3%81%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF_R6%E5%B9%B4%E5%BA%A6/Environment.bas

ああ、ありました。"Test"が。

Sub Test_mbGetPathOfAoganCashbook()

とか

Sub Test_mbGetPathOfAoganMembers()

とか。

kazurayam commented 3 months ago

どうしよう。

少し正規表現を捻ろうか?

/Test/

ではなくて

/Test\s*(/

とすればこの例については解消できる。つまり参照されるがわのプロシージャーの名前がKzResolveExternalFilePathだとして、参照する側のコードにはきっと(が後ろにくっついているはず、と条件を付加する。具体例を挙げるならEnvironment .basの中にこう書いている。

    mbGetPathOfAoganCashbook = KzResolveExternalFilePath(ThisWorkbook, "現金出納帳ファイルのパス", "B2")
kazurayam commented 3 months ago

ところがVBA言語ではプロシージャー名の後に(が後続していなくても、プロシージャ〜の呼び出しとして正しい、ということになっている。例えばEnvironmentの中にこう書いてる

    Call KzCls

あるいは

  Call KzCls   'イミディエイトウインドウを消す

とも書く。

これを妥当なものとして受け入れることができなければならない。

kazurayam commented 3 months ago

正規表現を複雑化すれば対処できる。やってみよう。

kazurayam commented 3 months ago

v0.1.4 で対処した。

結果、こうなった。

...
Member会員名簿のためのVBAライブラリ.プロシージャ一覧を作る o-- 会費納入状況チェック.プロシージャー一覧を作る : プロシージャー一覧を作る
会費納入状況チェック.Environment o-- Backboneライブラリ.KzUtil : KzCls
会費納入状況チェック.Environment o-- Backboneライブラリ.KzUtil : KzResolveExternalFilePath
会費納入状況チェック.プロシージャー一覧を作る o-- Backboneライブラリ.KzMetaprogramming : KzProcedureList
...

見ての通り

会費納入状況チェック.Environment o-- Backboneライブラリ.Test_DocTransformer : Test

この1行が出力されなくなった。