Skip to content

fix(cli): narrow parse_subnet_option/1 spec to the value it returns - #85

Merged
toshi0806 merged 2 commits into
mainfrom
issue-46-fix-dialyzer-contract-supertype
Aug 11, 2026
Merged

fix(cli): narrow parse_subnet_option/1 spec to the value it returns#85
toshi0806 merged 2 commits into
mainfrom
issue-46-fix-dialyzer-contract-supertype

Conversation

@toshi0806

Copy link
Copy Markdown
Member

概要

main で失敗し続けていた Dialyzer を解消する。あわせて、作業中に踏んだ .gitignore のパターン漏れを直す。

Resolves #46

issue #46 の実態

issue 本文は PLT の絶対パスエラー(/Users/toshi/... を CI が探しに行く)を報告しているが、これは issue の元になった commit f4693cc 自身(PR #45)が修正済みだった。しかし Dialyzer は現在の main でも別の理由で失敗し続けている。

Total errors: 9, Skipped: 8, Unnecessary Skips: 0
lib/tdig/cli.ex:239:contract_supertype
Type specification is a supertype of the success typing.
  @spec parse_subnet_option(String.t()) :: {atom(), map()}

なぜ気づけなかったか

再利用ワークフローの Dialyzer 実行ステップには continue-on-error: true が付いている。つまり ci / Dialyzer Analysis ...: success は Dialyzer が通った証拠にならない。 失敗しても job は success で、issue が立つだけである。

最新 main (56d90d7) のステップ単位の結果がそれを示している。

9.  Run Dialyzer with output capture: success   ← continue-on-error なので常に success
11. Create GitHub Issue for Dialyzer failures: success   ← 実行された = Dialyzer は failure
12. Close Dialyzer issues on success: skipped

さらに issue 作成には重複防止があり、open な Dialyzer issue が既にあると新規作成をスキップする。#46 が開いたままだったため、以後の失敗は一切表に出ていなかった。

変更内容

1. spec を実態に合わせて狭める

parse_subnet_option/1 は常に :edns_client_subnet タグ付きタプルを返し、その map のキーは 4 つとも既知である。{atom(), map()} は広すぎるため、:underspecscontract_supertype を報告していた。

形を @type edns_client_subnet として宣言した。呼び出し側が何に依存してよいかが spec に書かれ、返す map が変わったときに map() に吸収されず報告されるようになる。

挙動は変えていない。IPv4 / IPv6 両経路の既存テストはそのまま通る。

2. .gitignore の escript パターンをルートに固定

commit しようとしたところ git add lib/tdig/cli.ex が次の警告を出した。

The following paths are ignored by one of your .gitignore files: lib/tdig

29 行目が裸の tdig で、これは同名のパス構成要素すべてにマッチする。escript バイナリのつもりが lib/tdig/ ディレクトリにも当たっていた。追跡済みファイルには ignore 規則が適用されないので既存ファイルは無事だったが、実測すると新規ファイルは消える。

$ touch lib/tdig/probe_tmp.ex
$ git check-ignore -v lib/tdig/probe_tmp.ex
.gitignore:29:tdig      lib/tdig/probe_tmp.ex
$ git status --short lib/tdig/probe_tmp.ex
  → 出ない(= git status にすら現れない)

/tdig に固定した。修正後は lib/tdig/ 配下が ?? として見え、ルートのバイナリは従来どおり ignore される。

検証

mix dialyzer   → Total errors: 8, Skipped: 8  (未処理 0、done passed successfully)
mix test       → 60 passed
mix credo --strict → no issues
mix format --check-formatted → OK

補足

Dialyzer は push 時のみ動く(github.event_name == 'push')ため、この PR の CI では走らない。main に入った後の run で Close Dialyzer issues on success ステップが実行され #46 が閉じられることを確認するのが最終確認になる。

https://claude.ai/code/session_01YAjSJR67tWTvYDJLcQThbQ

Dialyzer runs with :underspecs and reported contract_supertype: the spec
said {atom(), map()} while the function only ever returns an
:edns_client_subnet tagged tuple whose map has four known keys.

Declare the shape as a type instead. The spec now says what the caller
can rely on, and a change to the returned map is reported rather than
absorbed by map().

No behaviour change; the existing parse_subnet_option/1 tests cover the
IPv4 and IPv6 paths unchanged.

Resolves #46

Claude-Session: https://claude.ai/code/session_01YAjSJR67tWTvYDJLcQThbQ
The pattern was a bare `tdig`, which matches any path component with that
name -- including the lib/tdig/ source directory. A file added there was
ignored and did not appear in `git status`, so it could be lost silently.
Existing files were unaffected because ignore rules do not apply to
tracked paths, which is why this went unnoticed.

Anchor it to the root so it covers only the escript binary.

Claude-Session: https://claude.ai/code/session_01YAjSJR67tWTvYDJLcQThbQ

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dialyzer の contract_supertype を解消するために spec を実態に合わせて狭める変更、および .gitignore のアンカー修正は、いずれも根拠が明確で適切な改善です。PR 本文の調査も丁寧で、continue-on-error による見落としの分析も有益でした。以下、細かな確認点をいくつか挙げます。

Comment thread lib/tdig/cli.ex
%{
family: 1 | 2,
client_subnet: :inet.ip_address(),
source_prefix: integer(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ [LOW] source_prefix: integer() について確認です。RFC 7871 のプレフィックス長は 0〜128 の非負値であり、実際に負値になることはないはずです。より success typing に近づけるなら non_neg_integer()(あるいは実装が返し得る具体的な範囲)にした方が、:underspecs 観点でも意図が明確になります。実装が本当に任意の integer を返し得るのであれば現状で問題ありませんが、範囲を絞れるなら検討をおすすめします。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「実装が本当に任意の integer を返し得るのであれば現状で問題ありません」という条件のほうが成立していました。実測です。

192.0.2.1/24:   source_prefix: 24
192.0.2.1/-5:   source_prefix: -5     ← 負値が通る
192.0.2.1/999:  source_prefix: 32     ← 上限だけは効く
2001:db8::1/-1: source_prefix: -1

prefix = String.to_integer(prefix_str)"-5" を素直に -5 にし、min(prefix, 32)上限しか抑えないためです。RFC 7871 上は 0〜128 ですが、この実装はその範囲を検証していません。

したがって non_neg_integer() にすると spec が実装より狭くなり、実際に返る値を排除する嘘の契約になります(Dialyzer の success typing も integer() です)。ここは integer() のまま据え置きます。

ただし指摘の背後にある「負の prefix は本来受け付けるべきでない」という点は妥当で、これは spec ではなく入力検証の欠落です。値域外を弾くのかクランプするのかは挙動の変更を伴うため、本 PR(Dialyzer の解消)とは分けて #86 に起票しました。

Comment thread .gitignore
# The escript binary, at the repository root only. Unanchored, this pattern
# also matches the lib/tdig/ directory, and a file added there is ignored
# without ever showing up in `git status`.
/tdig

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ [POSITIVE] tdig から /tdig への修正は、lib/tdig/ ディレクトリ配下の新規ファイルが暗黙に無視される問題を的確に解消しており、コメントで理由も明記されている点が非常に良いです。

@toshi0806
toshi0806 merged commit 81f0404 into main Aug 11, 2026
8 checks passed
@toshi0806
toshi0806 deleted the issue-46-fix-dialyzer-contract-supertype branch August 11, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dialyzer warnings/errors in main branch (f4693cc)

1 participant