commit 8385bbb03081746d02297f7fce62f51ccd54a691 Author: Ayman El Didi Date: Thu Jan 8 22:51:45 2026 -0700 initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e9ca3f7 --- /dev/null +++ b/.clang-format @@ -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 +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..466dc13 --- /dev/null +++ b/AGENTS.md @@ -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. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2fe6d99 --- /dev/null +++ b/CMakeLists.txt @@ -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 + $<$,$,$>: + -Wall -Wextra> + $<$: + /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() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5aec258 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4427414 --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +`errorck` is a Clang libTooling–based 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 call’s 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. diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a0d45b6 --- /dev/null +++ b/main.cpp @@ -0,0 +1,19 @@ +#include + +#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(); +}