CertC-DCL01

Do not reuse variable names in subscopes

Required inputs: IR

Do not use the same variable name in two scopes where one scope is contained in another. For example,

  • No other variable should share the name of a global variable if the other variable is in a subscope of the global variable.
  • A block should not declare a variable with the same name as a variable declared in any block that contains it.

Reusing variable names leads to programmer confusion about which variable is being modified. Additionally, if variable names are reused, generally one or both of the variable names are too generic.

Noncompliant Code Example

This noncompliant code example declares the msg identifier at file scope and reuses the same identifier to declare a character array local to the report_error() function. The programmer may unintentionally copy the function argument to the locally declared msg array within the report_error() function. Depending on the programmer's intention, it either fails to initialize the global variable msg or allows the local msg buffer to overflow by using the global value msgsize as a bounds for the local buffer.

#include <stdio.h>
 
static char msg[100];
static const size_t msgsize = sizeof( msg);

void report_error(const char *str) {
  char msg[80];
  snprintf(msg, msgsize, "Error: %s\n", str);
  /* ... */
}

int main(void) {
  /* ... */
  report_error("some error");
 
  return 0;
}
Compliant Solution

This compliant solution uses different, more descriptive variable names:

#include <stdio.h>
 
static char message[100];
static const size_t message_size = sizeof( message);

void report_error(const char *str) {
  char msg[80];
  snprintf(msg, sizeof( msg), "Error: %s\n", str);
  /* ... */
}

int main(void) {
  /* ... */
  report_error("some error");
 
  return 0;
}

When the block is small, the danger of reusing variable names is mitigated by the visibility of the immediate declaration. Even in this case, however, variable name reuse is not desirable. In general, the larger the declarative region of an identifier, the more descriptive and verbose should be the name of the identifier.

By using different variable names globally and locally, the compiler forces the developer to be more precise and descriptive with variable names.

Noncompliant Code Example

This noncompliant code example declares two variables with the same identifier, but in slightly different scopes. The scope of the identifier  i declared in the for loop's initial clause terminates after the closing curly brace of the for loop. The scope of the identifier i declared in the for loop's compound statement terminates before the closing curly brace. Thus, the inner declaration of  i hides the outer declaration of  i, which can lead to unintentionally referencing the wrong object.

void f(void) {
  for (int i = 0; i < 10; i++) {
    long i;
    /* ... */
  }
}
Compliant Solution

This compliant solution uses a unique identifier for the variable declared within the  for loop.

void f(void) {
  for (int i = 0; i < 10; i++) {
    long j;
    /* ... */
  }
}
Exceptions

DCL01-C-EX1: A function argument in a function declaration may clash with a variable in a containing scope provided that when the function is defined, the argument has a name that clashes with no variables in any containing scopes.

extern int name;
void f(char *name);  /* Declaration: no problem here */
/* ... */
void f(char *arg) {  /* Definition: no problem; arg doesn't hide name */
  /* Use arg */
}

DCL01-C-EX2: A temporary variable within a new scope inside of a macro can override an identifier in a containing scope. However,this exception does not apply to to the arguments of the macro itself.

#define SWAP(type, a, b) do { type tmp = a; a = b; b = tmp; } while(0)
 
void func(void) {
  int tmp = 100;
  int a = 10, b = 20;
  SWAP(int, a, b); /* Hidden redeclaration of tmp is acceptable */
  SWAP(int, tmp, b); /* NONCOMPLIANT: Hidden redeclaration of tmp clashes with argument */
}
Risk Assessment

Reusing a variable name in a subscope can lead to unintentionally referencing an incorrect variable.

Recommendation Severity Likelihood Remediation Cost Priority Level
DCL01-C Low Unlikely Medium P2 L3
Related Guidelines
SEI CERT C++ Coding Standard VOID DCL01-CPP. Do not reuse variable names in subscopes
MISRA C:2012 Rule 5.3 (required)
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/recommendations/declarations-and-initialization-dcl/dcl01-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

hiding

{} hides {}

None

False

Options

allow_hiding_if_condition_in_else

allow_hiding_if_condition_in_else : bool = False

Allow hiding a variable declared in the condition of an if-statement within the else block. This is useful to allow re-use of the same variable name in a chain of else-if using C++17 variable declarations in the if-statement:
  if (auto x = get(1); x.ok()) {}
  else if (auto x = get(2); x.ok()) {}
This option causes the rule to act as if variables declared in the if condition were only visible in the then-branch.
 

allow_in_classes

allow_in_classes : bool = False

Whether identifiers in local classes can hide surrounding ones.
 

allow_in_lambdas

allow_in_lambdas : bool = False

Whether identifiers in lambdas can hide surrounding ones.
 

allow_in_namespaces

allow_in_namespaces : bool = False

Whether identifiers in namespaces can hide surrounding ones.
 

allow_parameter_in_function_declaration

allow_parameter_in_function_declaration : bool = True

Whether identifiers of parameters in function declarations may hide surrounding ones, given the function declaration uses a different identifier for the same parameter that does not hide a surrounding one.
 

check_inheritance_hiding

check_inheritance_hiding : bool = False

Whether to check for hiding by inheritance.
 

check_nested_classes

check_nested_classes : bool = False

Whether symbols from inner classes should be reported as hiding symbols of outer classes.
 

constructor_parameters_not_hiding_fields_in_initializer_list

constructor_parameters_not_hiding_fields_in_initializer_list : bool = False

Consider use of constructor parameters in the constructor initializer list not as hiding, when parameter name is also a class field name.
 

distinguish_name_spaces

distinguish_name_spaces : bool = False

Whether C name spaces should be distinguished.
 

hiding_entities

hiding_entities : set[bauhaus.ir.LIR_Class_Name] = {'Field', 'Parameter', 'Typedef_Type', 'Variable'}

LIR node types to consider symbols that hide other symbols.
 

maxlen

maxlen : int | None = None

Number of significant characters (or None).
 

tolerate_constructor_parameters_hiding_fields

tolerate_constructor_parameters_hiding_fields : bool = True

Allow constructor parameters to hide fields.
 

tolerate_function_prototype_argument_hiding

tolerate_function_prototype_argument_hiding : bool = True

Whether to tolerate arguments of a function prototype, that itself is a function argument, that have the same identifier as a argument of the outer function. If true, the rule will still report cases, where argument identifiers match up and the function prototype refers to arguments of the outer function (e.g. by decltype()).
 

tolerate_hiding_private_members

tolerate_hiding_private_members : bool = False

Whether to tolerate hiding private members of base classes.
 

tolerate_macro_local_identifier

tolerate_macro_local_identifier : bool = True

Whether an identifier being local to a macro is allowed to hide identifiers from outside the macro