Skip to content
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.

## Next Release

### Features

#### CPP Rules

- Add `noPublicLeadingUnderscore` AST check flagging public member variables and member functions (including static and template ones) whose name starts with `_`, e.g. `int _count;` or `void _compute();` under `public:` (or under a `struct`'s default access) should drop the leading underscore. Private/protected members are never checked here, since that's the concern of `memberLeadingUnderscore`/`memberFunctionLeadingUnderscore`. Constructors/destructors, operator overloads/conversions, and methods overriding a base-class virtual method are always exempt, and members synthesized entirely by a macro invoked on the same source line are excluded too. Takes no configuration
- Add `classMemberOrder` AST check enforcing a fixed section order within each class/struct/union body: public, protected, then private member variables, followed by public, protected, then private member functions. Only in-class declarations count — out-of-line member-function definitions don't affect ordering — and declarations outside that list (nested types, `using` declarations, enums, friend declarations, ...) are ignored rather than resetting the sequence. Takes an optional `excluded_macros` list (e.g. `["Q_OBJECT"]`) so members/access-specifier changes synthesized by a named macro invocation are excluded from ordering entirely

<!-- insertion marker -->
## [0.7.0](https://github.com/repo/owner/releases/tag/0.7.0) - 2026-09-28

Expand Down
88 changes: 88 additions & 0 deletions docs/source/sections/ast_checks/enforce_class_member_order.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
classMemberOrder
=================

Flags a member variable or member function declared before a section that
must precede it. Within a single class/struct/union body (including
templates), the direct members must appear grouped, in exactly this order:

1. public member variables
2. protected member variables
3. private member variables
4. public member functions
5. protected member functions
6. private member functions

.. code-block:: cpp

class Widget {
public:
int size;

protected:
int flag;

private:
int count;

public:
void resize();

protected:
void hook();

private:
void compute();
};

class Bad {
private:
int count; // Bad — flagged, a private member variable

public:
int size; // before a public one.

void resize();

private:
void compute();

public:
void hook(); // Bad — flagged, a public member function after
// a private one.
};

A section may be skipped entirely (e.g. a class with no protected members
at all), but once a later section has started, an earlier one may not
reappear.

Only members declared lexically inside the class body count — an
out-of-line member-function definition (``void C::f() { ... }``) never
affects its class's ordering, only the in-class declaration does. Nested
types, ``using`` declarations, enums, friend declarations, and other
declarations not covered by the list above are ignored for ordering
purposes: they neither need to fit anywhere in particular nor reset the
sequence.

Configuration
--------------

Optional — exclude specific macros invoked inside a class body from
ordering entirely. Any member (or access-specifier change) synthesized by
a listed macro, matched by the macro's own name at its invocation line, is
skipped: it's neither flagged itself nor counted when checking what came
before or after it. Useful for macros such as Qt's ``Q_OBJECT`` that expand
to boilerplate members and their own access-specifier bookkeeping, whose
position isn't the author's choice.

.. code-block:: toml

[cpp.ast_check_config.classMemberOrder]
excluded_macros = ["Q_OBJECT", "MY_DECLARE_PROPERTY"]

Disabling
----------

.. code-block:: toml

[cpp]
ast_check_disabled_ids = ["classMemberOrder"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
noPublicLeadingUnderscore
===========================

Flags any data member or member function declared under a ``public:``
access specifier (or under no access specifier at all in a
``struct``/``union``) whose name starts with ``_``. Private/protected
members are never flagged here — that's the concern of
:doc:`memberLeadingUnderscore <enforce_member_leading_underscore>` and
:doc:`memberFunctionLeadingUnderscore
<enforce_member_function_leading_underscore>`.

.. code-block:: cpp

class Widget {
public:
int size; // Good.
int _size; // Bad — flagged, leading underscore on a public member.

void compute(); // Good.
void _compute(); // Bad — flagged, leading underscore on a public method.

private:
int _count; // Good — not checked here.
};

Constructors/destructors, operator overloads/conversions, and methods
overriding a base-class virtual method are always exempt, since their names
aren't the author's to change. A member synthesized entirely by a macro
invoked on that same source line is not flagged either — there is no
user-typed name to rename.

Configuration
--------------

This check takes no configuration.

Disabling
----------

.. code-block:: toml

[cpp]
ast_check_disabled_ids = ["noPublicLeadingUnderscore"]
10 changes: 10 additions & 0 deletions docs/source/sections/ast_checks/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ Available checks
* - :doc:`memberFunctionLeadingUnderscore <enforce_member_function_leading_underscore>`
- A private or protected member function not starting with ``_``.
- None
* - :doc:`noPublicLeadingUnderscore <enforce_no_public_leading_underscore>`
- A public member variable/function starting with ``_``.
- None
* - :doc:`classMemberOrder <enforce_class_member_order>`
- A member variable/function declared before a section that must
precede it (public/protected/private members, then
public/protected/private member functions).
- Optional (``excluded_macros``)
* - :doc:`paramNameForType <param_name_for_type>`
- A parameter of a configured type not using its canonical name.
- Required (``type_to_name``)
Expand All @@ -92,6 +100,8 @@ Available checks

enforce_member_leading_underscore
enforce_member_function_leading_underscore
enforce_no_public_leading_underscore
enforce_class_member_order
param_name_for_type
macro_replacement
no_global_using
Expand Down
Loading
Loading