Skip to content

Code Style and Conventions

Hugo edited this page Feb 26, 2026 · 1 revision

Code Style and Conventions

Formatting

The project uses clang-format version 20 with an LLVM-based configuration.

Key Settings

  • Column limit: 100
  • Indent: 4 spaces
  • Brace style: Allman (braces on new lines)
  • No tabs

Running

# 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-check

CI fails if any file is not formatted. Always format before committing.


Language Standard

  • C++20 (CMAKE_CXX_STANDARD 20)
  • Extensions disabled (CMAKE_CXX_EXTENSIONS OFF)
  • Minimum compiler: GCC 12+ or Clang 15+

Naming Conventions

Namespaces

namespace ctrace::stack           // main namespace
namespace ctrace::stack::analysis // analysis modules
namespace ctrace::stack::cli      // CLI parsing
namespace ctrace::stack::app      // application layer

Types

  • Classes/structs: PascalCase (AnalysisConfig, FunctionResult, DiagnosticEmitter)
  • Enums: PascalCase (AnalysisMode, DiagnosticSeverity)
  • Enum values: PascalCase (AnalysisMode::IR, DiagnosticSeverity::Warning)

Functions

  • Free functions: camelCase (analyzeModule, toJson, toSarif)
  • Member functions: camelCase (isSatisfiedBy, shouldAnalyze)
  • Static helper functions: camelCase (normalizePath, filterResult)

Variables

  • Local variables: camelCase (inputFilenames, loadedModules)
  • Member variables: camelCase_ with trailing underscore (cfg_, filters_)
  • Constants: kPascalCase (kCrossTUMaxIterations, kCacheSchema)

Code Organization

Header/Source Split

  • Headers in include/ (public API) and include/analysis/, include/analyzer/, etc.
  • Implementations in src/ mirroring the include structure
  • All headers use #pragma once

File Organization

Each analysis module follows the pattern:

  1. Header declares the finding struct and the analysis function
  2. Source implements the analysis logic
  3. Module is stateless when possible (takes module/function as input, returns findings)

Include Order

  1. Corresponding header
  2. Project headers
  3. LLVM headers
  4. Standard library headers

Patterns Used

Result Types

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;
};

Specification Pattern

Used for filter logic:

class FunctionReportSpecification
{
public:
    bool isSatisfiedBy(const FunctionResult& function) const;
};

class InputExclusionSpecification
{
public:
    bool isSatisfiedBy(const std::string& inputPath) const;
};

Strategy Pattern

Used for execution and output dispatch:

class AnalysisExecutionStrategy { virtual AppStatus execute(...) = 0; };
class OutputStrategy { virtual int emit(...) = 0; };

Builder Pattern

Used for constructing the analysis run plan:

class RunPlanBuilder
{
public:
    explicit RunPlanBuilder(cli::ParsedArguments parsedArgs);
    AppResult<RunPlan> build();
};

LLVM Coding Patterns

Iterating Functions

for (const auto& F : module)
{
    if (F.isDeclaration())
        continue;
    // analyze F
}

Iterating Instructions

for (const auto& BB : function)
{
    for (const auto& I : BB)
    {
        if (auto* store = llvm::dyn_cast<llvm::StoreInst>(&I))
        {
            // handle store
        }
    }
}

Debug Info Access

if (const auto& DL = instruction.getDebugLoc())
{
    unsigned line = DL.getLine();
    unsigned col = DL.getCol();
}

Commit Guidelines

  • The project uses conventional commits checked by CI
  • Format: type(scope): description
  • Types: feat, fix, chore, test, docs, refactor
  • Always run format-check before pushing

Clone this wiki locally