Packages
metastatic
0.5.2
0.26.0
0.25.0
0.24.1
0.24.0
0.23.0
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.3
0.20.2
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Cross-language code meta-model library using unified MetaAST representation. Parse, transform, and translate code across Python, Elixir, Ruby, Erlang, Haskell, and more via a shared three-tuple AST format.
Current section
Files
Jump to
Current section
Files
lib/metastatic/analysis/business_logic/missing_error_handling.ex
defmodule Metastatic.Analysis.BusinessLogic.MissingErrorHandling do
@moduledoc """
Detects pattern matching on success cases without error handling.
This analyzer identifies code that pattern matches on a success value (typically
a tuple or enum variant) without handling the potential error case, which can
lead to runtime crashes.
## Cross-Language Applicability
This pattern applies to languages with pattern matching and Result/Option types:
- **Elixir**: Matching `{:ok, value}` without handling `{:error, reason}`
- **Rust**: Unwrapping `Result<T, E>` with `.unwrap()` or matching only `Ok(v)`
- **OCaml/F#**: Matching only `Some(v)` without handling `None`
- **Scala**: Pattern matching on `Success` without `Failure`
- **Haskell**: Pattern matching on `Right` without `Left`
## Examples
### Bad (Elixir)
{:ok, user} = Accounts.get_user(id) # Will crash if error returned
### Good (Elixir)
case Accounts.get_user(id) do
{:ok, user} -> user
{:error, reason} -> handle_error(reason)
end
# Or with pattern
with {:ok, user} <- Accounts.get_user(id) do
user
end
### Bad (Rust)
let user = get_user(id).unwrap(); // Panics on error
### Good (Rust)
let user = match get_user(id) {
Ok(u) => u,
Err(e) => handle_error(e),
};
## Detection Strategy
Detects pattern matching nodes where:
1. The pattern is a success variant (e.g., tuple with `:ok` atom, or similar markers)
2. No corresponding error handling pattern exists in the same scope
"""
@behaviour Metastatic.Analysis.Analyzer
alias Metastatic.Analysis.Analyzer
# Common success markers across languages
@success_markers [:ok, :some, :right, :success]
@impl true
def info do
%{
name: :missing_error_handling,
category: :correctness,
description: "Detects pattern matching on success without error handling",
severity: :warning,
explanation: """
Pattern matching directly on success cases without handling errors can
lead to runtime crashes. Always handle both success and error cases, or
use safe unwrapping mechanisms provided by your language.
Consider using:
- Explicit case/match with all branches
- Safe unwrapping (e.g., `unwrap_or`, `match`, `with`)
- Result/Option combinators (map, and_then, etc.)
""",
configurable: false
}
end
@impl true
def analyze({:pattern_match, pattern, _value} = node, _context) do
if success_pattern_without_error?(pattern) do
[
Analyzer.issue(
analyzer: __MODULE__,
category: :correctness,
severity: :warning,
message: "Pattern match on success case without error handling can cause crashes",
node: node,
metadata: %{pattern: pattern}
)
]
else
[]
end
end
def analyze(_node, _context), do: []
# ----- Private Helpers -----
# Check if pattern is a success pattern without error handling
# This looks for patterns like {:ok, value} or similar success markers
defp success_pattern_without_error?(pattern) do
case pattern do
# Tuple pattern with success marker: {:ok, value}
{:list, [marker | _]} when is_tuple(marker) ->
case marker do
{:literal, :atom, atom} when atom in @success_markers -> true
_ -> false
end
# Map pattern with success marker
{:map, fields} when is_list(fields) ->
Enum.any?(fields, fn
{{:literal, :atom, key}, _value} when key in @success_markers -> true
_ -> false
end)
_ ->
false
end
end
end