Skip to main content

W0084 - attribute_order

Severity: Warning

Problem

This diagnostic fires when module-level attributes do not follow the order configured for the linter.

-module(main).
-compile(warn_missing_spec_all).
-oncall("an_oncall").
%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: W0084: `-oncall` must appear before `-compile`.

Explanation

Some codebases mandate a fixed ordering for module-level attributes so that file headers stay consistent and easy to scan. This diagnostic enforces the order you configure in the [attribute_order] section of .elp_lint.toml:

[attribute_order]
order = [
"module",
"author",
"oncall",
"moduledoc",
"compile",
"behaviour",
"export",
"export_type",
["include", "include_lib"],
["type", "opaque", "nominal", "define", "record"],
]

Each entry is either a single attribute name or a group — a list of names that share one rank, so any order among them is accepted. Above, -include and -include_lib are interchangeable.

-type / -opaque / -nominal, -define and -record are grouped together on purpose: Erlang requires a record or macro used inside a type to be declared before it, so a fixed order among them (e.g. type before record) would flag — and a reorder would break — otherwise-valid modules. Keeping them in one rank enforces only that definitions come after the header block, not their internal order.

Attributes are matched by name. Custom (wild) attributes — e.g. -author, -oncall, or a project-specific -my_attr — match their literal name. Structured attributes are matched by a canonical keyword:

module, moduledoc, doc, compile, behaviour (covers both -behaviour and -behavior), callback, optional_callbacks, export, export_type, import, import_record, export_record, deprecated, feature, include, include_lib, type, opaque, nominal, define, and record.

Only the names listed in order are checked, and a listed name that doesn't match any attribute in a given file is simply ignored — it never triggers or anchors a violation. Only Erlang modules (.erl) are checked; header files (.hrl) are skipped, so the include-guard idiom — a -define guard preceding the file's -include_lib directives — is never reported. Ordering is checked per module, so attributes pulled in from an included header are not mixed with the including module's attributes.

The linter is enabled by default but is a no-op until an order is configured — without an order section it reports nothing.

Fix

Reorder the attributes to match the configured order.