initial commit

This commit is contained in:
Ayman El Didi 2026-01-08 22:51:45 -07:00
commit 8385bbb030
7 changed files with 562 additions and 0 deletions

297
.clang-format Normal file
View file

@ -0,0 +1,297 @@
---
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: true
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AllowShortNamespacesOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AttributeMacros:
- __capability
BinPackArguments: true
BinPackLongBracedList: true
BinPackParameters: BinPack
BitFieldColonSpacing: Both
BracedInitializerIndentWidth: -1
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTemplateCloser: false
BreakBeforeTernaryOperators: true
BreakBinaryOperations: Never
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: MultiLine
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
EnumTrailingComma: Leave
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExportBlock: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: true
AtStartOfFile: true
KeepFormFeed: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
OneLineFormatOffRegex: ''
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakBeforeMemberAccess: 150
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: Always
RemoveBracesLLVM: false
RemoveEmptyLinesInUnwrappedLines: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes:
Enabled: true
IgnoreCase: false
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterOperatorKeyword: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterNot: false
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
WrapNamespaceBodyWithEmptyLines: Leave
...

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

34
AGENTS.md Normal file
View file

@ -0,0 +1,34 @@
When you are asked to write code, do so in a minimal way. Try to emphasize
clarity of expression in code above verbosity. For example, An array index used
on every line of a loop needn't be named any more elaborately than i. Saying
index or elementnumber is more to type (or calls upon your text editor) and
obscures the details of the computation. As in all other aspects of readable
programming, consistency is important in naming. If you call one variable
maxphysaddr, don't call its cousin lowestaddress. Whenever there's a convention
for something, follow it. The code should strive to be simple enough to be
obviously bug free.
When code appears non-trivial, include a comment above the non-trivial section
explaining the "why", not the "what" behind the code, with any references where
a developer can read more about why code does something.
Be critical of any and all code, but whenever it makes sense do things the way
we do it in other places throughout our codebase. Whenever you see something
suspicious or shoddy, you should leave a comment marking it as so, and explain
why. Each of these such comments should be marked with a `TODO`, explaining
briefly what the issue is, and what might need to be done to fix it. Do not try
to address many things at once, but always keep them in mind.
Whenever a requirement is unclear, you should ask for clarification.
clang-format is available, and should always be used to format code before
considering it completed. clang-tidy is available, and should also be used.
Code with any lint warnings or errors should not be considered complete. Some
lint warnings are not really relevant, and if one seems to require some
arbitrary fix which doesn't affect the quality or robustness of the code we
will remove it, however you should always ask before disabling any checks.
CMake is used to build the project. The build directory is called `build`, and
is likely already generated. You can regenerate it if needed, but this fetches
and build LLVM, which is a very large dependency and takes a long time to
compile so you should try to avoid doing so.

83
CMakeLists.txt Normal file
View file

@ -0,0 +1,83 @@
cmake_minimum_required(VERSION 3.20)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
project(errorck LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(REQUIRED_LLVM_VERSION "18.1.8" CACHE STRING "Required LLVM/Clang version")
set(LLVM_ROOT "" CACHE PATH "Prefix containing lib/cmake/llvm and lib/cmake/clang")
set(LLVM_TAG "llvmorg-18.1.8" CACHE STRING "llvm-project git tag/commit to fetch")
if(LLVM_ROOT)
list(PREPEND CMAKE_PREFIX_PATH "${LLVM_ROOT}")
endif()
find_package(LLVM CONFIG QUIET)
find_package(Clang CONFIG QUIET)
if(LLVM_FOUND AND DEFINED LLVM_PACKAGE_VERSION AND NOT (LLVM_PACKAGE_VERSION VERSION_EQUAL REQUIRED_LLVM_VERSION))
if(LLVM_ROOT)
message(FATAL_ERROR "Found LLVM ${LLVM_PACKAGE_VERSION} at LLVM_ROOT, but required ${REQUIRED_LLVM_VERSION}.")
else()
set(LLVM_FOUND FALSE)
set(Clang_FOUND FALSE)
endif()
endif()
if(NOT (LLVM_FOUND AND Clang_FOUND))
include(FetchContent)
FetchContent_Declare(
llvm_project
GIT_REPOSITORY https://github.com/llvm/llvm-project.git
GIT_TAG ${LLVM_TAG}
SOURCE_SUBDIR llvm
)
set(LLVM_ENABLE_PROJECTS "clang;clang-tools-extra" CACHE STRING "" FORCE)
FetchContent_MakeAvailable(llvm_project)
endif()
add_executable(errorck "${CMAKE_CURRENT_LIST_DIR}/main.cpp")
target_compile_options(errorck PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-Wall -Wextra>
$<$<CXX_COMPILER_ID:MSVC>:
/W4>)
target_include_directories(errorck PRIVATE ${LLVM_INCLUDE_DIRS})
target_compile_definitions(errorck PRIVATE ${LLVM_DEFINITIONS})
if(DEFINED CLANG_INCLUDE_DIRS)
target_include_directories(errorck PRIVATE ${CLANG_INCLUDE_DIRS})
endif()
if(NOT (LLVM_FOUND AND Clang_FOUND))
target_include_directories(errorck PRIVATE
${LLVM_SOURCE_DIR}/include
${LLVM_BINARY_DIR}/include
${LLVM_EXTERNAL_CLANG_SOURCE_DIR}/include
${LLVM_BINARY_DIR}/tools/clang/include
)
endif()
if(TARGET clang-cpp)
target_link_libraries(errorck PRIVATE clang-cpp)
else()
target_link_libraries(errorck PRIVATE
clangTooling
clangFrontend
clangSerialization
clangDriver
clangParse
clangSema
clangAnalysis
clangRewrite
clangAST
clangASTMatchers
clangEdit
clangLex
clangBasic
)
endif()

10
LICENSE Normal file
View file

@ -0,0 +1,10 @@
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

118
README.md Normal file
View file

@ -0,0 +1,118 @@
`errorck` is a Clang libToolingbased static analysis tool for measuring how
often error-returning functions are ignored, partially handled, or handled
with future-proof catch-all logic in real-world C and C++ codebases.
It is intended for large-scale, empirical analysis across many projects,
not as an interactive linter.
## Overview
`errorck` analyzes calls to a fixed set of error-returning functions
(hardcoded in the source for now) and classifies how each calls return
value is handled. It also detects simple one-layer wrapper functions that
forward error codes (e.g. project-specific allocation wrappers) and treats
calls through those wrappers equivalently.
The tool emits machine-readable output suitable for ingestion into a
database and later statistical analysis.
## What `errorck` detects
For each call to a watched function (or trivial wrapper), `errorck` classifies
the handling into one of the following categories:
ignored
The return value is discarded and not read.
assigned_not_read
The return value is assigned to a local variable but never read,
branched on, or returned.
branched_no_catchall
The return value is used in an if or switch, but there is no
else/default branch to handle future error cases.
branched_with_catchall
The return value is branched on and includes an else or default.
propagated
The return value is returned directly to the caller.
used_other
The return value is used in some other way (logging, passed to
another function, etc.).
For reporting purposes, the following are considered “ignored” error
conditions:
- ignored
- assigned_not_read
- branched_no_catchall
## Trivial wrapper detection
`errorck` detects one layer of trivial wrappers around watched functions.
A function is considered a trivial wrapper if it:
- returns a watched function call directly, or
- assigns the result of a watched function call to a local variable,
optionally branches or logs based on that value, and then returns the
value unchanged.
Wrappers are reported explicitly, and calls through wrappers are attributed
to the underlying base function.
## Limitations
`errorck` deliberately trades completeness for scalability and clarity.
Current limitations include:
- One-layer wrapper detection only
- No interprocedural dataflow
- No function pointer resolution
- Simplified wrapper body patterns (single return, single result variable)
- Catch-all detection limited to else/default
- Analysis is per-translation-unit
These limitations are documented and must be considered when interpreting
results.
## Building
`errorck` requires LLVM/Clang with libTooling.
A minimal build using llvm-config:
$ c++ -std=c++17 `errorck`.cpp -o `errorck` \
`llvm-config --cxxflags --ldflags --system-libs \
--libs core clangTooling clangFrontend clangAST clangBasic`
Alternatively, a CMake build can be used.
## Running
`errorck` requires a compilation database.
$ `errorck` -p /path/to/build file1.c file2.cpp ...
Output is written to stdout as JSON Lines (one record per line).
Example:
$ `errorck` -p . src/*.c > results.jsonl
## Intended use
`errorck` is designed for:
- empirical studies of error handling
- large-scale analysis across many repositories
- research and auditing, not enforcement
It is not intended to replace compiler warnings or linters.
## License
Public domain, Unlicense, 0BSD, or CC0. Whichever you prefer. The 0BSD license
is included in the source distribution.

19
main.cpp Normal file
View file

@ -0,0 +1,19 @@
#include <cstdlib>
#include "clang/Tooling/CommonOptionsParser.h"
#include "llvm/Support/CommandLine.h"
using namespace clang::tooling;
using namespace llvm;
static cl::OptionCategory Category("errorck options");
int main(int argc, const char **argv) {
auto pRes = CommonOptionsParser::create(argc, argv, Category);
if (!pRes) {
llvm::errs() << pRes.takeError();
return EXIT_FAILURE;
}
CommonOptionsParser &OptionsParser = pRes.get();
}