Moved out of Crafter.Build's lint-rules.h so a project.cpp pulls them in with `#pragma crafter dependency` + `import Crafter.LintRules;` instead of copying a header. The rule bodies are unchanged; format-concat now sits with the other transforms (it rewrites, it was registered as a report), and the helpers are module-internal. tests/HouseRules moved along with the rules and links crafter-build from git, or the sibling checkout with --local. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3 KiB
Crafter.LintRules
The Catcrafts house-style lint rules for crafter-build, packaged as one C++ module. Naming conventions, K&R braces, enum class, std::println over iostream, fixed-width integer types, std::format over concatenation, line wrapping, whitespace hygiene and a few AST-backed checks (const locals, constexpr constants, no char*). Most rules are transforms, so crafter-build format applies them.
Use
In a project.cpp:
import std;
import Crafter.Build;
#pragma crafter dependency "https://forgejo.catcrafts.net/Catcrafts/Crafter.LintRules.git"
import Crafter.LintRules;
using namespace Crafter;
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
Configuration cfg;
// ...
LintRules::AddRules(cfg);
return cfg;
}
Then crafter-build lint reports, crafter-build format rewrites, and crafter-build format --check gates CI. Pin a revision for reproducible builds:
#pragma crafter dependency "https://forgejo.catcrafts.net/Catcrafts/Crafter.LintRules.git" commit "<sha>"
Suppress a finding with // lint-disable-next-line <rule> or a whole file with // lint-disable-file <rule>.
Rules
| Rule | Kind | What it wants |
|---|---|---|
spdx-header |
report | // SPDX-License-Identifier: on line 1, // SPDX-FileCopyrightText: on line 2, blank line 3 |
no-tabs |
report | spaces |
naming |
report, AST | PascalCase types, functions, statics and constants; camelCase variables |
enum-class |
report | enum class, never a plain enum |
no-iostream-print |
report | std::println instead of std::cout/std::cerr streaming |
no-char-pointer |
report, AST | std::string / std::string_view over char* outside C interop |
const-local |
report, AST | const on a scalar local that is never written |
constexpr-constant |
report, AST | constexpr on a constant with a constant initialiser |
format-concat |
transform | std::format instead of + with string literals |
fixed-width-types |
transform, AST | std::int32_t and friends instead of int, long, … |
single-declaration |
transform, AST | one declaration per statement |
brace-style |
transform | K&R braces |
if-single-line |
transform | if (x) return; on one line when it fits |
wrap-join |
transform | join wrapped statements that fit on one line |
paren-spacing |
transform | if ( and for (, no space before a call's ( |
trim-trailing-ws |
transform | no trailing whitespace |
final-newline |
transform | files end with a newline |
Tests
tests/HouseRules exercises every rule against small inputs. It links crafter-build to run the linter in-process, so it fetches and builds it from git; with a sibling checkout at ../Crafter.Build use crafter-build test --local.
Building for other targets
The module imports Crafter.Build, which only exists for the host. That is all a rule ever needs: rules run inside the crafter-build process that loads your project.cpp.