Splint - Secure Programming Lint |
info@splint.org |
Manual Contents - Other Formats | Section: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E Sponsors - Credits |
12 Naming Conventions
Naming conventions tend to be a religious issue. Generally, it doesn't matter too much what naming convention is followed as long as one is chosen and followed religiously. There are two kinds of naming conventions supported by Splint. Type-based naming conventions (Section 12.1) constrain identifier names according to the abstract types that are accessible where the identifier is defined. Prefix naming conventions (Section 12.2) constrain the initial characters of identifier names according to what is being declared and its scope. Naming conventions may be combined or different conventions may be selected for different kinds of identifiers. In addition, Splint supports checking that names do not conflict with names reserved for the standard library or implementation (Section 12.3) and are sufficiently distinguishable from other names.
12.1 Type-Based Naming Conventions
Generic naming conventions constrain valid names of identifiers. By limiting valid names, namespaces may be preserved and programs may be more easily understood since the name gives clues as to how and where the name is defined and how it should be used.
Names may be constrained by the scope of the name (external, file static, internal), the file in which the identifier is defined, the type of the identifier, and global constraints.
12.1.1 Czech Names
Czech[17] names denote operations and variables of abstract types by preceding the names by <type>_. The remainder of the name should begin with a lowercase character, but may use any other character besides the underscore. Types may be named using any non-underscore characters.
The Czech naming convention is selected by the czech flag. If access-czech is on, a function, variable, constant or iterator named <type>_<name> has access to the abstract type <type>. Reporting of violations of the Czech naming convention is controlled by different flags depending on what is being declared:
czech-fcns
Functions and iterators. An error is reported for a function name of the form <prefix> _<name> where <prefix> is not the name of an accessible type. Note that if accessczech is on, a type named <prefix> would be accessible in a function beginning with <prefix> _. If access-czech is off, an error is reported instead. An error is reported for a function name that does not have an underscore if any abstract types are accessible where the function is defined.
czech-vars
czech-constants
czech-macros
Variables, constants and expanded macros. An error is reported if the identifier name starts with <prefix> _and prefix is not the name of an accessible abstract type, or if an abstract type is accessible and the identifier name does not begin with <type>_ where type is the name of an accessible abstract type. If access-czech is on, the representation of the type is visible in the constant or variable definition.
czech-types
User-defined types. An error is reported if a type name includes an underscore character.
12.1.2 Slovak Names
Slovak names are similar to Czech names, except they are spelled differently. A Slovak name is of the form <type><Name>. The type prefix may not use uppercase characters. The remainder of the name starts with the first uppercase character.
The slovak flag selects the Slovak naming convention. Like Czech names, it may be used with access-slovak to control access to abstract representations. The slovak-fcns, slovak-vars, slovak-constants, and slovak-macros flags are analogous to the similar Czech flags. If slovak-type is on, an error is reported if a type name includes an uppercase letter.
12.1.3 Czechoslovak Names
Czechoslovak names are a combination of Czech names and Slovak names. Operations may be named either <type>_ followed by any sequence of non-underscore characters, or <type> followed by an uppercase letter and any sequence of characters. Czechoslovak names have been out of favor since 1993, but may be necessary for checking legacy code. The czechoslovak-fcns, czechoslovak-vars, czechoslovak-macros, and czechoslovak-constants flags are analogous to the similar Czech flags. If czechoslovak-type is on, an error is reported if a type name contains either an uppercase letter or an underscore character.
12.2 Namespace Prefixes
Another way to restrict names is to constrain the leading character sequences of various kinds of identifiers. For example, the names of all user-defined types might begin with T followed by an uppercase letter and all file static names begin with an uppercase letter. This may be useful for enforcing a namespace (e.g., all names exported by the X-windows library should begin with X) or just making programs easier to understand by establishing an enforced convention. Splint can be used to constrain identifiers in this way to detect identifiers inconsistent with prefixes.
All namespace flags are of the form, -<context>prefix <string>. For example, the macro variable namespace restricting identifiers declared in macro bodies to be preceded by m_ would be selected by -macrovarprefix "m_". The string may contain regular characters that may appear in a C identifier. These must match the initial characters of the identifier name. In addition, special characters (shown in Figure 23) can be used to denote a class of characters.[18] The * character may be used at the end of a prefix string to specify the rest of the identifier is zero or more characters matching the character immediately before the *. For example, the prefix string T&* matches T or TWINDOW but not Twin.
Different prefixes can be selected for the following identifier contexts:
macro-var-prefix
Any variable declared inside a macro body
unchecked-macro-prefix
Any macro that is not checked as a function or constant (see Section 11.4)
tag-prefix
Tags for struct, union and enum declarations
enum-prefix
Members of enum types
type-prefix
Name of a user-defined type
file-static-prefix
Any identifier with file static scope
glob-var-prefix
Any variable (not of function type) with global scope
const-prefix
Any constant (see Section 11.1)
iter-prefix
An iterator (see Section 11.4)
proto-param-prefix
A parameter in a function declaration prototype
external-prefix
Any exported identifier
If an identifier is in more than one of the namespace contexts, the most specific defined namespace prefix is used (e.g., a global variable is also an exported identifier, so if global-var-prefix is set, it is checked against the variable name; if not, the identifier is checked against the external-prefix.)
For each prefix flag, a corresponding flag named <prefixname> exclude controls whether errors are reported if identifiers in a different namespace match the namespace prefix. For example, if macro-var-prefix-exclude is on, Splint checks that no identifier that is not a variable declared inside a macro body uses the macro variable prefix.
Here is a (somewhat draconian) sample naming convention:
-unchecked-macro-prefix "~*"
Unchecked macros have no lowercase letters.
-type-prefix "T^&*"
All type names begin with T followed by an uppercase letter. The rest of the name is all lowercase letters.
+type-prefix-exclude
No identifier that does not name a user-defined type name begins with the type name prefix.
-file-static-prefix "^&&&"
File static scope variables begin with an uppercase letter and three lowercase letters.
-proto-param-prefix "p_"
All parameters in prototypes must begin with p_.
-glob-var-prefix "G"
All global variables start with G.
+glob-var-prefix-exclude
No identifier that is not a global variable starts with G.
The prefix for parameters in function prototypes is useful for making sure parameter names are not in conflict with macros defined before the function prototype. In most cases, it may be preferable to not name prototype parameters. If the proto-param-name flag is set, an error is reported for any named parameter in a prototype declaration. If a proto-param-prefix is set, no error is reported for unnamed parameters.
It may also be useful to check the names of prototype parameters correspond to the names in definitions. While using header files as documentation is not generally recommended, it is common enough practice that it makes sense to check that parameter names are consistent. A discrepancy may indicate an error in the parameter order in the function prototype. If proto-param-match is set, Splint will report an error if the name of a definition parameter does not match the corresponding prototype parameter (after removing the protoparamprefix).
^
Any uppercase letter, A-Z
&
Any lowercase letter, a-z
%
Any character that is not an uppercase letter (allows lowercase letters, digits and underscore)
~
Any character that is not a lowercase letter (allows uppercase letters, digits and underscore)
$
Any letter (a-z, A-Z)
/
Any letter or digit (A-Z, a-z, 0-9)
?
Any character valid in a C identifier
#
Any digit, 0-9
Figure 23. Prefix Character Codes
12.3 Naming Restrictions
Additional naming restrictions can be used to check that names do no conflict with names reserved for the standard library, and that identifier are sufficiently distinct (either for the compiler and linker, or for the programmer.) Restrictions may be different for names that are needed by the linker (external names) and names that are only needed during compilations (internal names). Names of non-static functions and global variables are external; all other names are internal.
12.3.1 Reserved Names
Many names are reserved for the implementation and standard library. A complete list of reserved names can be found in [vdL, p. 126-128]. Some name prefixes such as str followed by a lowercase character are reserved for future library extensions. Most C compilers do not detect naming conflicts, and they can lead to unpredictable program behavior. If ansi-reserved is on, Splint warns about external names that conflict with reserved names. If ansi-reserved-internal is on, warnings are also produced for internal names.
If +cpp-names is set, Splint warns about identifier names that are keywords or reserved words in C++. This is useful if the code may later be compiled with a C++ compiler (of course, this is not enough to ensure the meaning of the code is not changed when it is compiled as C++.)
12.3.2 Distinct Names
Splint can check that names differ within a given number of characters, optionally ignoring alphabetic case and differences between characters that look similar. The number of significant characters may be different for external and internal names.
Using +distinct-external-names sets the number of significant characters for external names to six and makes alphabetical case insignificant for external names. This is the minimum significance acceptable in an ANSI-conforming compiler. Most modern compilers exceed these minimums (which are particularly hard to follow if one uses the Czech or Slovak naming convention). The number of significant characters can be changed using the external-name-length <number> flag. If external-name-case-insensitive is on, alphabetical case is ignored in comparing external names. Splint reports identifiers that differ only in alphabetic case.
For internal identifiers, a conforming compiler must recognize at least 31 characters and treat alphabetical cases distinctly. Nevertheless, it may still be useful to check that internal names are more distinct then required by the compiler to minimize the likelihood that identifiers are confused in the program. Analogously to external names, the internal-name-length <number> flag sets the number of significant characters in an internal name and internal-name-case-insensitive sets the case sensitivity. The internal-name-look-alike flag further restricts distinctions between identifiers. When set, similar-looking characters match — the lowercase letter l matches the uppercase letter I and the number 1; the letter O or o matches the number 0; 5 matches S; and 2 matches Z. Identifiers that are not distinct except for look-alike characters will produce an error message. External names are also internal names, so they must satisfy both the external and internal distinct identifier checks. Figure 24 provides some examples of distinct name checking.
names.c
Running Splint
char *stringrev (char *s);
3 int f (int x)
{
5 int lookalike = 1;
6 int looka1ike = 2;
if (x > 3)
{
10 int x = lookalike;
x += looka1ike;
}
return x;
}
> splint names.c +distinctinternalnames
+internalnamelookalike +isoreserved
names.c:1: Name stringreverse is reserved for future
library extensions. Functions that begin with
"str" and a lowercase letter may be added to
<stdlib.h> or <string.h>. (ISO99:7.26.9)
names.c:6: Internal identifier looka1ike is not
distinguishable from lookalike except by lookalike
characters
names.c:5: Declaration of lookalike
names.c:10: Variable x shadows outer declaration
names.c:3: Previous declaration of x: int
Figure 24. Distinct Names
Next: 13. Completeness
Return to Contents