thodg/libgit2/docs/coding-style.md

Download

libgit2 Coding Style

This documentation describes the preferred coding style for the libgit2 project. While not all parts of our code base conform to this coding style, the outlined rules are what we aim for.

Note that in no case do we accept changes that convert huge parts of the code base to use our coding style. Instead, it is encouraged to modernize small parts of code you’re going to modify anyway for a given change you want to introduce. A good rule to follow is the Boy Scout Rule: “Leave the campground cleaner than you found it.”

C Coding Style

The following sections define the coding style for all code files and headers.

Indentation and Alignment

Code is indented by tabs, where a tab is 8 spaces. Each opening scope increases the indentation level.

int foobar(int void)
{
    if (condition)
        doit();
    /* Body */
}

Switch statements have their cases aligned with the switch keyword. Case bodies are indented by an additional level. Case bodies should not open their own scope to declare variables.

switch (c) {
case 'a':
case 'b':
    return 0;
default:
    return -1;
}

Multi-line conditions should be aligned with the opening brace of the current statement:

if (one_very_long_condition(c) &&
    another_very_long_condition(c))
    doit();

Spaces

There must be no space between the function and its arguments, arguments must be separated by a space:

int doit(int first_arg, int second_arg);
doit(1, 2);

For any binary or ternary operators, the arguments and separator must be separated by a space:

1 + 2;
x ? x : NULL;

Unary operators do not have a space between them and the argument they refer to:

*c
&c

The sizeof operator always must not have a space and must use braces around the type:

sizeof(int)

There must be a space after the keywords if, switch, case, do and while.

Braces

Functions must have their opening brace on the following line:

void foobar(void)
{
    doit();
}

For conditions, braces should be placed on the same line as the condition:

if (condition(c)) {
    doit();
    dothat();
}

while (true) {
    doit();
}

In case a condition’s body has a single line, only, it’s allowed to omit braces, except if any of its else if or else branches has more than one line:

if (condition(c))
    doit();

if (condition(c))
    doit();
else if (other_condition(c))
    doit();

/* This example must use braces as the `else if` requires them. */
if (condition(c)) {
    doit();
} else if (other_condition(c)) {
    doit();
    dothat();
} else {
    abort();
}

Comments

Comments must use C-style /* */ comments. C++-style //comments are not allowed in our codebase. This is a strict requirement as libgit2 tries to be compliant with the ISO C90 standard, which only allows C-style comments.

Single-line comments may have their opening and closing tag on the same line:

/* This is a short comment. */

For multi-line comments, the opening and closing tag should be empty:

/*
 * This is a rather long and potentially really unwiedly but informative
 * multiline comment that helps quite a lot.
 */

Public functions must have documentation that explain their usage, internal functions should have a comment. We use Docurium to generate documentation derived from these comments, which uses syntax similar to Doxygen. The first line should be a short summary of what the function does. More in-depth explanation should be separated from that first line by an empty line. Parameters and return values should be documented via @return and @param tags:

/*
 * Froznicate the string.
 *
 * Froznicate the string by foobaring its internal structure into a more obvious
 * translation. Note that the returned string is a newly allocated string that
 * shall be `free`d by the caller.
 *
 * @param s String to froznicate
 * @return A newly allocated string or `NULL` in case an error occurred.
 */
char *froznicate(const char *s);

Variables

Variables must be declared at the beginning of their scope. This is a strict requirement as libgit2 tries to be compliant with the ISO C90 standard, which forbids mixed declarations and code:

void foobar(void)
{
    char *c = NULL;
    int a, b;

    a = 0;
    b = 1;

    return c;
}

Naming

Variables must have all-lowercase names. In case a variable name has multiple words, words should be separated by an underscore _ character. While recommended to use descriptive naming, common variable names like i for indices are allowed.

All public functions must have a git prefix as well as a prefix indicating their respective subsystem. E.g. a function that opens a repository should be called git_repository_open(). Functions that are not public but declared in an internal header file for use by other subsystems should follow the same naming pattern. File-local static functions must not have a git prefix, but should have a prefix indicating their respective subsystem.

All structures declared in the libgit2 project must have a typedef, we do not use struct type variables. Type names follow the same schema as functions.

Error Handling

The libgit2 project mostly uses error codes to indicate errors. Error codes are always of type int, where 0 indicates success and a negative error code indicates an error case. In some cases, positive error codes may be used to indicate special cases. Returned values that are not an error code should be returned via an out parameter. Out parameters must always come first in the list of arguments.

int doit(const char **out, int arg)
{
    if (!arg)
        return -1;
    *out = "Got an argument";
    return 0;
}

To avoid repetitive and fragile error handling in case a function has resources that need to be free’d, we use goto outs:

int doit(char **out, int arg)
{
    int error = 0;
    char *c;

    c = malloc(strlen("Got an argument") + 1);
    if (!c) {
        error = -1;
        goto out;
    }

    if (!arg) {
        error = -1;
        goto out;
    }

    strcpy(c, "Got an argument")
    *out = c;

out:
    if (error)
        free(c);
    return error;
}

When calling functions that return an error code, you should assign the error code to an error variable and, in case an error case is indicated and no custom error handling is required, return that error code:

int foobar(void)
{
    int error;

    if ((error = doit()) < 0)
        return error;

    return 0;
}

When doing multiple function calls where all of the functions return an error code, it’s common practice to chain these calls together:

int doit(void)
{
    int error;

    if ((error = dothis()) < 0 ||
        (error = dothat()) < 0)
        return error;

    return 0;
}

CMake Coding Style

The following section defines the coding style for our CMake build system.

Indentation

Code is indented by tabs, where a tab is 8 spaces. Each opening scope increases the indentation level.

if(CONDITION)
    doit()
endif()

Spaces

There must be no space between keywords and their opening brace. While this is the same as in our C codebase for function calls, this also applies to conditional keywords. This is done to avoid the awkward-looking else () statement.

if(CONDITION)
    doit()
else()
    dothat()
endif()

Case

While CMake is completely case-insensitive when it comes to function calls, we want to agree on a common coding style for this. To reduce the danger of repetitive strain injuries, all function calls should be lower-case (NB: this is not currently the case yet, but introduced as a new coding style by this document).

Variables are written all-uppercase. In contrast to functions, variables are case-sensitive in CMake. As CMake itself uses upper-case variables in all places, we should follow suit and do the same.

Control flow keywords must be all lowercase. In contrast to that, test keywords must be all uppercase:

if(NOT CONDITION)
        doit()
elseif(FOO AND BAR)
        dothat()
endif()

Targets

CMake code should not use functions that modify the global scope but prefer their targeted equivalents, instead. E.g. instead of using include_directories(), you must use target_include_directories(). An exception to this rule is setting up global compiler flags like warnings or flags required to set up the build type.

Dependencies

Dependencies should not be discovered or set up in the main “CMakeLists.txt” module. Instead, they should either have their own module in our top-level “cmake/“ directory or have a “CMakeLists.txt” in their respective “deps/“ directory in case it is a vendored library. All dependencies should expose interface library targets that can be linked against with target_link_libraries().


Source

Download