comply

comply

Compliant Style Guide

Use these listings as a general guideline during development or as a reference when fixing violations.

comply is an open-source tool that helps you write better C99.

This page provides a listing of the 35 rules that comply checks your code against when running its analysis.



Download   View on GitHub

Listings are based on comply 0.13.0 and were last edited on October 25, 2018


 required   advisory   elective 

Name Description Why is this important?

identifier-too-long

Avoid exceeding 31 characters per identifier.

Identifier is too long ({length} > {max} characters) => Use a shorter name.

Identifiers should be kept as short as possible, while still retaining enough meaning that it is immediately clear what it represents- or does.

An identifier that requires more than 31 characters to provide meaning is an indication that complexity might be too high in the specific context and often presents a refactoring opportunity.

line-too-long

Don't exceed 80 characters per line.

Line is too long ({length} > {max} characters) => Use shorter identifiers or split statements to multiple lines.

Any line of code should fit on the screen it is being viewed on under any scenario; whether single file or side-by-side.

Lines that are too long can be difficult to visually comprehend, and wrapping or scrolling makes it harder to read.

Lines shorter than 80 characters will fit on most viewers, thus improving readability.

References:

func-too-long

Avoid exceeding 40 lines per function.

Function is longer than recommended ({length} > {max} lines) => This function might be too complex. Consider refactoring.

A large function can be difficult to read and easily comprehend- especially so if it requires scrolling to fully fit on the screen/display of the viewer.

Basing this rule on a 40 line maximum may seem like an arbitrary number, and while it is certainly not a scientifically proven limit, it does represent a viable breaking point where most displays will be able to keep every line visible.

Additionally, and similar to too-many-params, when a function is getting large and increasingly complex, it is also often a sign that it is doing too much and would likely benefit from being refactored into smaller parts.

References:

invisible-characters

Don't put invisible characters in code.

File contains invisible characters (found {count}) => Delete each occurrence or replace with whitespace.

Invisible characters (in code, i.e. not in literals) serve no useful purpose and may confuse both editing tools and readers.

duplicate-include

Don't include another file more than once (per file).

File already included previously => Remove duplicate #include directive.

unnamed-int

Provide meaningful names for integer parameters if able.

Unnamed integer parameter => Provide a name for this parameter.

The majority of function prototypes will suffer from having unnamed integer parameters, as their meaning might be difficult to derive without.

There are exceptions, of course; a good example is a math function such as max(int, int) where adding parameter names (e.g. int a and int b) would not add value or make it any easier to understand.

In general, however, it is almost always preferable to provide parameter names.

symbol-used

Always list used symbols as needed/required.

Not implemented.

Used symbol '{symbol}' not listed as needed => Add symbol '{symbol}' to list.

If your code is using a symbol, but not explicitly telling where it got it from, you might have a hard time figuring out just how far your code reaches out.

See require-symbols.

too-many-params

Don't exceed 4 parameters per function.

Function might be too broad ({count} > {max} parameters) => This function might be taking on too much work. Consider refactoring.

When a function has many parameters, it is often a sign that it is doing too much and would benefit from being refactored into smaller parts.

Each parameter adds to the complexity of a function, and the more it has, the harder it becomes to understand (and use).

A common practice is to bundle parameters into a struct when many parameters are absolutely necessary (a pattern commonly referred to as Parameter Object).

This practice, however, does not reduce the complexity of the function- but it does improve its readability.

redundant-const

Don't mark parameter names as const in function prototypes.

Parameter name marked const => Remove const qualifier for parameter name.

A function parameter name might be marked as const in its prototype, but implementations of that function are not required to comply with that- making it an implementation detail that should not be part of the exposed interface.

pad-braces

Always pad braced bodies with inner whitespace.

Braced body not padded with whitespace => Add a single whitespace to the {left_or_right} of '{brace}'.

missing-braces

Always surround the bodies of control statements with scoped braces.

Body of control statement not surrounded by braces => Add opening and ending braces for the body of the control statement.

You might be tempted to save a line or two by not adding braces to that single-line if statement.

However, such a decision may bite you later on, as an unsuspecting programmer may fail to notice the lack of braces and unintentionally be writing code in the wrong scope- leading to potentially undesirable or unpredictable consequences.

References:

inconsistent-name-placement

Always place function name and return type on separate lines (for function implementations).

Function name not at beginning of line => Split function name and return type to separate lines.

This style provides a quick and consistent reading of functions, and helps in reducing line length when return types are long and complicated.

too-many-blanks

Don't add more than 1 blank line, neither leading, nor following, any line of code.

Too many consecutive blank lines ({count} > {max}) => Remove excess blank lines.

Blank lines are occasionally used as a way of partitioning or grouping chunks of logically separated code, but this is not recommended.

pad-commas

Always follow comma-separators by whitespace.

Comma separator not followed by whitespace => Add a single whitespace or linebreak to the right of the comma.

prefer-stdint

Always use explicitly sized integer types (e.g. stdint.h).

'{int}' used instead of '{stdint}' => Use '{stdint}' instead of '{int}'.

Read more

Here's a general table of reference:

Integer type <stdint.h>
unsigned char uint8_t
unsigned short uint16_t
unsigned int uint32_t
unsigned long uint64_t
char int8_t
short int16_t
int int32_t
long int64_t

Being explicit about the type and size that you want to use helps improve portability.

It also increases readability as it makes types read more uniformly, and does away entirely with the unsigned and signed keywords.

It's worth noting that when sticking with basic types (e.g. int), the compiler may just do a better job than you at deciding which size is actually the optimal choice.

However, leaving that an implicit choice could result in unexpected issues down the line.

Being explicit lets you avoid making assumptions. The trade-off is potentially losing some (often neglible) performance.

References:

file-too-long

Avoid exceeding 600 lines per source file.

File might be too complex ({length} > {max} lines) => Consider refactoring or splitting into separate files.

Files that are very long can be difficult to navigate and easily comprehend, and may indicate that the file is too extensive and covering too many things.

Note that, as with func-too-long, this limit is completely arbitrary and only serves as a general indicator of complexity. Whether or not a file is actually too long is highly variable and can only be judged on a situational basis.

redundant-size

Don't specify array length in function signatures unless it can be enforced.

Size specifier has no effect => Remove size specifier '{size}'.

The size specification for pointer-degrading array parameters is not enforced by the compiler, thus making it a source of confusion that should be avoided.

For example, you could, without warning, specify one length in a function prototype, but another in the implementation.

unified-header

Don't use unified headers if you can avoid it.

Header contains no prototype declarations => Avoid having headers that only includes other headers.

A unified header is a header file whose only purpose is to include other header files.

As convenient as they may be, unified headers do not promote modularity and increases compile time in cases where the consumer does not need all of the included headers.

References:

paramless-func

Always specify parameters as void if a function implementation takes zero parameters.

Parameterless function does not specify parameters as 'void' => Add 'void' to indicate that this is a parameterless function.

Technically, this is not required for the compiler to do its job, but being explicit helps in keeping a clear and consistent interface.

require-symbols

Always list needed/required symbols.

#include directive does not list needed symbols => Add a comma-separated list with each symbol needed.

Helps in determining dependencies that are no longer needed and could be removed, and encourages use of smaller, more well-defined headers.

Fewer dependencies reduce complexity, so being able to remove an inclusion is always an improvement.

By maintaining these lists obsessively you make it much easier for yourself, and others, to determine the actual dependencies of your code.

References:

pad-keywords

Always pad control keywords (if, else, for etc.) with space on both sides.

Keyword not padded with whitespace on both sides => Add a single whitespace to the {left_or_right} of '{keyword}'.

Padding control keywords improves readability by clearly separating them from macros and function calls.

tab-characters

Don't use tabs. Use spaces.

File contains tab characters ({count} tabs) => Replace each tab with spaces (typically 4).

Using tabs for indentation will produce inconsistent line lengths, as the size of a tab may vary depending on the viewer.

References:

unused-symbol

Don't list unused symbols as needed.

Unused symbol '{symbol}' listed as needed => Remove symbol '{symbol}' from list.

Helps in determining when a symbol is no longer used, potentially leading to being able to remove an inclusion completely, reducing dependencies and improving maintainability.

See require-symbols.

filename-has-whitespace

Avoid whitespace in filenames.

Filename contains whitespace => Replace whitespaces with underscores (e.g. '{filename}').

Mostly a matter of convention, but helps in avoiding issues when interacting with the file on the command-line or through external tools.

left-aligned-const

Always place const qualifiers to the right of type declarations.

Left-aligned const qualifier => Move 'const' to the right side of the type declaration.

Placing const qualifiers to the left makes for an inconsistent reading of types.

References:

guard-header

Always provide include guards in header files.

Header does not provide an include guard => Wrap your header in an include guard named "{guard}" or use "#pragma once".

Helps prevent redundant inclusions and improves compilation times.

logical-continuation

Don't begin lines with a logical continuation.

Line begins with logical continuation => Move the logical continuation to the end of the previous line.

pad-pointers

Always pad pointer declarations with space on both sides.

Pointer declaration not padded with whitespace => Add a single whitespace to the {left_or_right} of the asterisk.

Having no padding for *'s makes for an inconsistent reading of types- especially when combined with const qualifiers.

See left-aligned-const.

Additionally, padding provides a clear separation between a declaration and a pointer dereference.

References:

including-source

Don't include source files (.c) in other source files.

Including source file => Find a way to remove the #include directive.

This is advisable to avoid potentially compiling the same unit twice, resulting in multiple symbol definitions and linker errors.

header-in-header

Don't include other headers if you can avoid it.

Header included in header => Replace '{inclusion}' with a forward-declaration for each needed type.

Avoiding header inclusions can help keep compile times low.

Forcing source files to include everything they need helps provide a clear picture on the dependencies of the particular unit and makes it easier to spot redundancies.

References:

todo

Use todo's liberally, but don't forget to deal with them.

TODO: {todo} => Consider promoting to a full report in your issue tracker.

These small notes are great for quickly persisting thoughts directly related to specific parts of your code. They serve as reminders for both yourself, and others, that something needs to be looked at eventually.

However, it is dangerous todo-and-forget; in time, these reminders may turn stale- the context might be forgotten, or the issue silently fixed- yet the todo remains.

This is a problem, because future-you may no longer understand why, or even what, is wrong. In such a case, you might not dare deleting it, rendering the todo as nothing but a source of confusion and obfuscation.

redundant-name

Don't name parameters identically to their type.

Parameter '{param}' named identically to its type '{type}' => Rename parameter '{param}' to something meaningful or omit it.

Redundant information is never useful. If a parameter can not be named something meaningful, then it is typically better omitted.

References:

padded-parens

Don't pad parenthesized groups with whitespace.

Opening or closing parenthesis padded with whitespace => Remove whitespace from the {left_or_right} side of the parenthesis.

ambiguous-func

Don't provide ambiguous function declarations.

Ambiguous function declaration => Add 'void' to indicate that this is a parameterless function.

This mainly pertains to functions with parameterless declarations.

In C, a function declaration with no parameters is ambiguous, as it implicitly declares a function that can take an arbitrary number of parameters.

scope-too-deep

Don't write deeply nested code.

Scope is too deep ({depth} > {max} levels) => Avoid nesting code too deeply. Consider refactoring.

A deeply nested scope is often an indication of too high complexity and can be difficult to read.

Made by Jacob Hauberg Hansen. Page sources are available on GitHub.