rspec/rules/S1117/cfamily/rule.adoc
Marco Borgeaud 98517de0ff
Modify rule S1117: Migrate To LayC - shadow variables (#3270)
The languages for this rule fall into two categories:

* CFamily, JS, and PLSQL: "Variables should not be shadowed" (general
case of shadowing)
* C#, Flex, Java, PHP, Swift: "Local variables should not shadow
field/property/enum case/..." (narrow case of shadowing)

For CFamily, these tickets are also handled: CPP-2785 CPP-3589
2023-10-16 12:04:08 +02:00

146 lines
3.8 KiB
Plaintext

include::../why-general.adoc[]
The examples below show typical situations in which shadowing can occur.
* Parameter shadowing
+
[source,cpp]
----
void f(int x, bool b) {
int y = 4;
if (b) {
int x = 7; // Noncompliant: the parameter "x" is shadowed.
int y = 9; // Noncompliant: the local variable "y" is shadowed.
// ...
}
}
----
* Member variable shadowing
+
[source,cpp]
----
class Foo {
private:
int myField;
public:
void doSomething() {
int myField = 0; // Noncompliant: Foo::myField is shadowed.
// ...
}
};
----
* Global variable shadowing
+
[source,cpp]
----
namespace ns {
int state;
void bar() {
int state = 0; // Noncompliant: the namespace variable is shadowed.
}
}
----
=== Exceptions
It is common practice to have constructor arguments shadowing the fields they initialize in the _member initializer list_.
This pattern avoids the need to select new names for the constructor arguments and will not be reported by this rule.
[source,cpp]
----
class Point {
public:
Point(int x, int y)
: x(x) // Compliant by exception: the parameter "x" is used
// in the member initializer list.
{
y = y; // Noncompliant: the parameter is assigned to itself
// and the member "y" is not initialized.
}
private:
int x;
int y;
};
----
=== Caveats
==== Shadowing in `if`, `else if`, and `else`
Variables can be introduced in the condition of an `if` statement.
Their scope includes the optional `else` statement, which may be surprising.
Consequently, such variables can be shadowed in an `else if` statement.
This can be even more confusing and result in unintended behavior, as illustrated in this example:
[source,cpp]
----
using ExpectedData = std::expected<std::string, std::error_code>;
if (ExpectedData e = readData()) {
printMessage(e.value());
} else if (ExpectedData e = readFallbackSource()) { // Noncompliant
printMessage(e.value());
} else {
logError(
"Initial source failed with: ",
e.error() // Contrary to the intention, the second "e" is used.
);
}
----
==== Shadowing of inaccessible declarations
This rule also raises issues on some variables, although they do not shadow another variable according to a strict interpretation of the {cpp} language. There are mainly two reasons for this.
. Primarily, the readability and maintainability of the code are impaired.
Readers need an advanced understanding of the {cpp} language to understand the subtle differences.
. Secondly, a small change can lead to actual shadowing.
This can lead to subtle bugs when updating the code.
Here is an example with nested classes:
[source,cpp]
----
class A {
public:
int x;
class B;
};
class A::B {
void f(int x) { // Noncompliant: The parameter "x" shadows the field "A::x".
// ...
}
};
----
In the above example, `A::x` cannot be used from `A::B` member functions because it is not a `static` field.
This can lead to surprising effects when moving code around, particularly if the declaration of `A::x` was changed from `int x;` to `static int x;`.
You should always avoid shadowing to avoid any confusion and increase the maintainability of your code.
== Resources
=== External coding guidelines
* MISRA C:2004, 5.2 - Identifiers in an inner scope shall not use the same name as an identifier in an outer scope, and therefore hide that identifier
* MISRA {cpp}:2008, 2-10-2 - Identifiers declared in an inner scope shall not hide an identifier declared in an outer scope
* MISRA C:2012, 5.3 - An identifier declared in an inner scope shall not hide an identifier declared in an outer scope
=== Standards
* CERT - https://wiki.sei.cmu.edu/confluence/display/c/DCL01-C.+Do+not+reuse+variable+names+in+subscopes[DCL01-C. Do not reuse variable names in subscopes]
=== Related rules
* S2387 - Child class fields should not shadow parent class fields
include::../rspecator.adoc[]