-
Notifications
You must be signed in to change notification settings - Fork 0
Code Style and Conventions
Hugo edited this page Feb 26, 2026
·
1 revision
The project uses clang-format version 20 with an LLVM-based configuration.
- Column limit: 100
- Indent: 4 spaces
- Brace style: Allman (braces on new lines)
- No tabs
# Format all files
./scripts/format.sh
# Check without modifying
./scripts/format-check.sh
# Via CMake
cmake --build build --target format
cmake --build build --target format-checkCI fails if any file is not formatted. Always format before committing.
-
C++20 (
CMAKE_CXX_STANDARD 20) - Extensions disabled (
CMAKE_CXX_EXTENSIONS OFF) - Minimum compiler: GCC 12+ or Clang 15+
namespace ctrace::stack // main namespace
namespace ctrace::stack::analysis // analysis modules
namespace ctrace::stack::cli // CLI parsing
namespace ctrace::stack::app // application layer- Classes/structs:
PascalCase(AnalysisConfig,FunctionResult,DiagnosticEmitter) - Enums:
PascalCase(AnalysisMode,DiagnosticSeverity) - Enum values:
PascalCase(AnalysisMode::IR,DiagnosticSeverity::Warning)
- Free functions:
camelCase(analyzeModule,toJson,toSarif) - Member functions:
camelCase(isSatisfiedBy,shouldAnalyze) - Static helper functions:
camelCase(normalizePath,filterResult)
- Local variables:
camelCase(inputFilenames,loadedModules) - Member variables:
camelCase_with trailing underscore (cfg_,filters_) - Constants:
kPascalCase(kCrossTUMaxIterations,kCacheSchema)
- Headers in
include/(public API) andinclude/analysis/,include/analyzer/, etc. - Implementations in
src/mirroring the include structure - All headers use
#pragma once
Each analysis module follows the pattern:
- Header declares the finding struct and the analysis function
- Source implements the analysis logic
- Module is stateless when possible (takes module/function as input, returns findings)
- Corresponding header
- Project headers
- LLVM headers
- Standard library headers
The codebase uses AppResult<T> for error handling:
template <typename T> struct AppResult
{
std::optional<T> value;
std::string error;
static AppResult<T> success(T v);
static AppResult<T> failure(std::string e);
bool isOk() const;
};Used for filter logic:
class FunctionReportSpecification
{
public:
bool isSatisfiedBy(const FunctionResult& function) const;
};
class InputExclusionSpecification
{
public:
bool isSatisfiedBy(const std::string& inputPath) const;
};Used for execution and output dispatch:
class AnalysisExecutionStrategy { virtual AppStatus execute(...) = 0; };
class OutputStrategy { virtual int emit(...) = 0; };Used for constructing the analysis run plan:
class RunPlanBuilder
{
public:
explicit RunPlanBuilder(cli::ParsedArguments parsedArgs);
AppResult<RunPlan> build();
};for (const auto& F : module)
{
if (F.isDeclaration())
continue;
// analyze F
}for (const auto& BB : function)
{
for (const auto& I : BB)
{
if (auto* store = llvm::dyn_cast<llvm::StoreInst>(&I))
{
// handle store
}
}
}if (const auto& DL = instruction.getDebugLoc())
{
unsigned line = DL.getLine();
unsigned col = DL.getCol();
}- The project uses conventional commits checked by CI
- Format:
type(scope): description - Types:
feat,fix,chore,test,docs,refactor - Always run
format-checkbefore pushing