Skip to content

Commit ffb6a57

Browse files
committed
docs: add documentation for our coding style
For years, we've repeatedly had confusion about what our actual coding style is not only for newcomers, but also across the core contributors. This can mostly be attributed to the fact that we do not have any coding conventions written down. This is now a thing of the past with the introduction of a new document that gives an initial overview of our style and most important best practices for both our C codebase as well as for CMake. While the proposed coding style for our C codebase should be rather uncontroversial, the coding style for CMake might be. This can be attributed to multiple facts. First, the CMake code base doesn't really have any uniform coding style and is quite outdated in a lot of places. Second, the proposed coding style actually breaks with our existing one: we currently use all-uppercase function names and variables, but the documented coding style says we use all-lowercase function names but all-uppercase variables. It's common practice in CMake to write variables in all upper-case, and in fact all variables made available by CMake are exactly that. As variables are case-sensitive in CMake, we cannot and shouldn't break with this. In contrast, function calls are case insensitive, and modern CMake always uses all-lowercase ones. I argue we should do the same to get in line with other codebases and to reduce the likelihood of repetitive strain injuries. So especially for CMake, the proposed coding style says something we don't have yet. I'm fine with that, as the document explicitly says that it's what we want to have and not what we have right now.
1 parent ad341eb commit ffb6a57

File tree

1 file changed

+364
-0
lines changed

1 file changed

+364
-0
lines changed

docs/coding-style.md

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
# libgit2 Coding Style
2+
3+
This documentation describes the preferred coding style for the libgit2 project.
4+
While not all parts of our code base conform to this coding style, the outlined
5+
rules are what we aim for.
6+
7+
Note that in no case do we accept changes that convert huge parts of the code
8+
base to use our coding style. Instead, it is encouraged to modernize small parts
9+
of code you're going to modify anyway for a given change you want to introduce.
10+
A good rule to follow is the Boy Scout Rule: "Leave the campground cleaner than
11+
you found it."
12+
13+
## C Coding Style
14+
15+
The following sections define the coding style for all code files and headers.
16+
17+
### Indentation and Alignment
18+
19+
Code is indented by tabs, where a tab is 8 spaces. Each opening scope increases
20+
the indentation level.
21+
22+
```c
23+
int foobar(int void)
24+
{
25+
if (condition)
26+
doit();
27+
/* Body */
28+
}
29+
```
30+
31+
Switch statements have their `case`s aligned with the `switch` keyword. Case
32+
bodies are indented by an additional level. Case bodies should not open their
33+
own scope to declare variables.
34+
35+
```c
36+
switch (c) {
37+
case 'a':
38+
case 'b':
39+
return 0;
40+
default:
41+
return -1;
42+
}
43+
```
44+
45+
Multi-line conditions should be aligned with the opening brace of the current
46+
statement:
47+
48+
```c
49+
if (one_very_long_condition(c) &&
50+
another_very_long_condition(c))
51+
doit();
52+
```
53+
54+
### Spaces
55+
56+
There must be no space between the function and its arguments, arguments must be
57+
separated by a space:
58+
59+
```c
60+
int doit(int first_arg, int second_arg);
61+
doit(1, 2);
62+
```
63+
64+
For any binary or ternary operators, the arguments and separator must be
65+
separated by a space:
66+
67+
```c
68+
1 + 2;
69+
x ? x : NULL;
70+
```
71+
72+
Unary operators do not have a space between them and the argument they refer to:
73+
74+
```c
75+
*c
76+
&c
77+
```
78+
79+
The `sizeof` operator always must not have a space and must use braces around
80+
the type:
81+
82+
```
83+
sizeof(int)
84+
```
85+
86+
There must be a space after the keywords `if`, `switch`, `case`, `do` and
87+
`while`.
88+
89+
### Braces
90+
91+
Functions must have their opening brace on the following line:
92+
93+
```c
94+
void foobar(void)
95+
{
96+
doit();
97+
}
98+
```
99+
100+
For conditions, braces should be placed on the same line as the condition:
101+
102+
```c
103+
if (condition(c)) {
104+
doit();
105+
dothat();
106+
}
107+
108+
while (true) {
109+
doit();
110+
}
111+
```
112+
113+
In case a condition's body has a single line, only, it's allowed to omit braces,
114+
except if any of its `else if` or `else` branches has more than one line:
115+
116+
```c
117+
if (condition(c))
118+
doit();
119+
120+
if (condition(c))
121+
doit();
122+
else if (other_condition(c))
123+
doit();
124+
125+
/* This example must use braces as the `else if` requires them. */
126+
if (condition(c)) {
127+
doit();
128+
} else if (other_condition(c)) {
129+
doit();
130+
dothat();
131+
} else {
132+
abort();
133+
}
134+
```
135+
136+
### Comments
137+
138+
Comments must use C-style `/* */` comments. C++-style `// `comments are not
139+
allowed in our codebase. This is a strict requirement as libgit2 tries to be
140+
compliant with the ISO C90 standard, which only allows C-style comments.
141+
142+
Single-line comments may have their opening and closing tag on the same line:
143+
144+
```c
145+
/* This is a short comment. */
146+
```
147+
148+
For multi-line comments, the opening and closing tag should be empty:
149+
150+
```c
151+
/*
152+
* This is a rather long and potentially really unwiedly but informative
153+
* multiline comment that helps quite a lot.
154+
*/
155+
```
156+
157+
Public functions must have documentation that explain their usage, internal
158+
functions should have a comment. We use Docurium to generate documentation
159+
derived from these comments, which uses syntax similar to Doxygen. The first
160+
line should be a short summary of what the function does. More in-depth
161+
explanation should be separated from that first line by an empty line.
162+
Parameters and return values should be documented via `@return` and `@param`
163+
tags:
164+
165+
```c
166+
/*
167+
* Froznicate the string.
168+
*
169+
* Froznicate the string by foobaring its internal structure into a more obvious
170+
* translation. Note that the returned string is a newly allocated string that
171+
* shall be `free`d by the caller.
172+
*
173+
* @param s String to froznicate
174+
* @return A newly allocated string or `NULL` in case an error occurred.
175+
* /
176+
char *froznicate(const char *s);
177+
```
178+
179+
### Variables
180+
181+
Variables must be declared at the beginning of their scope. This is a strict
182+
requirement as libgit2 tries to be compliant with the ISO C90 standard, which
183+
forbids mixed declarations and code:
184+
185+
```c
186+
void foobar(void)
187+
{
188+
char *c = NULL;
189+
int a, b;
190+
191+
a = 0;
192+
b = 1;
193+
194+
return c;
195+
}
196+
```
197+
198+
### Naming
199+
200+
Variables must have all-lowercase names. In case a variable name has multiple
201+
words, words should be separated by an underscore `_` character. While
202+
recommended to use descriptive naming, common variable names like `i` for
203+
indices are allowed.
204+
205+
All public functions must have a `git` prefix as well as a prefix indicating
206+
their respective subsystem. E.g. a function that opens a repository should be
207+
called `git_repository_open()`. Functions that are not public but declared in
208+
an internal header file for use by other subsystems should follow the same
209+
naming pattern. File-local static functions must not have a `git` prefix, but
210+
should have a prefix indicating their respective subsystem.
211+
212+
All structures declared in the libgit2 project must have a `typedef`, we do not
213+
use `struct type` variables. Type names follow the same schema as functions.
214+
215+
### Error Handling
216+
217+
The libgit2 project mostly uses error codes to indicate errors. Error codes are
218+
always of type `int`, where `0` indicates success and a negative error code
219+
indicates an error case. In some cases, positive error codes may be used to
220+
indicate special cases. Returned values that are not an error code should be
221+
returned via an out parameter. Out parameters must always come first in the list
222+
of arguments.
223+
224+
```c
225+
int doit(const char **out, int arg)
226+
{
227+
if (!arg)
228+
return -1;
229+
*out = "Got an argument";
230+
return 0;
231+
}
232+
```
233+
234+
To avoid repetitive and fragile error handling in case a function has resources
235+
that need to be free'd, we use `goto out`s:
236+
237+
```c
238+
int doit(char **out, int arg)
239+
{
240+
int error = 0;
241+
char *c;
242+
243+
c = malloc(strlen("Got an argument") + 1);
244+
if (!c) {
245+
error = -1;
246+
goto out;
247+
}
248+
249+
if (!arg) {
250+
error = -1;
251+
goto out;
252+
}
253+
254+
strcpy(c, "Got an argument")
255+
*out = c;
256+
257+
out:
258+
if (error)
259+
free(c);
260+
return error;
261+
}
262+
```
263+
264+
When calling functions that return an error code, you should assign the error
265+
code to an `error` variable and, in case an error case is indicated and no
266+
custom error handling is required, return that error code:
267+
268+
```c
269+
int foobar(void)
270+
{
271+
int error;
272+
273+
if ((error = doit()) < 0)
274+
return error;
275+
276+
return 0;
277+
}
278+
```
279+
280+
When doing multiple function calls where all of the functions return an error
281+
code, it's common practice to chain these calls together:
282+
283+
```c
284+
int doit(void)
285+
{
286+
int error;
287+
288+
if ((error = dothis()) < 0 ||
289+
(error = dothat()) < 0)
290+
return error;
291+
292+
return 0;
293+
}
294+
```
295+
296+
## CMake Coding Style
297+
298+
The following section defines the coding style for our CMake build system.
299+
300+
### Indentation
301+
302+
Code is indented by tabs, where a tab is 8 spaces. Each opening scope increases
303+
the indentation level.
304+
305+
```cmake
306+
if(CONDITION)
307+
doit()
308+
endif()
309+
```
310+
311+
### Spaces
312+
313+
There must be no space between keywords and their opening brace. While this is
314+
the same as in our C codebase for function calls, this also applies to
315+
conditional keywords. This is done to avoid the awkward-looking `else ()`
316+
statement.
317+
318+
```cmake
319+
if(CONDITION)
320+
doit()
321+
else()
322+
dothat()
323+
endif()
324+
```
325+
326+
### Case
327+
328+
While CMake is completely case-insensitive when it comes to function calls, we
329+
want to agree on a common coding style for this. To reduce the danger of
330+
repetitive strain injuries, all function calls should be lower-case (NB: this is
331+
not currently the case yet, but introduced as a new coding style by this
332+
document).
333+
334+
Variables are written all-uppercase. In contrast to functions, variables are
335+
case-sensitive in CMake. As CMake itself uses upper-case variables in all
336+
places, we should follow suit and do the same.
337+
338+
Control flow keywords must be all lowercase. In contrast to that, test keywords
339+
must be all uppercase:
340+
341+
```cmake
342+
if(NOT CONDITION)
343+
doit()
344+
elseif(FOO AND BAR)
345+
dothat()
346+
endif()
347+
```
348+
349+
### Targets
350+
351+
CMake code should not use functions that modify the global scope but prefer
352+
their targeted equivalents, instead. E.g. instead of using
353+
`include_directories()`, you must use `target_include_directories()`. An
354+
exception to this rule is setting up global compiler flags like warnings or
355+
flags required to set up the build type.
356+
357+
### Dependencies
358+
359+
Dependencies should not be discovered or set up in the main "CMakeLists.txt"
360+
module. Instead, they should either have their own module in our top-level
361+
"cmake/" directory or have a "CMakeLists.txt" in their respective "deps/"
362+
directory in case it is a vendored library. All dependencies should expose
363+
interface library targets that can be linked against with
364+
`target_link_libraries()`.

0 commit comments

Comments
 (0)