Skip to content

Commit d4801f8

Browse files
authored
Merge pull request #253 from knewbury01/knewbury01/more-FP-fix
Fixes for #214 and 216 FP reports
2 parents 9de7232 + cd2154d commit d4801f8

File tree

11 files changed

+130
-1
lines changed

11 files changed

+130
-1
lines changed

change_notes/2023-03-13-fp-a16-0-1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* `A16-0-1` - reduce unneeded results related to `#pragma`, as it's already reported by A16-7-1.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* `DCL51-CPP` - reduce false positives related to use of `__func__`

change_notes/2023-03-14-fp-a2-10-1.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* `A2-10-1` - reduce false positives for identifiers in same scope and relating to template variables
2+
* `RULE-5-3`- reduce false positives for identifiers in same scope

change_notes/2023-03-16-fp-a5-1-1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* `A5-1-1` - reduce false positives by omitting literals written into file streams and wrappers around log and stream calls

cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ where
2626
not exists(ConstructorCall cc | cc.getAnArgument() = l) and
2727
not exists(ConstructorFieldInit cf | cf.getExpr() = l) and
2828
not l = any(LoggingOperation logOp).getALoggedExpr().getAChild*() and
29+
// Exclude arguments to wrapper functions (depth 1)
30+
not exists(FunctionCall fc, LoggerOrStreamWrapperFunction w |
31+
fc.getAnArgument() = l and w.getACallToThisFunction() = fc
32+
) and
33+
// Exclude Macros with names like *LOG
34+
not exists(MacroInvocation m | m.getMacroName().matches("%LOG") and m.getAnAffectedElement() = l) and
2935
// Exclude literal 0
3036
not l.getValue() = "0" and
3137
// Exclude character literals
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
| test.cpp:5:9:5:25 | constant string | Literal value "constant string" used outside of type initialization StringLiteral |
22
| test.cpp:14:23:14:25 | 100 | Literal value 100 used outside of type initialization Literal |
33
| test.cpp:54:7:54:7 | 1 | Literal value 1 used outside of type initialization Literal |
4+
| test.cpp:75:23:75:28 | test | Literal value "test" used outside of type initialization StringLiteral |
5+
| test.cpp:76:19:76:28 | not okay | Literal value "not okay" used outside of type initialization StringLiteral |

cpp/autosar/test/rules/A5-1-1/test.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,38 @@ void test_class() {
5252
void test_assignment() {
5353
int x = 0; // COMPLIANT - used in type initialization
5454
x = 1; // NON_COMPLIANT - used in assignment
55+
}
56+
57+
void test_stream(std::ostream &os, const char *str) noexcept {
58+
os << str << "logging string"; // COMPLIANT - literal used in stream write
59+
}
60+
61+
#define WRAPPER_MACRO(X, Y) test_stream(X, Y)
62+
63+
void test_wrapper_stream(std::ostream &os, const char *str) noexcept {
64+
test_stream(os, "test"); // COMPLIANT - wrapper for stream write
65+
WRAPPER_MACRO(os, "test"); // COMPLIANT - wrapper for stream write
66+
}
67+
68+
void test_stream_two(std::ostream &os, const char *str,
69+
const char *alt) noexcept {
70+
os << str << "logging string"; // COMPLIANT - literal used in stream write
71+
throw alt;
72+
}
73+
74+
void test_not_wrapper_stream(std::ostream &os, const char *str) noexcept {
75+
test_stream_two(os, "test",
76+
"not okay"); // NON_COMPLIANT - test_stream_two is
77+
// not actually exclusively a wrapper
78+
}
79+
80+
#define MACRO_LOG(test_str) \
81+
do { \
82+
struct test_struct { \
83+
static const char *get_str() { return static_cast<char *>(test_str); } \
84+
}; \
85+
} while (false)
86+
87+
void f() {
88+
MACRO_LOG("test"); // COMPLIANT - exclusion
5589
}

cpp/common/src/codingstandards/cpp/LoggingOperation.qll

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import cpp
22
import semmle.code.cpp.security.OutputWrite
3+
import codingstandards.cpp.standardlibrary.FileStreams
34

45
/**
56
* A operation which may perform logging.
@@ -16,9 +17,34 @@ class OutputWriteLogging extends LoggingOperation, OutputWrite {
1617
override Expr getALoggedExpr() { result = getASource() }
1718
}
1819

20+
/**
21+
* A `FileStreamFunctionCall` operation is considered a log operation for Coding Standards purposes.
22+
*/
23+
class FileStreamLogging extends LoggingOperation {
24+
FileStreamLogging() { this instanceof FileStreamFunctionCall }
25+
26+
override Expr getALoggedExpr() { result = this.(FileStreamFunctionCall).getAnArgument() }
27+
28+
Expr getFStream() { result = this.(FileStreamFunctionCall).getQualifier() }
29+
}
30+
1931
/** A call which looks like `printf`. */
2032
class PrintfLikeCall extends LoggingOperation, Call {
2133
PrintfLikeCall() { getTarget().getName().toLowerCase().matches("%printf%") }
2234

2335
override Expr getALoggedExpr() { result = getAnArgument() }
2436
}
37+
38+
/**
39+
* In a wrapper `Function`, all accesses of all `Parameters`
40+
* are in located in logging or stream calls
41+
*/
42+
class LoggerOrStreamWrapperFunction extends Function {
43+
LoggerOrStreamWrapperFunction() {
44+
forall(VariableAccess va |
45+
exists(Parameter p | p.getFunction() = this and va = p.getAnAccess())
46+
|
47+
any(LoggingOperation logOp).getALoggedExpr().getAChild*() = va
48+
)
49+
}
50+
}

cpp/common/src/codingstandards/cpp/Scope.qll

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ private UserVariable getPotentialScopeOfVariable_candidate(UserVariable v) {
130130
)
131131
}
132132

133+
/** Gets a variable that is in the potential scope of variable `v`. */
134+
private UserVariable getOuterScopesOfVariable_candidate(UserVariable v) {
135+
exists(Scope s |
136+
result = s.getAVariable() and
137+
(
138+
// Variable in an ancestor scope, but only if there are less than 100 variables in this scope
139+
v = s.getAnAncestor().getAVariable() and
140+
s.getNumberOfVariables() < 100
141+
)
142+
)
143+
}
144+
133145
/** Holds if there exists a translation unit that includes both `f1` and `f2`. */
134146
pragma[noinline]
135147
predicate inSameTranslationUnit(File f1, File f2) {
@@ -148,6 +160,15 @@ UserVariable getPotentialScopeOfVariable(UserVariable v) {
148160
inSameTranslationUnit(v.getFile(), result.getFile())
149161
}
150162

163+
/**
164+
* Gets a user variable which occurs in the "outer scope" of variable `v`.
165+
*/
166+
cached
167+
UserVariable getPotentialScopeOfVariableStrict(UserVariable v) {
168+
result = getOuterScopesOfVariable_candidate(v) and
169+
inSameTranslationUnit(v.getFile(), result.getFile())
170+
}
171+
151172
/** A file that is a C/C++ source file */
152173
class SourceFile extends File {
153174
SourceFile() {
@@ -182,6 +203,15 @@ private predicate hides_candidate(UserVariable v1, UserVariable v2) {
182203
not (v1.isMember() or v2.isMember())
183204
}
184205

206+
/** Holds if `v2` may hide `v1`. */
207+
private predicate hides_candidateStrict(UserVariable v1, UserVariable v2) {
208+
not v1 = v2 and
209+
v2 = getPotentialScopeOfVariableStrict(v1) and
210+
v1.getName() = v2.getName() and
211+
// Member variables cannot hide other variables nor be hidden because the can be referenced through their qualified name.
212+
not (v1.isMember() or v2.isMember())
213+
}
214+
185215
/** Holds if `v2` hides `v1`. */
186216
predicate hides(UserVariable v1, UserVariable v2) {
187217
hides_candidate(v1, v2) and
@@ -192,6 +222,16 @@ predicate hides(UserVariable v1, UserVariable v2) {
192222
)
193223
}
194224

225+
/** Holds if `v2` strictly (`v2` is in an inner scope compared to `v1`) hides `v1`. */
226+
predicate hidesStrict(UserVariable v1, UserVariable v2) {
227+
hides_candidateStrict(v1, v2) and
228+
// Confirm that there's no closer candidate variable which `v2` hides
229+
not exists(UserVariable mid |
230+
hides_candidateStrict(v1, mid) and
231+
hides_candidateStrict(mid, v2)
232+
)
233+
}
234+
195235
/** Holds if `decl` has namespace scope. */
196236
predicate hasNamespaceScope(Declaration decl) {
197237
// getNamespace always returns a namespace (e.g. the global namespace).

cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ Query getQuery() { result instanceof IdentifierHiddenSharedQuery }
1414
query predicate problems(UserVariable v2, string message, UserVariable v1, string varName) {
1515
not isExcluded(v1, getQuery()) and
1616
not isExcluded(v2, getQuery()) and
17-
hides(v1, v2) and
17+
//ignore template variables for this rule
18+
not v1 instanceof TemplateVariable and
19+
not v2 instanceof TemplateVariable and
20+
hidesStrict(v1, v2) and
1821
varName = v1.getName() and
1922
message = "Variable is hiding variable $@."
2023
}

cpp/common/test/rules/identifierhidden/test.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,17 @@ void f3() {
2727
for (int id1; id1 < 1; id1++) {
2828
} // NON_COMPLIANT
2929
}
30+
}
31+
32+
template <typename T> constexpr bool foo = false; // COMPLIANT
33+
34+
namespace {
35+
template <typename T> bool foo = true; // COMPLIANT - omit variable templates
36+
}
37+
38+
template <class T> constexpr T foo1 = T(1.1L);
39+
40+
template <class T, class R> T f(T r) {
41+
T v = foo1<T> * r * r; // COMPLIANT
42+
T v1 = foo1<R> * r * r; // COMPLIANT
3043
}

0 commit comments

Comments
 (0)