This rule is part of MISRA C++:2023.
Usage of this content is governed by Sonar’s terms and conditions. Redistribution is
prohibited.
Rule 6.4.1 - A variable declared in an inner scope shall not hide a variable declared in an outer scope
Category: Required
Analysis: Decidable,Single Translation Unit
Amplification
A variable declaration [1] in an inner scope is considered to hide a variable in an outer scope when it has the
same name and the variable in the outer scope would be found by name lookup in the inner scope at a point immediately before the
declaration [1].
The terms outer scope and inner scope are defined as follows:
- The global scope is the outermost scope;
- Each block (compound-statement), namespace or class introduces an inner scope;
- In a function definition, the function parameters have the same scope as the corresponding function body (compound-statement or
function-try-block);
- A derived class is treated as an inner scope with respect to the base class;
- The definition of a member function introduces an inner scope to the class’s definition;
- The selection-statement_s and _iteration-statement_s introduce an _inner scope which contains the controlled statement(s) and
corresponding condition and init-statement.
If declarations [1] from a namespace are introduced into a scope by a using-declaration, then they are treated as though they
were declared in that scope.
For the purposes of this rule, the following are treated as the declaration [1] of variables:
- All data member and function parameter declarations [1]; and
- The enumerators of an unscoped enumeration type [2] (which have the same scope as the enumeration type).
Rationale
Identifier hiding may lead to developer confusion.
Note: this rule prevents the name of a global variable from being reused as the name of a local variable.
Exception
A class constructor may have a parameter with the same name as a member variable, provided the only use made of that parameter is to initialize the
member. This is a common idiom that poses no risk.
Example
int16_t i;
void f1()
{
int32_t i; // Non-compliant - hides i in global scope
int32_t z;
if ( i == 3 ) // It could be confusing as to which i this refers
{
int32_t z; // Non-compliant - hides z before if
}
}
void f2( int8_t i ) // Non-compliant - hides i in global scope
{
}
class C
{
float i; // Non-compliant - hides i in global scope
float j;
public:
C ( float j ) // Compliant by exception
: j ( j ) {}
C ( float j, float k )
: j ( j )
{
j += k; // Non-compliant - 'j' hides C::j
}
void f3()
{
int32_t j = 0; // Non-compliant - hides C::j
}
};
namespace NS1
{
int32_t i; // Non-compliant - hides i in global scope
void f4( int32_t j ) // Compliant - parameter j does not hide C::j
{
int32_t l = i + j; // Compiles using ::i if NS1::i declaration removed
}
}
namespace NS2
{
int32_t v;
}
using NS2::v;
void f5()
{
float v; // Non-compliant - using hides NS2::v in global scope
}
enum E { e0, e1, e2 };
namespace
{
int32_t e1 = 32; // Non-compliant - hides e1 member of E (in global
} // scope)
Note that compiler reporting of a redeclaration [3] error against para is inconsistent for the following example:
int16_t f6( int16_t para ) // 'para' has same scope as function body
try
{ // Inner scope within function body
int16_t para = 1; // Non-compliant - hides parameter
int16_t a = 2;
return para + a;
}
catch( ... )
{ // Inner scope within function body
int16_t para = 1; // Non-compliant - hides parameter
int16_t a = 2;
return para + a;
}
void f7( int32_t i )
{
for ( int32_t i = 0; i < 9; ++i ) {} // Non-compliant
for ( int32_t j = 0; j < i; ++j ) {}
for ( int32_t j = 0; j < i; ++j ) {} // Compliant - new scope
for ( int32_t k = 0; k < i; ++k ) {}
int32_t k = i; // Compliant - for-loop 'k' not in scope
for ( int32_t k = 0; k < i; ++k ) {} // Non-compliant - hides 'k' above
if ( get() ) // Introduces an inner scope into which 'k'
{ // is defined.
int32_t k; // Non-compliant - hides 'k' in outer scope
}
}
In the following example, there is no hiding in the compliant examples as the local
variable z cannot be found by name lookup within the body of a lambda.
void f8()
{
char z;
auto L1 = [ z ](){ return z; }; // Compliant - no hiding
auto L2 = []( char z ){ return z; }; // Compliant - no hiding
auto L3 = [](){ char z { 'a' }; }; // Compliant - no hiding
auto L4 = [ z ](){ char z { 'a' }; }; // Non-compliant - captured z is hidden
}
Glossary
[1] Declaration
A declaration introduces the name of an entity into a translation unit (see [basic.def]/1).
An entity may be declared several times. The first declaration of an entity in a translation unit is
called an introduction [4]. All subsequent declarations are called redeclarations [3].
A definition [5] is a declaration, as described in [basic.def]/2.
[2] Unscoped enumeration type
A type created with the enum keyword that is not created as enum class or enum struct. Values of such a type
will be subject to integral promotion.
[3] Redeclaration
See declaration [1].
[4] Introduction
See declaration [1].
[5] Definition
See declaration [1].
Copyright The MISRA Consortium Limited © 2023