================
@@ -188,3 +188,12 @@ void strcpy_and_friends() {
strndup(FOO, sizeof(FOO)); // \
// expected-warning {{'strndup' call operates on objects of type 'const
char' while the size is based on a different type 'const char *'}}
expected-note{{did you mean to provide an explicit length?}}
}
+
+extern "C" int snprintf(char* buffer, __SIZE_TYPE__ buf_size, const char*
format, ...);
+extern "C" void* malloc(unsigned size);
+
+void check_prints(){
----------------
ojhunt wrote:
This should have negative tests as well - a few cases where there is no sizeof
arg, where sizeof is associated with a different field, etc.
This should also have some template tests as well, I've noticed over time that
clang makes it very easy to perform operations over dependent expressions even
though the operations with fail.
My suggestions, not a complete set of all options, you should see if you can
think of any edge cases the might also be interesting. This will become easier
over time as you get an intuition for potentially unexpected interactions -
it's a useful skill as it means you get better at identifying underlying
issues, and also get better at fixing not just a specific case of an error but
those that are nearby, so instead of fixing a single symptom you fix a class of
them.
I'm not saying that that's an issue in this PR, or the related code, but just
that it's a useful skill, and useful to testing (and preventing future
regressions) :D
Anyway, assumed you've got a general test function:
```cpp
void my_test() {
some_test_func<parameters>();
some_other_test_func<parameters>();
...
}
```
Which you'd fill with also the instantiation tests you might think are
interesting
```cpp
// This is a dependent context by none of the actual operations are dependent.
template <class T> void foo() {
char* a = (char*) malloc(20);
const char* b = "Hello World";
snprintf(a, sizeof(a), "%s", b);
}
// test call would be anything, e.g. foo<int>() would be fine
// Messing with the buffer type
template <class T> void foo() {
T a = (T) malloc(20);
const char* b = "Hello World";
snprintf((char *)a, sizeof(a), "%s", b);
}
// tests that would be worth considering: foo<char*>, foo<char>, foo<void*>, etc
// some will be errors, some will be warnings, and `char*` should produce the
identical
// warning to the initial non-template test
// Closer to the original case, all the pointer casts should work, etc
template <class T> void foo() {
T *a = (T *) malloc(20);
const char* b = "Hello World";
snprintf((char *)a, sizeof(a), "%s", b);
}
// tests that would be worth considering: foo<char*>, foo<char>, foo<void*>, etc
```
In the first case you could diagnose the mistake in the template function
definition as the expression should not be dependent, however many of our
diagnostics simply skip dependent contexts entirely (and it's possible this
already occurs in logic that `CheckMemAccess`, rendering the behavioral
question moot. If your diagnostic logic does get called in dependent contexts
I'd recommend you have a look at the options for behavior to see the overall
behavior and make a choice of which you think is your preferred behavior
(without jumping through hoops to make the diagnostics "better" in the
dependent case).
https://github.com/llvm/llvm-project/pull/170637
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits