diff --git a/c/common/test/rules/identifierhidden/IdentifierHidden.expected b/c/common/test/rules/identifierhidden/IdentifierHidden.expected index 67809ee00..d6f574e31 100644 --- a/c/common/test/rules/identifierhidden/IdentifierHidden.expected +++ b/c/common/test/rules/identifierhidden/IdentifierHidden.expected @@ -1,5 +1,5 @@ -| test.c:4:7:4:9 | id1 | Variable is hiding variable $@. | test.c:1:5:1:7 | id1 | id1 | -| test.c:7:13:7:15 | id1 | Variable is hiding variable $@. | test.c:1:5:1:7 | id1 | id1 | -| test.c:10:12:10:14 | id1 | Variable is hiding variable $@. | test.c:1:5:1:7 | id1 | id1 | -| test.c:11:14:11:16 | id1 | Variable is hiding variable $@. | test.c:10:12:10:14 | id1 | id1 | -| test.c:24:24:24:26 | id2 | Variable is hiding variable $@. | test.c:22:5:22:7 | id2 | id2 | +| test.c:4:7:4:9 | id1 | Declaration is hiding declaration $@. | test.c:1:5:1:7 | id1 | id1 | +| test.c:7:13:7:15 | id1 | Declaration is hiding declaration $@. | test.c:1:5:1:7 | id1 | id1 | +| test.c:10:12:10:14 | id1 | Declaration is hiding declaration $@. | test.c:1:5:1:7 | id1 | id1 | +| test.c:11:14:11:16 | id1 | Declaration is hiding declaration $@. | test.c:10:12:10:14 | id1 | id1 | +| test.c:24:24:24:26 | id2 | Declaration is hiding declaration $@. | test.c:22:5:22:7 | id2 | id2 | diff --git a/change_notes/2024-02-27-identifier-hidden.md b/change_notes/2024-02-27-identifier-hidden.md new file mode 100644 index 000000000..d43e1ad9e --- /dev/null +++ b/change_notes/2024-02-27-identifier-hidden.md @@ -0,0 +1,3 @@ +- `A2-10-1`, `RULE-5-3` - `IdentifierHiding.ql`, `IdentifierHidingC.ql`: + - Address FN reported in #118. Rule was missing detection of functions. Additionally omitted class template instantiations. + - Fix FP for identifiers in nested namespaces. \ No newline at end of file diff --git a/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql b/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql index 3c2ae9a59..13272c816 100644 --- a/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql +++ b/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql @@ -18,6 +18,7 @@ import codingstandards.cpp.autosar import codingstandards.cpp.TrivialType import codingstandards.cpp.SideEffect import semmle.code.cpp.controlflow.SSA +import codingstandards.cpp.Expr predicate isZeroInitializable(Variable v) { not exists(v.getInitializer().getExpr()) and @@ -34,78 +35,6 @@ predicate isTypeZeroInitializable(Type t) { t.getUnderlyingType() instanceof ArrayType } -/** - * An optimized set of expressions used to determine the flow through constexpr variables. - */ -class VariableAccessOrCallOrLiteral extends Expr { - VariableAccessOrCallOrLiteral() { - this instanceof VariableAccess or - this instanceof Call or - this instanceof Literal - } -} - -/** - * Holds if the value of source flows through compile time evaluated variables to target. - */ -predicate flowsThroughConstExprVariables( - VariableAccessOrCallOrLiteral source, VariableAccessOrCallOrLiteral target -) { - ( - source = target - or - source != target and - exists(SsaDefinition intermediateDef, StackVariable intermediate | - intermediateDef.getAVariable().getFunction() = source.getEnclosingFunction() and - intermediateDef.getAVariable().getFunction() = target.getEnclosingFunction() and - intermediateDef.getAVariable() = intermediate and - intermediate.isConstexpr() - | - DataFlow::localExprFlow(source, intermediateDef.getDefiningValue(intermediate)) and - flowsThroughConstExprVariables(intermediateDef.getAUse(intermediate), target) - ) - ) -} - -/* - * Returns true if the given call may be evaluated at compile time and is compile time evaluated because - * all its arguments are compile time evaluated and its default values are compile time evaluated. - */ - -predicate isCompileTimeEvaluated(Call call) { - // 1. The call may be evaluated at compile time, because it is constexpr, and - call.getTarget().isConstexpr() and - // 2. all its arguments are compile time evaluated, and - forall(DataFlow::Node ultimateArgSource, DataFlow::Node argSource | - argSource = DataFlow::exprNode(call.getAnArgument()) and - DataFlow::localFlow(ultimateArgSource, argSource) and - not DataFlow::localFlowStep(_, ultimateArgSource) - | - ( - ultimateArgSource.asExpr() instanceof Literal - or - any(Call c | isCompileTimeEvaluated(c)) = ultimateArgSource.asExpr() - ) and - // If the ultimate argument source is not the same as the argument source, then it must flow through - // constexpr variables. - ( - ultimateArgSource != argSource - implies - flowsThroughConstExprVariables(ultimateArgSource.asExpr(), argSource.asExpr()) - ) - ) and - // 3. all the default values used are compile time evaluated. - forall(Expr defaultValue, Parameter parameterUsingDefaultValue, int idx | - parameterUsingDefaultValue = call.getTarget().getParameter(idx) and - not exists(call.getArgument(idx)) and - parameterUsingDefaultValue.getAnAssignedValue() = defaultValue - | - defaultValue instanceof Literal - or - any(Call c | isCompileTimeEvaluated(c)) = defaultValue - ) -} - from Variable v where not isExcluded(v, ConstPackage::variableMissingConstexprQuery()) and @@ -119,7 +48,7 @@ where ( v.getInitializer().getExpr().isConstant() or - any(Call call | isCompileTimeEvaluated(call)) = v.getInitializer().getExpr() + any(Call call | isCompileTimeEvaluatedCall(call)) = v.getInitializer().getExpr() or isZeroInitializable(v) or diff --git a/cpp/common/src/codingstandards/cpp/Expr.qll b/cpp/common/src/codingstandards/cpp/Expr.qll index 5c0a20fd9..fe2877f84 100644 --- a/cpp/common/src/codingstandards/cpp/Expr.qll +++ b/cpp/common/src/codingstandards/cpp/Expr.qll @@ -191,8 +191,89 @@ module MisraExpr { } /** + * An optimized set of expressions used to determine the flow through constexpr variables. + */ +class VariableAccessOrCallOrLiteral extends Expr { + VariableAccessOrCallOrLiteral() { + this instanceof VariableAccess and this.(VariableAccess).getTarget().isConstexpr() + or + this instanceof Call + or + this instanceof Literal + } +} + +/** + * Holds if the value of source flows through compile time evaluated variables to target. + */ +predicate flowsThroughConstExprVariables( + VariableAccessOrCallOrLiteral source, VariableAccessOrCallOrLiteral target +) { + ( + source = target + or + source != target and + exists(SsaDefinition intermediateDef, StackVariable intermediate | + intermediateDef.getAVariable().getFunction() = source.getEnclosingFunction() and + intermediateDef.getAVariable().getFunction() = target.getEnclosingFunction() and + intermediateDef.getAVariable() = intermediate and + intermediate.isConstexpr() + | + DataFlow::localExprFlow(source, intermediateDef.getDefiningValue(intermediate)) and + flowsThroughConstExprVariables(intermediateDef.getAUse(intermediate), target) + ) + ) +} + +predicate isCompileTimeEvaluatedExpression(Expr expression) { + forall(DataFlow::Node ultimateSource, DataFlow::Node source | + source = DataFlow::exprNode(expression) and + DataFlow::localFlow(ultimateSource, source) and + not DataFlow::localFlowStep(_, ultimateSource) + | + isDirectCompileTimeEvaluatedExpression(ultimateSource.asExpr()) and + // If the ultimate source is not the same as the source, then it must flow through + // constexpr variables. + ( + ultimateSource != source + implies + flowsThroughConstExprVariables(ultimateSource.asExpr(), source.asExpr()) + ) + ) +} + +predicate isDirectCompileTimeEvaluatedExpression(Expr expression) { + expression instanceof Literal + or + any(Call c | isCompileTimeEvaluatedCall(c)) = expression +} + +/* + * Returns true if the given call may be evaluated at compile time and is compile time evaluated because + * all its arguments are compile time evaluated and its default values are compile time evaluated. + */ + +predicate isCompileTimeEvaluatedCall(Call call) { + // 1. The call may be evaluated at compile time, because it is constexpr, and + call.getTarget().isConstexpr() and + // 2. all its arguments are compile time evaluated, and + forall(Expr argSource | argSource = call.getAnArgument() | + isCompileTimeEvaluatedExpression(argSource) + ) and + // 3. all the default values used are compile time evaluated. + forall(Expr defaultValue, Parameter parameterUsingDefaultValue, int idx | + parameterUsingDefaultValue = call.getTarget().getParameter(idx) and + not exists(call.getArgument(idx)) and + parameterUsingDefaultValue.getAnAssignedValue() = defaultValue + | + isDirectCompileTimeEvaluatedExpression(defaultValue) + ) +} + +/* * an operator that does not evaluate its operand */ + class UnevaluatedExprExtension extends Expr { UnevaluatedExprExtension() { this.getAChild().isUnevaluated() diff --git a/cpp/common/src/codingstandards/cpp/Scope.qll b/cpp/common/src/codingstandards/cpp/Scope.qll index 4dd727b8d..cfa2d062f 100644 --- a/cpp/common/src/codingstandards/cpp/Scope.qll +++ b/cpp/common/src/codingstandards/cpp/Scope.qll @@ -57,10 +57,18 @@ private Element getParentScope(Element e) { /** A variable which is defined by the user, rather than being from a third party or compiler generated. */ class UserVariable extends Variable { - UserVariable() { + UserVariable() { this instanceof UserDeclaration } +} + +/** A construct which is defined by the user, rather than being from a third party or compiler generated. */ +class UserDeclaration extends Declaration { + UserDeclaration() { exists(getFile().getRelativePath()) and - not isCompilerGenerated() and + not this.(Variable).isCompilerGenerated() and + not this.(Function).isCompilerGenerated() and not this.(Parameter).getFunction().isCompilerGenerated() and + // Class template instantiations are compiler generated instances that share the same parent scope. This will result in a cross-product on class template instantiations because they have the same name and same parent scope. We therefore exclude these from consideration like we do with other compiler generated identifiers of interest. + not this instanceof ClassTemplateInstantiation and // compiler inferred parameters have name of p#0 not this.(Parameter).getName() = "p#0" } @@ -74,11 +82,13 @@ class Scope extends Element { int getNumberOfVariables() { result = count(getAVariable()) } + int getNumberOfDeclarations() { result = count(getADeclaration()) } + Scope getAnAncestor() { result = this.getStrictParent+() } Scope getStrictParent() { result = getParentScope(this) } - Declaration getADeclaration() { getParentScope(result) = this } + UserDeclaration getADeclaration() { getParentScope(result) = this } Expr getAnExpr() { this = getParentScope(result) } @@ -122,31 +132,31 @@ class GeneratedBlockStmt extends BlockStmt { GeneratedBlockStmt() { this.getLocation() instanceof UnknownLocation } } -/** Gets a variable that is in the potential scope of variable `v`. */ -private UserVariable getPotentialScopeOfVariable_candidate(UserVariable v) { +/** Gets a Declaration that is in the potential scope of Declaration `v`. */ +private UserDeclaration getPotentialScopeOfDeclaration_candidate(UserDeclaration v) { exists(Scope s | - result = s.getAVariable() and + result = s.getADeclaration() and ( - // Variable in an ancestor scope, but only if there are less than 100 variables in this scope - v = s.getAnAncestor().getAVariable() and - s.getNumberOfVariables() < 100 + // Declaration in an ancestor scope, but only if there are less than 100 declarations in this scope + v = s.getAnAncestor().getADeclaration() and + s.getNumberOfDeclarations() < 100 or - // In the same scope, but not the same variable, and choose just one to report - v = s.getAVariable() and + // In the same scope, but not the same Declaration, and choose just one to report + v = s.getADeclaration() and not result = v and v.getName() <= result.getName() ) ) } -/** Gets a variable that is in the potential scope of variable `v`. */ -private UserVariable getOuterScopesOfVariable_candidate(UserVariable v) { +/** Gets a Declaration that is in the potential scope of Declaration `v`. */ +private UserDeclaration getPotentialScopeOfDeclarationStrict_candidate(UserDeclaration v) { exists(Scope s | - result = s.getAVariable() and + result = s.getADeclaration() and ( - // Variable in an ancestor scope, but only if there are less than 100 variables in this scope - v = s.getAnAncestor().getAVariable() and - s.getNumberOfVariables() < 100 + // Declaration in an ancestor scope, but only if there are less than 100 variables in this scope + v = s.getAnAncestor().getADeclaration() and + s.getNumberOfDeclarations() < 100 ) ) } @@ -161,20 +171,20 @@ predicate inSameTranslationUnit(File f1, File f2) { } /** - * Gets a user variable which occurs in the "potential scope" of variable `v`. + * Gets a user Declaration which occurs in the "outer scope" of Declaration `v`. */ cached -UserVariable getPotentialScopeOfVariable(UserVariable v) { - result = getPotentialScopeOfVariable_candidate(v) and +UserDeclaration getPotentialScopeOfDeclarationStrict(UserDeclaration v) { + result = getPotentialScopeOfDeclarationStrict_candidate(v) and inSameTranslationUnit(v.getFile(), result.getFile()) } /** - * Gets a user variable which occurs in the "outer scope" of variable `v`. + * Gets a user variable which occurs in the "potential scope" of variable `v`. */ cached -UserVariable getPotentialScopeOfVariableStrict(UserVariable v) { - result = getOuterScopesOfVariable_candidate(v) and +UserDeclaration getPotentialScopeOfDeclaration(UserDeclaration v) { + result = getPotentialScopeOfDeclaration_candidate(v) and inSameTranslationUnit(v.getFile(), result.getFile()) } @@ -204,18 +214,9 @@ class TranslationUnit extends SourceFile { } /** Holds if `v2` may hide `v1`. */ -private predicate hides_candidate(UserVariable v1, UserVariable v2) { +private predicate hides_candidateStrict(UserDeclaration v1, UserDeclaration v2) { not v1 = v2 and - v2 = getPotentialScopeOfVariable(v1) and - v1.getName() = v2.getName() and - // Member variables cannot hide other variables nor be hidden because the can be referenced through their qualified name. - not (v1.isMember() or v2.isMember()) -} - -/** Holds if `v2` may hide `v1`. */ -private predicate hides_candidateStrict(UserVariable v1, UserVariable v2) { - not v1 = v2 and - v2 = getPotentialScopeOfVariableStrict(v1) and + v2 = getPotentialScopeOfDeclarationStrict(v1) and v1.getName() = v2.getName() and // Member variables cannot hide other variables nor be hidden because the can be referenced through their qualified name. not (v1.isMember() or v2.isMember()) and @@ -239,6 +240,15 @@ private predicate hides_candidateStrict(UserVariable v1, UserVariable v2) { ) } +/** Holds if `v2` may hide `v1`. */ +private predicate hides_candidate(UserDeclaration v1, UserDeclaration v2) { + not v1 = v2 and + v2 = getPotentialScopeOfDeclaration(v1) and + v1.getName() = v2.getName() and + // Member variables cannot hide other variables nor be hidden because the can be referenced through their qualified name. + not (v1.isMember() or v2.isMember()) +} + /** * Gets the enclosing statement of the given variable, if any. */ @@ -257,20 +267,22 @@ private Stmt getEnclosingStmt(LocalScopeVariable v) { } /** Holds if `v2` hides `v1`. */ -predicate hides(UserVariable v1, UserVariable v2) { +predicate hides(UserDeclaration v1, UserDeclaration v2) { hides_candidate(v1, v2) and // Confirm that there's no closer candidate variable which `v2` hides - not exists(UserVariable mid | + not exists(UserDeclaration mid | hides_candidate(v1, mid) and hides_candidate(mid, v2) - ) + ) and + // Unlike `hidesStrict`, that requires a different scope, `hides` considers declarations in the same scope. This will include function overloads based on their name. To remove overloads from consideration, we exclude them. + not v1.(Function).getAnOverload() = v2 } /** Holds if `v2` strictly (`v2` is in an inner scope compared to `v1`) hides `v1`. */ -predicate hidesStrict(UserVariable v1, UserVariable v2) { +predicate hidesStrict(UserDeclaration v1, UserDeclaration v2) { hides_candidateStrict(v1, v2) and // Confirm that there's no closer candidate variable which `v2` hides - not exists(UserVariable mid | + not exists(UserDeclaration mid | hides_candidateStrict(v1, mid) and hides_candidateStrict(mid, v2) ) @@ -287,3 +299,18 @@ predicate hasClassScope(Declaration decl) { exists(decl.getDeclaringType()) } /** Holds if `decl` has block scope. */ predicate hasBlockScope(Declaration decl) { exists(BlockStmt b | b.getADeclaration() = decl) } + +/** + * identifiers in nested (named/nonglobal) namespaces are exceptions to hiding due to being able access via fully qualified ids + */ +predicate excludedViaNestedNamespaces(UserDeclaration outerDecl, UserDeclaration innerDecl) { + exists(Namespace inner, Namespace outer | + outer.getAChildNamespace+() = inner and + //outer is not global + not outer instanceof GlobalNamespace and + not outer.isAnonymous() and + not inner.isAnonymous() and + innerDecl.getNamespace() = inner and + outerDecl.getNamespace() = outer + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll b/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll index 87a4580ab..4876ca9a5 100644 --- a/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll +++ b/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll @@ -47,7 +47,7 @@ string step1(string s) { string step2(string s) { s = "m_" and result = "rn" } predicate violation(UserVariable v1, UserVariable v2) { - v2 = getPotentialScopeOfVariable(v1) and + v2 = getPotentialScopeOfDeclaration(v1) and exists(string s1, string s2 | // over-approximate a match, because it is cheaper to compute getCanon(v1) = getCanon(v2) and diff --git a/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll b/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll index fc0a01cbd..d5d8a0d93 100644 --- a/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll +++ b/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll @@ -6,18 +6,88 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import codingstandards.cpp.Scope +import codingstandards.cpp.ConstHelpers abstract class IdentifierHiddenSharedQuery extends Query { } Query getQuery() { result instanceof IdentifierHiddenSharedQuery } -query predicate problems(UserVariable v2, string message, UserVariable v1, string varName) { - not isExcluded(v1, getQuery()) and - not isExcluded(v2, getQuery()) and +/** + * a `Variable` that is nonvolatile and const + * and of type `IntegralOrEnumType` + */ +class NonVolatileConstIntegralOrEnumVariable extends Variable { + NonVolatileConstIntegralOrEnumVariable() { + not this.isVolatile() and + this.isConst() and + this.getUnspecifiedType() instanceof IntegralOrEnumType + } +} + +/** + * Holds if declaration `innerDecl`, declared in a lambda, hides a declaration `outerDecl` by the lambda. + */ +predicate hiddenInLambda(UserVariable outerDecl, UserVariable innerDecl) { + exists( + Scope innerScope, LambdaExpression lambdaExpr, Scope lambdaExprScope, Scope outerScope, + Closure lambdaClosure + | + // The variable `innerDecl` is declared inside of the lambda. + innerScope.getADeclaration() = innerDecl and + // Because a lambda is compiled down to a closure, we need to use the closure to determine if the declaration + // is part of the lambda. + innerScope.getAnAncestor() = lambdaClosure and + // Next we determine the scope of the lambda expression to determine if `outerDecl` is visible in the scope of the lambda. + lambdaClosure.getLambdaExpression() = lambdaExpr and + lambdaExprScope.getAnExpr() = lambdaExpr and + outerScope.getADeclaration() = outerDecl and + lambdaExprScope.getStrictParent*() = outerScope and + ( + // A definition can be hidden if it is in scope and it is captured by the lambda, + exists(LambdaCapture cap | + lambdaExpr.getACapture() = cap and + // The outer declaration is captured by the lambda + outerDecl.getAnAccess() = cap.getInitializer() + ) + or + // it is is non-local, + outerDecl instanceof GlobalVariable + or + // it has static or thread local storage duration, + (outerDecl.isThreadLocal() or outerDecl.isStatic()) + or + //it is a reference that has been initialized with a constant expression. + outerDecl.getType().stripTopLevelSpecifiers() instanceof ReferenceType and + outerDecl.getInitializer().getExpr() instanceof Literal + or + // //it const non-volatile integral or enumeration type and has been initialized with a constant expression + outerDecl instanceof NonVolatileConstIntegralOrEnumVariable and + outerDecl.getInitializer().getExpr() instanceof Literal + or + //it is constexpr and has no mutable members + outerDecl.isConstexpr() and + not exists(Class c | + c = outerDecl.getType() and not c.getAMember() instanceof MutableVariable + ) + ) and + // Finally, the variables must have the same names. + innerDecl.getName() = outerDecl.getName() + ) +} + +query predicate problems( + UserDeclaration innerDecl, string message, UserDeclaration outerDecl, string varName +) { + not isExcluded(outerDecl, getQuery()) and + not isExcluded(innerDecl, getQuery()) and //ignore template variables for this rule - not v1 instanceof TemplateVariable and - not v2 instanceof TemplateVariable and - hidesStrict(v1, v2) and - varName = v1.getName() and - message = "Variable is hiding variable $@." + not outerDecl instanceof TemplateVariable and + not innerDecl instanceof TemplateVariable and + //ignore types for this rule as the Misra C/C++ 23 version of this rule (rule 6.4.1 and 6.4.2) focuses solely on variables and functions + not innerDecl instanceof Type and + not outerDecl instanceof Type and + (hidesStrict(outerDecl, innerDecl) or hiddenInLambda(outerDecl, innerDecl)) and + not excludedViaNestedNamespaces(outerDecl, innerDecl) and + varName = outerDecl.getName() and + message = "Declaration is hiding declaration $@." } diff --git a/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected b/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected index 2ea18aa9c..1b0d94d83 100644 --- a/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected +++ b/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected @@ -1,11 +1,18 @@ -| test.cpp:4:5:4:7 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | -| test.cpp:8:5:8:7 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | -| test.cpp:11:5:11:7 | id1 | Variable is hiding variable $@. | test.cpp:8:5:8:7 | id1 | id1 | -| test.cpp:20:7:20:9 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | -| test.cpp:23:13:23:15 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | -| test.cpp:26:12:26:14 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | -| test.cpp:27:14:27:16 | id1 | Variable is hiding variable $@. | test.cpp:26:12:26:14 | id1 | id1 | -| test.cpp:65:11:65:11 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | -| test.cpp:67:9:67:9 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | -| test.cpp:70:12:70:12 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | -| test.cpp:75:16:75:16 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:4:5:4:7 | id1 | Declaration is hiding declaration $@. | test.cpp:1:5:1:7 | id1 | id1 | +| test.cpp:8:5:8:7 | id1 | Declaration is hiding declaration $@. | test.cpp:1:5:1:7 | id1 | id1 | +| test.cpp:20:7:20:9 | id1 | Declaration is hiding declaration $@. | test.cpp:1:5:1:7 | id1 | id1 | +| test.cpp:23:13:23:15 | id1 | Declaration is hiding declaration $@. | test.cpp:1:5:1:7 | id1 | id1 | +| test.cpp:26:12:26:14 | id1 | Declaration is hiding declaration $@. | test.cpp:1:5:1:7 | id1 | id1 | +| test.cpp:27:14:27:16 | id1 | Declaration is hiding declaration $@. | test.cpp:26:12:26:14 | id1 | id1 | +| test.cpp:65:11:65:11 | i | Declaration is hiding declaration $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:67:9:67:9 | i | Declaration is hiding declaration $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:70:12:70:12 | i | Declaration is hiding declaration $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:75:16:75:16 | i | Declaration is hiding declaration $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:81:5:81:5 | a | Declaration is hiding declaration $@. | test.cpp:79:5:79:5 | a | a | +| test.cpp:102:9:102:9 | b | Declaration is hiding declaration $@. | test.cpp:96:11:96:11 | b | b | +| test.cpp:114:9:114:17 | globalvar | Declaration is hiding declaration $@. | test.cpp:110:5:110:13 | globalvar | globalvar | +| test.cpp:133:11:133:11 | b | Declaration is hiding declaration $@. | test.cpp:127:13:127:13 | b | b | +| test.cpp:142:9:142:10 | a1 | Declaration is hiding declaration $@. | test.cpp:140:14:140:15 | a1 | a1 | +| test.cpp:147:9:147:10 | a2 | Declaration is hiding declaration $@. | test.cpp:145:20:145:21 | a2 | a2 | +| test.cpp:152:9:152:10 | a3 | Declaration is hiding declaration $@. | test.cpp:150:17:150:18 | a3 | a3 | +| test.cpp:164:9:164:10 | a5 | Declaration is hiding declaration $@. | test.cpp:162:13:162:14 | a5 | a5 | diff --git a/cpp/common/test/rules/identifierhidden/test.cpp b/cpp/common/test/rules/identifierhidden/test.cpp index cdd7137c5..ede4bb24d 100644 --- a/cpp/common/test/rules/identifierhidden/test.cpp +++ b/cpp/common/test/rules/identifierhidden/test.cpp @@ -74,4 +74,102 @@ void test_scope_order() { } catch (int i) { // NON_COMPLIANT } +} + +int a; +namespace b { +int a() {} // NON_COMPLIANT +} // namespace b + +namespace b1 { +typedef int a; // COMPLIANT - do not consider types +} + +namespace ns_exception1_outer { +int a1; // COMPLIANT - exception +namespace ns_exception1_inner { +void a1(); // COMPLIANT - exception +} +} // namespace ns_exception1_outer + +void f4() { + int a1, b; + auto lambda1 = [a1]() { + int b = 10; // COMPLIANT - exception - non captured variable b + }; + + auto lambda2 = [b]() { + int b = 10; // NON_COMPLIANT - not an exception - captured + // variable b + }; +} + +void f5(int i) {} // COMPLIANT - exception - assume purposefully overloaded +void f5(double d) {} // COMPLIANT - exception - assume purposefully overloaded + +int globalvar = 0; + +int f6() { + auto lambda_with_shadowing = []() { + int globalvar = 1; // NON_COMPLIANT - not an exception - not captured but + // still accessible + return globalvar + globalvar; + }; + + auto lambda_without_shadowing = []() { return globalvar + globalvar; }; + + return lambda_with_shadowing(); +} + +void f7(int p) { + // Introduce a nested scope to test scope comparison. + if (p != 0) { + int a1, b; + auto lambda1 = [a1]() { + int b = 10; // COMPLIANT - exception - non captured variable b + }; + + auto lambda2 = [b]() { + int b = 10; // NON_COMPLIANT - not an exception - captured + // variable b + }; + } +} + +void f8() { + static int a1; + auto lambda1 = []() { + int a1 = 10; // NON_COMPLIANT - Lambda can access static variable. + }; + + thread_local int a2; + auto lambda2 = []() { + int a2 = 10; // NON_COMPLIANT - Lambda can access thread local variable. + }; + + constexpr int a3 = 10; + auto lambda3 = []() { + int a3 = a3 + 1; // NON_COMPLIANT - Lambda can access const + // expression without mutable members. + }; + + const int &a4 = a3; + auto lambda4 = []() { + int a4 = a4 + 1; // NON_COMPLIANT[FALSE_NEGATIVE] - Lambda can access + // reference initialized with constant expression. + }; + + const int a5 = 10; + auto lambda5 = []() { + int a5 = a5 + 1; // NON_COMPLIANT - Lambda can access const + // non-volatile integral or enumeration type initialized + // with constant expression. + }; + + volatile const int a6 = 10; + auto lambda6 = []() { + int a6 = + a6 + 1; // COMPLIANT - Lambda cannot access const volatile integral or + // enumeration type initialized with constant expression. + }; } \ No newline at end of file