diff --git a/change_notes/2022-11-02-m0-1-4-single-use-with-templates.md b/change_notes/2022-11-02-m0-1-4-single-use-with-templates.md new file mode 100644 index 0000000000..c6c2c5d99e --- /dev/null +++ b/change_notes/2022-11-02-m0-1-4-single-use-with-templates.md @@ -0,0 +1,2 @@ + - `M0-1-4` - `SingleUseMemberPODVariable.ql` + - Reduce false positives by excluding any constexpr variable whose constant value is used as an argument to a template. \ No newline at end of file diff --git a/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll b/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll index c750bb130c..c4e220549a 100644 --- a/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll +++ b/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll @@ -4,6 +4,12 @@ import cpp import codingstandards.cpp.TrivialType import codingstandards.cpp.deadcode.UnusedVariables +/** Gets the constant value of a constexpr variable. */ +private string getConstExprValue(Variable v) { + result = v.getInitializer().getExpr().getValue() and + v.isConstexpr() +} + /** Gets a "use" count according to rule M0-1-4. */ int getUseCount(Variable v) { exists(int initializers | @@ -12,7 +18,14 @@ int getUseCount(Variable v) { result = initializers + count(VariableAccess access | access = v.getAnAccess() and not access.isCompilerGenerated()) - + count(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) + + count(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) + + // For constexpr variables used as template arguments, we don't see accesses (just the + // appropriate literals). We therefore take a conservative approach and count the number of + // template instantiations that use the given constant, and consider each one to be a use + // of the variable + count(ClassTemplateInstantiation cti | + cti.getTemplateArgument(_).(Expr).getValue() = getConstExprValue(v) + ) ) } diff --git a/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected b/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected index f4309e7a4d..89d048d67a 100644 --- a/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected +++ b/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected @@ -1,3 +1,4 @@ +| test.cpp:36:24:36:29 | unused | Member POD variable unused in C1 is only $@. | test.cpp:36:31:36:31 | initializer for unused | used once | | test_global_or_namespace.cpp:16:7:16:7 | x | Member POD variable x in GA is only $@. | test_global_or_namespace.cpp:38:6:38:6 | x | used once | | test_global_or_namespace.cpp:54:7:54:7 | x | Member POD variable x in N1A is only $@. | test_global_or_namespace.cpp:76:6:76:6 | x | used once | | test_member.cpp:5:7:5:8 | m2 | Member POD variable m2 in A is only $@. | test_member.cpp:9:21:9:25 | constructor init of field m2 | used once | diff --git a/cpp/autosar/test/rules/M0-1-4/test.cpp b/cpp/autosar/test/rules/M0-1-4/test.cpp index 81391e444a..e6ab55c24a 100644 --- a/cpp/autosar/test/rules/M0-1-4/test.cpp +++ b/cpp/autosar/test/rules/M0-1-4/test.cpp @@ -1,5 +1,5 @@ /** Test cases for `SingleUseLocalPODVariable.ql` */ - +#include class A {}; class B { @@ -30,4 +30,12 @@ void test_templates() { f1(); // Triggers a NON_COMPLIANT case in f1(), because B is a POD type f1(); // Does not trigger a NON_COMPLIANT case in f1(), because C is not a // POD type -} \ No newline at end of file +} + +class C1 { + static constexpr int unused{1}; // NON_COMPLIANT + static constexpr int used{2}; // COMPLIANT + int test_use() { return used; } + static constexpr int size{3}; // COMPLIANT + std::array array{false, false}; // size is used here +}; \ No newline at end of file