================
@@ -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

Reply via email to