API Reference MetaCredo v#0.1.0
View SourceModules
Cross-language static code analysis tool built on MetaAST.
Terminal output formatting for MetaCredo analysis results.
Behaviour and macro for defining MetaCredo checks.
Detects exception or error container names that do not end in "Error" or "Exception". Consistent naming makes it easier to identify error types at a glance.
Detects functions that destructure data in the body when they could destructure directly in the parameters. Pattern matching in function heads is more idiomatic and clearly communicates intent.
Detects functions with cyclomatic complexity exceeding a threshold. Cyclomatic complexity measures the number of linearly independent paths through a function. High complexity makes code harder to test, understand, and maintain.
Detects modules with too many external dependencies (imports/aliases). High efferent coupling makes modules difficult to test, maintain, and reason about. Consider applying dependency inversion or splitting the module.
Detects modules where functions don't share data or variables, indicating the module lacks cohesion and may have multiple responsibilities. Consider splitting into focused modules.
Detects FIXME comments left in the codebase. FIXME tags indicate known bugs or broken code that must be fixed before shipping.
Detects TODO comments left in the codebase. While useful during development, TODOs should be tracked in an issue tracker and resolved before shipping to production.
Detects external HTTP client calls (e.g. HTTPoison.get, Req.post, Tesla.request, Finch.request) without telemetry instrumentation. External requests should be wrapped with telemetry to track API latency, failure rates, and service health.
Detects authentication/authorization code without telemetry or audit logging. Auth operations should be instrumented for security auditing, compliance, and incident response.
Detects component lifecycle methods (e.g. LiveView mount/3, React
componentDidMount) without telemetry. Lifecycle events should be
tracked for performance monitoring.
Detects background job processing functions (e.g. Oban worker perform/1)
without telemetry instrumentation. Background jobs should emit metrics
for monitoring, debugging, and alerting on job execution.
Detects telemetry/metrics emissions inside recursive functions. This causes metric spam (N emissions for N iterations), performance degradation, and misleading metrics. Instead, wrap the entire recursive operation with telemetry at the top level.
Detects deeply nested boolean operations (e.g. a and (b or (c and d))).
Complex boolean expressions are hard to reason about. Extract sub-conditions
into well-named variables or helper functions.
Detects functions with nesting depth exceeding a threshold. Deeply nested code (conditionals inside loops inside conditionals, etc.) is harder to understand and should be refactored into smaller functions.
Detects function names that do not follow snake_case convention.
Elixir functions should be named in snake_case, optionally ending
with ! or ?.
Detects large integer literals (> 9999) without underscore separators.
Use underscores to improve readability of large numbers
(e.g. 1_000_000 instead of 1000000).
Detects functions with too many statements. Long functions are harder to read, test, and maintain. Break them into smaller, focused functions.
Detects functions with too many parameters. Long parameter lists are hard to remember and use correctly. Consider grouping related parameters into a struct, map, or keyword list.
Detects numeric literals used directly in expressions without being assigned to a named constant. Magic numbers make code harder to understand and maintain.
Detects modules without documentation. Every module should have
a @moduledoc describing its purpose.
Detects module names that do not follow PascalCase convention.
Module names in Elixir should use PascalCase (e.g. MyApp.UserAccount).
Detects deeply nested function calls like foo(bar(baz(x))).
Extract intermediate results into variables or use pipes for clarity.
Detects single-step pipe chains (value |> func). A single pipe
adds visual noise without improving readability -- use a direct
function call instead.
Detects public functions without a preceding @spec type annotation.
Typespecs improve documentation, enable Dialyzer analysis, and make
function contracts explicit.
Detects variable names that do not follow snake_case convention.
Variables in Elixir should use snake_case (e.g. my_var, _unused).
Detects list ++ [item] pattern. Appending to a list with ++ creates
a full copy of the left list. Consider prepending with [item | list]
and reversing, or using a different data structure.
Detects duplicate function bodies within the same module by comparing normalized AST structure. Functions with identical logic should be consolidated into a shared helper.
Detects unreachable code after early returns. Statements following
a return, raise, or throw can never execute and should be removed.
Detects !!value (double boolean negation). While commonly used to
coerce a value to boolean, it harms readability. Use explicit
conversion or pattern matching instead.
Detects Enum.filter(...) |> Enum.count() patterns. Use
Enum.count(enumerable, fun) instead for a single-pass operation.
Detects if !condition do ... else ... end or if not condition do ... else ... end.
Swap the branches and remove the negation for clearer code.
Detects pipe chains that start with a literal value. Pipes should
start with a variable or function call, not a raw literal like
"hello" |> String.upcase().
Detects conditionals that return boolean literals and can be simplified to direct boolean expressions.
Detects unless ... else constructs. unless with an else branch
is confusing -- use if with swapped branches instead.
Detects the same variable name being assigned multiple times in the same block. Rebinding variables makes code harder to follow -- use distinct names or restructure the code.
Detects hardcoded URLs, IP addresses, and other sensitive values in string literals. Move these values to configuration files or environment variables.
Detects improper input validation patterns (CWE-20).
Detects incorrect authorization patterns (CWE-863).
Detects inline executable code in templates/strings (XSS/injection risk).
Detects Insecure Direct Object Reference (IDOR) vulnerabilities (CWE-639).
Detects critical functions without authentication checks (CWE-306).
Detects sensitive operations without authorization checks (CWE-862).
Detects state-changing endpoints without CSRF protection (CWE-352).
Detects potential Path Traversal vulnerabilities (CWE-22).
Detects potential SQL injection vulnerabilities (CWE-89).
Detects potential Server-Side Request Forgery (SSRF) vulnerabilities (CWE-918).
Detects exposure of sensitive information to unauthorized actors (CWE-200).
Detects Time-of-Check-Time-of-Use (TOCTOU) race condition vulnerabilities (CWE-367).
Detects unrestricted file upload vulnerabilities (CWE-434).
Detects potential Cross-Site Scripting (XSS) vulnerabilities (CWE-79).
Shared utilities for check implementations.
Detects blocking operations (HTTP calls, long DB queries, file I/O)
inside Plug middleware functions (call/2, init/1) or Phoenix
controller plugs. Blocking in the request pipeline degrades throughput
for all concurrent requests.
Detects boolean operations where both operands are structurally identical,
such as x && x, x || x, x and x, x or x. These are always
redundant and likely indicate a copy-paste error.
Detects deeply nested conditional statements (case, with, if/else)
exceeding the configured nesting threshold. Deep nesting creates
"callback hell" that is hard to read, test, and maintain.
Detects debug function calls left in code, such as IO.inspect,
IO.puts, dbg(), print(), console.log, pry, and similar.
These should be removed before merging to production.
Detects direct struct field updates (e.g. %User{user | name: "new"})
that bypass validation. In Ecto-backed applications, data changes should
go through changesets to ensure validation, casting, and constraint
checking.
Detects imperative if/else or case chains that branch on a status or
state field with 3+ distinct values. Ad-hoc status management is
error-prone (missing transitions, invalid state paths) and should be
replaced with a finite state machine (Finitomata, gen_statem, or
equivalent).
Detects the pattern of fetching all records from the database (e.g.
Repo.all(User)) and then filtering them in memory with Enum.filter/2
or Enum.reject/2. This wastes bandwidth, memory, and CPU.
Detects Logger calls that use string interpolation directly instead of wrapping the message in an anonymous function. Eager interpolation performs the string building even when the log level is disabled.
Detects pattern matches on success tuples ({:ok, value} = expr)
without corresponding error handling. This can crash the process
on unexpected errors.
Detects LiveView handle_event callbacks that perform blocking work
(HTTP calls, heavy computation, long DB queries) without delegating
to start_async/3 or Task.Supervisor. Blocking in handle_event
freezes the LiveView process and the user sees no feedback.
Detects collection operations (e.g. Enum.map) over database query
results that were fetched without eager loading. This pattern is a
strong indicator of potential N+1 queries when associations are accessed
inside the loop.
Detects expensive operations triggered by user input (form submissions,
search endpoints, API mutations) without rate limiting or throttling.
In LiveView, form inputs sending phx-change events should use
phx-debounce or phx-throttle to avoid flooding the server.
Detects database operations (e.g. Repo.get, Repo.one) called inside
collection operations like Enum.map/2 or Enum.each/2. This creates
an N+1 query problem where N extra queries are issued for N items.
Detects arithmetic operations on identical operands that produce a
constant result: x - x is always 0, x / x is always 1.
These are likely copy-paste errors or logic mistakes.
Detects arithmetic operations with a constant result or identity
operand: x * 0 is always 0, and x + 0 is a no-op identity.
These suggest dead code or incomplete expressions.
Detects bare raise or throw inside rescue/exception handling blocks
without re-raise semantics. Using raise inside a rescue block instead
of reraise loses the original stack trace, making debugging harder.
Detects case statements that match on {:ok, _} without a corresponding
{:error, _} branch. Missing error branches cause silent failures when
the called function returns an error tuple.
Detects try/rescue blocks where the rescue clause neither logs the
exception nor re-raises it. Swallowing exceptions silently hides errors
and makes debugging nearly impossible.
Detects blocking synchronous operations (HTTP calls, long DB queries,
file I/O) inside GenServer callbacks (handle_call, handle_cast,
handle_info) or LiveView callbacks (mount, handle_event,
handle_params). Blocking in these contexts stalls the process
mailbox and degrades responsiveness.
Detects Task.async/1 and Task.start/1 calls that are not routed
through a Task.Supervisor. Unsupervised tasks can leak memory,
silently fail, and leave orphaned processes.
Detects System.cmd, os:cmd, :os.cmd, or similar execution calls
with user-controlled arguments. Passing user input to system commands
can lead to command injection vulnerabilities.
Detects function call results that are unused. A function call appearing as a statement in a block whose result is neither assigned nor returned (i.e., not the last statement and not wrapped in an assignment) likely indicates a missing assignment or accidental side-effect-only call.
Test helpers for MetaCredo check tests.
Parses and manages .metacredo.exs configuration files.
Orchestrates the full analysis pipeline: source discovery, check execution, issue collection, and inline-disable filtering.
Represents a single issue found during analysis.
Wraps a Metastatic.Document with source text and filename for analysis.
Discovers and parses source files for analysis.
Mix Tasks
Runs MetaCredo checks on your project.
Generates a .metacredo.exs configuration file in the current directory.