Skip to content

Commit f074412

Browse files
aspalaclaude
andauthored
feat(tooling): add dialyxir and fix all dialyzer warnings (#12)
* feat(tooling): add dialyxir and fix all dialyzer warnings - Add dialyxir ~> 1.4 as a dev dependency - Add @type t to CodeQA.Pipeline.FileContext so the type is resolvable - Remove nan?/1 helper (BEAM has no NaN floats, made not nan?() always true) - Remove redundant is_float/1 check in keep_correlation? (pearson always returns float) - Simplify maybe_add/3 to a single-clause if expression Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): add dialyzer diff workflow for PRs Runs dialyzer on both the PR head and merge base, diffs the warnings, and posts a summary comment on the PR. Fails if new warnings are introduced. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): replace dialyzer diff with zero-warnings check The diff approach was broken on this PR because the merge base (main) doesn't have dialyxir yet — mix dialyzer silently produced an empty baseline, making it look like base had 0 warnings. Since this PR already brings us to 0 warnings, a simple "fail if any warnings" check is correct and stricter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f085bff commit f074412

5 files changed

Lines changed: 82 additions & 9 deletions

File tree

.github/workflows/dialyzer.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Dialyzer
2+
3+
on:
4+
pull_request:
5+
branches: ["main"]
6+
7+
concurrency:
8+
group: dialyzer-${{ github.head_ref }}
9+
cancel-in-progress: true
10+
11+
jobs:
12+
dialyzer:
13+
name: Dialyzer (zero warnings)
14+
runs-on: ubuntu-latest
15+
16+
env:
17+
MIX_ENV: dev
18+
ELIXIR_VERSION: "1.19"
19+
OTP_VERSION: "27.3"
20+
21+
steps:
22+
- name: Checkout PR
23+
uses: actions/checkout@v4
24+
25+
- name: Set up Elixir
26+
uses: erlef/setup-beam@v1
27+
with:
28+
elixir-version: ${{ env.ELIXIR_VERSION }}
29+
otp-version: ${{ env.OTP_VERSION }}
30+
31+
- name: Cache deps
32+
uses: actions/cache@v4
33+
with:
34+
path: deps
35+
key: deps-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('mix.lock') }}
36+
restore-keys: |
37+
deps-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
38+
39+
- name: Cache build
40+
uses: actions/cache@v4
41+
with:
42+
path: _build
43+
key: build-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('mix.lock') }}
44+
restore-keys: |
45+
build-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
46+
47+
- name: Cache PLT
48+
uses: actions/cache@v4
49+
with:
50+
path: priv/plts
51+
key: plt-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('mix.lock') }}
52+
restore-keys: |
53+
plt-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
54+
55+
- name: Install deps
56+
run: mix deps.get
57+
58+
- name: Compile
59+
run: mix compile
60+
61+
- name: Dialyzer
62+
run: mix dialyzer

lib/codeqa/cli.ex

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ defmodule CodeQA.CLI do
501501
end
502502

503503
defp keep_correlation?(corr, opts) do
504-
valid = is_float(corr) and not nan?(corr) and corr != 0.0
504+
valid = corr != 0.0
505505
valid = if opts[:hide_exact], do: valid and abs(corr) != 1.0, else: valid
506506
valid = if valid and opts[:min], do: corr >= opts[:min], else: valid
507507
if valid and opts[:max], do: corr <= opts[:max], else: valid
@@ -895,13 +895,9 @@ defmodule CodeQA.CLI do
895895
end)
896896
end
897897

898-
defp maybe_add(opts, nil, _item), do: opts
899-
defp maybe_add(opts, false, _item), do: opts
900-
defp maybe_add(opts, _truthy, item), do: [item | opts]
901-
902-
# IEEE 754: NaN != NaN — Credo false positive (comparison IS meaningful for NaN)
903-
# credo:disable-for-next-line Credo.Check.Warning.OperationOnSameValues
904-
defp nan?(x), do: x != x
898+
defp maybe_add(opts, val, item) do
899+
if val, do: [item | opts], else: opts
900+
end
905901

906902
defp parse_ignore_paths(nil), do: []
907903

lib/codeqa/pipeline.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ defmodule CodeQA.Pipeline do
1515
:line_count
1616
]
1717
defstruct @enforce_keys
18+
19+
@type t :: %__MODULE__{
20+
content: String.t(),
21+
tokens: tuple(),
22+
token_counts: map(),
23+
words: tuple(),
24+
identifiers: tuple(),
25+
lines: tuple(),
26+
encoded: String.t(),
27+
byte_count: non_neg_integer(),
28+
line_count: non_neg_integer()
29+
}
1830
end
1931

2032
@word_re ~r/\b[a-zA-Z_]\w*\b/u

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ defmodule CodeQA.MixProject do
2929
{:flow, "~> 1.2"},
3030
{:yaml_elixir, "~> 2.11"},
3131
{:ex_doc, "~> 0.34", only: :dev, runtime: false},
32-
{:credo, "~> 1.7", only: :dev, runtime: false}
32+
{:credo, "~> 1.7", only: :dev, runtime: false},
33+
{:dialyxir, "~> 1.4", only: :dev, runtime: false}
3334
]
3435
end
3536
end

mix.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
33
"complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"},
44
"credo": {:hex, :credo, "1.7.17", "f92b6aa5b26301eaa5a35e4d48ebf5aa1e7094ac00ae38f87086c562caf8a22f", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1eb5645c835f0b6c9b5410f94b5a185057bcf6d62a9c2b476da971cde8749645"},
5+
"dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"},
56
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
7+
"erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"},
68
"ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"},
79
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
810
"flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"},

0 commit comments

Comments
 (0)