Assay.Ignore (assay v0.6.1)

Copy Markdown View Source

Warning decoration and ignore rule filtering.

This module wraps raw Dialyzer warnings with metadata (file paths, line numbers, warning codes) and applies ignore rules from dialyzer_ignore.exs files.

Ignore File Format

The ignore file (dialyzer_ignore.exs by default) should return a list of rules. Each rule can be:

  • A string - matches if the warning text contains the string
  • A regex - matches if the warning text matches the regex
  • A map with keys:
    • :file or :relative - file path pattern (string or regex)
    • :message - message text pattern (string or regex)
    • :line - exact line number (integer)
    • :code or :tag - warning code atom (e.g., :warn_failing_call)

Example

# dialyzer_ignore.exs
[
  "Function will never return",  # Simple string match
  ~r/unknown function/,          # Regex match
  %{file: "lib/legacy.ex"},      # Match all warnings in a file
  %{code: :warn_not_called, line: 42}  # Match specific warning
]

Summary

Functions

Wraps raw Dialyzer warnings with useful metadata for formatting and ignore matching.

Applies ignore rules (if any) and returns {kept, ignored, file_path} where the file_path is the ignore file that was loaded or nil when no file was used.

Types

entry()

@type entry() :: %{
  raw: term(),
  text: String.t(),
  match_text: String.t(),
  path: String.t() | nil,
  relative_path: String.t() | nil,
  line: integer() | nil,
  column: integer() | nil,
  code: atom()
}

Functions

decorate(warnings, project_root)

@spec decorate([term()], binary()) :: [entry()]

Wraps raw Dialyzer warnings with useful metadata for formatting and ignore matching.

Examples

# Decorate a single warning
warning = {:warn_not_called, {"/project/lib/foo.ex", {10, 5}}, {:MyApp, :unused, 1}}
entries = Assay.Ignore.decorate([warning], "/project")
[entry] = entries
entry.code
# => :warn_not_called
entry.path
# => "/project/lib/foo.ex"
entry.relative_path
# => "lib/foo.ex"
entry.line
# => 10
entry.column
# => 5

filter(entries, ignore_file, opts \\ [])

@spec filter([entry()], binary() | nil, keyword()) ::
  {[entry()], [entry()], binary() | nil}

Applies ignore rules (if any) and returns {kept, ignored, file_path} where the file_path is the ignore file that was loaded or nil when no file was used.

When explain?: true is passed in opts, the ignored entries will include a :matched_rules field containing the list of rules that matched.

Examples

# Filter with string rule
entry = %{
  code: :warn_not_called,
  match_text: "Function MyApp.unused/1 is never called",
  path: "/project/lib/foo.ex",
  relative_path: "lib/foo.ex",
  line: 10
}
rules = ["never called"]
# In real usage, rules come from dialyzer_ignore.exs
# This is a simplified example
{kept, ignored, _path} = Assay.Ignore.filter([entry], nil)
# kept is empty, ignored contains the entry (if rules matched)

# Filter with file pattern
entry = %{
  code: :warn_failing_call,
  match_text: "Call will fail",
  path: "/project/lib/legacy.ex",
  relative_path: "lib/legacy.ex",
  line: 5
}
# With ignore file containing: [%{file: "lib/legacy.ex"}]
{kept, ignored, path} = Assay.Ignore.filter([entry], "dialyzer_ignore.exs")
# If ignore file exists and matches, entry is in ignored list