[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-19 Thread Phabricator via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC337470: [Sema] Add a new warning, -Wmemset-transposed-args 
(authored by epilk, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D49112?vs=156178=156298#toc

Repository:
  rC Clang

https://reviews.llvm.org/D49112

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaChecking.cpp
  test/Sema/transpose-memset.c

Index: test/Sema/transpose-memset.c
===
--- test/Sema/transpose-memset.c
+++ test/Sema/transpose-memset.c
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1   -Wmemset-transposed-args -verify %s
+// RUN: %clang_cc1 -xc++ -Wmemset-transposed-args -verify %s
+
+#define memset(...) __builtin_memset(__VA_ARGS__)
+#define bzero(x,y) __builtin_memset(x, 0, y)
+#define real_bzero(x,y) __builtin_bzero(x,y)
+
+int array[10];
+int *ptr;
+
+int main() {
+  memset(array, sizeof(array), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(array, sizeof(array), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(ptr), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(ptr, sizeof(*ptr) * 10, 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *), 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *) + 10, 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(char) * sizeof(int *), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(array, sizeof(array), sizeof(array)); // Uh... fine I guess.
+  memset(array, 0, sizeof(array));
+  memset(ptr, 0, sizeof(int *) * 10);
+  memset(array, (int)sizeof(array), (0)); // no warning
+  memset(array, (int)sizeof(array), 32); // no warning
+  memset(array, 32, (0)); // no warning
+
+  bzero(ptr, 0); // expected-warning{{'size' argument to bzero is '0'}} expected-note{{parenthesize the second argument to silence}}
+  real_bzero(ptr, 0); // expected-warning{{'size' argument to bzero is '0'}} expected-note{{parenthesize the second argument to silence}}
+}
+
+void macros() {
+#define ZERO 0
+  int array[10];
+  memset(array, 0xff, ZERO); // no warning
+  // Still emit a diagnostic for memsetting a sizeof expression:
+  memset(array, sizeof(array), ZERO); // expected-warning{{'sizeof'}} expected-note{{cast}}
+  bzero(array, ZERO); // no warning
+  real_bzero(array, ZERO); // no warning
+#define NESTED_DONT_DIAG\
+  memset(array, 0xff, ZERO);\
+  real_bzero(array, ZERO);
+
+  NESTED_DONT_DIAG;
+
+#define NESTED_DO_DIAG  \
+  memset(array, 0xff, 0);   \
+  real_bzero(array, 0)
+
+  NESTED_DO_DIAG; // expected-warning{{'size' argument to memset}} expected-warning{{'size' argument to bzero}} expected-note2{{parenthesize}}
+
+#define FN_MACRO(p) \
+  memset(array, 0xff, p)
+
+  FN_MACRO(ZERO);
+  FN_MACRO(0); // FIXME: should we diagnose this?
+
+  __builtin_memset(array, 0, ZERO); // no warning
+  __builtin_bzero(array, ZERO);
+  __builtin_memset(array, 0, 0); // expected-warning{{'size' argument to memset}} // expected-note{{parenthesize}}
+  __builtin_bzero(array, 0); // expected-warning{{'size' argument to bzero}} // expected-note{{parenthesize}}
+}
Index: lib/Sema/SemaChecking.cpp
===
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -8629,24 +8629,26 @@
   return nullptr;
 }
 
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+  if (const auto *Unary = dyn_cast(E))
+if (Unary->getKind() == UETT_SizeOf)
+  return Unary;
+  return nullptr;
+}
+
 /// If E is a sizeof expression, returns its argument expression,
 /// otherwise returns NULL.
 static const Expr *getSizeOfExprArg(const Expr *E) {
-  if (const UnaryExprOrTypeTraitExpr *SizeOf =
-  dyn_cast(E))
-if (SizeOf->getKind() == UETT_SizeOf && !SizeOf->isArgumentType())
+  if (const 

[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-19 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman accepted this revision.
aaron.ballman added a comment.

LGTM aside from a minor nit.




Comment at: clang/lib/Sema/SemaChecking.cpp:8751
+
+  if (auto *BO = dyn_cast(SizeofExpr)) {
+if (BO->getOpcode() != BO_Mul && BO->getOpcode() != BO_Add)

`const auto *`


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-18 Thread Erik Pilkington via Phabricator via cfe-commits
erik.pilkington added inline comments.



Comment at: clang/lib/Sema/SemaChecking.cpp:7984
+  if (isa(ThirdArg) &&
+  cast(ThirdArg)->getValue() == 0) {
+WarningKind = 0;

Quuxplusone wrote:
> > Suppress the diagnostic in cases like `memset(ptr, 0xff, PADDING)`, when 
> > `PADDING` is a macro defined to be `0`.
> 
> Would it suffice to treat a literal `0xFF` in the exact same way you treat a 
> literal `0`? That is,
> ```
> if (SecondArg is integer literal 0 or 255 || ThirdArg involves sizeof) {
> do nothing
> } else if (ThirdArg is integer literal 0 or 255 || SecondArg involves sizeof) 
> {
> diagnose it
> }
> ```
> You should add a test case proving that `memset(x, 0, 0)` doesn't get 
> diagnosed (assuming the two `0`s come from two different macro expansions, 
> for example).
> My sketch above would fail to diagnose `memset(x, 0, 255)`. I don't know if 
> this is a feature or a bug. :)  You probably ought to have a test case for 
> that, either way, just to demonstrate the expected behavior.
I definitely don't think that ThirdArg being 255 should lead to a diagnostic, 
regardless of what SecondArg is. I'm fine with omitting a diagnostic in 
`memset(ptr, 255, 0)`, but if the user explicitly spelled out `0` then I would 
probably prefer to diagnose. FWIW, for the case I found where we emitted a 
false-positive the second argument was 0xd9 or something.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-18 Thread Erik Pilkington via Phabricator via cfe-commits
erik.pilkington updated this revision to Diff 156178.
erik.pilkington marked 5 inline comments as done.
erik.pilkington added a comment.
This revision is now accepted and ready to land.

In this new patch:

- Add support for __builtin_bzero (-Wsuspicious-bzero), improve diagnostics for 
platforms that define bzero to __builtin_memset
- Suppress the diagnostic when the `0` in memset(ptr, something, 0) came from a 
macro expansion.
- Address review comments

Thanks!


https://reviews.llvm.org/D49112

Files:
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaChecking.cpp
  clang/test/Sema/transpose-memset.c

Index: clang/test/Sema/transpose-memset.c
===
--- /dev/null
+++ clang/test/Sema/transpose-memset.c
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1   -Wmemset-transposed-args -verify %s
+// RUN: %clang_cc1 -xc++ -Wmemset-transposed-args -verify %s
+
+#define memset(...) __builtin_memset(__VA_ARGS__)
+#define bzero(x,y) __builtin_memset(x, 0, y)
+#define real_bzero(x,y) __builtin_bzero(x,y)
+
+int array[10];
+int *ptr;
+
+int main() {
+  memset(array, sizeof(array), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(array, sizeof(array), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(ptr), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(ptr, sizeof(*ptr) * 10, 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *), 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *) + 10, 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(char) * sizeof(int *), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(array, sizeof(array), sizeof(array)); // Uh... fine I guess.
+  memset(array, 0, sizeof(array));
+  memset(ptr, 0, sizeof(int *) * 10);
+  memset(array, (int)sizeof(array), (0)); // no warning
+  memset(array, (int)sizeof(array), 32); // no warning
+  memset(array, 32, (0)); // no warning
+
+  bzero(ptr, 0); // expected-warning{{'size' argument to bzero is '0'}} expected-note{{parenthesize the second argument to silence}}
+  real_bzero(ptr, 0); // expected-warning{{'size' argument to bzero is '0'}} expected-note{{parenthesize the second argument to silence}}
+}
+
+void macros() {
+#define ZERO 0
+  int array[10];
+  memset(array, 0xff, ZERO); // no warning
+  // Still emit a diagnostic for memsetting a sizeof expression:
+  memset(array, sizeof(array), ZERO); // expected-warning{{'sizeof'}} expected-note{{cast}}
+  bzero(array, ZERO); // no warning
+  real_bzero(array, ZERO); // no warning
+#define NESTED_DONT_DIAG\
+  memset(array, 0xff, ZERO);\
+  real_bzero(array, ZERO);
+
+  NESTED_DONT_DIAG;
+
+#define NESTED_DO_DIAG  \
+  memset(array, 0xff, 0);   \
+  real_bzero(array, 0)
+
+  NESTED_DO_DIAG; // expected-warning{{'size' argument to memset}} expected-warning{{'size' argument to bzero}} expected-note2{{parenthesize}}
+
+#define FN_MACRO(p) \
+  memset(array, 0xff, p)
+
+  FN_MACRO(ZERO);
+  FN_MACRO(0); // FIXME: should we diagnose this?
+
+  __builtin_memset(array, 0, ZERO); // no warning
+  __builtin_bzero(array, ZERO);
+  __builtin_memset(array, 0, 0); // expected-warning{{'size' argument to memset}} // expected-note{{parenthesize}}
+  __builtin_bzero(array, 0); // expected-warning{{'size' argument to bzero}} // expected-note{{parenthesize}}
+}
Index: clang/lib/Sema/SemaChecking.cpp
===
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -8629,24 +8629,26 @@
   return nullptr;
 }
 
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+  if (const auto *Unary = dyn_cast(E))
+if (Unary->getKind() == UETT_SizeOf)
+  return Unary;
+  return nullptr;
+}
+
 /// If E is a sizeof expression, returns its argument expression,
 /// otherwise returns 

[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-18 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/lib/Sema/SemaChecking.cpp:7975-7976
+static void CheckMemsetSizeof(Sema , unsigned BId, const CallExpr *Call) {
+  if (BId != Builtin::BImemset)
+return;
+

erik.pilkington wrote:
> aaron.ballman wrote:
> > This functionality should apply equally to `wmemset()` as well, should it 
> > not? The only difference I can think of would be that the type should be 
> > cast to `wchar_t` instead of `int` to silence the warning.
> Looks like clang doesn't have a builtin for wmemset, its just defined as a 
> normal function. I suppose that we could still try to diagnose calls to 
> top-level functions named wmemset, but thats unprecedented and doesn't really 
> seem worth the trouble.
I'm fine leaving it off then; I agree it's not worth the trouble.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-17 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added inline comments.



Comment at: clang/lib/Sema/SemaChecking.cpp:7984
+  if (isa(ThirdArg) &&
+  cast(ThirdArg)->getValue() == 0) {
+WarningKind = 0;

> Suppress the diagnostic in cases like `memset(ptr, 0xff, PADDING)`, when 
> `PADDING` is a macro defined to be `0`.

Would it suffice to treat a literal `0xFF` in the exact same way you treat a 
literal `0`? That is,
```
if (SecondArg is integer literal 0 or 255 || ThirdArg involves sizeof) {
do nothing
} else if (ThirdArg is integer literal 0 or 255 || SecondArg involves sizeof) {
diagnose it
}
```
You should add a test case proving that `memset(x, 0, 0)` doesn't get diagnosed 
(assuming the two `0`s come from two different macro expansions, for example).
My sketch above would fail to diagnose `memset(x, 0, 255)`. I don't know if 
this is a feature or a bug. :)  You probably ought to have a test case for 
that, either way, just to demonstrate the expected behavior.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-17 Thread Erik Pilkington via Phabricator via cfe-commits
erik.pilkington planned changes to this revision.
erik.pilkington added a comment.

In https://reviews.llvm.org/D49112#1158793, @thakis wrote:

> lgtm assuming you ran this on some large code base to make sure it doesn't 
> have false positives.


Sorry for the delay here, I was running this on some very large internal code 
bases. This is overwhelmingly true-positive, but there are 2 improvements I 
want to make here before landing this:

- Suppress the diagnostic in cases like `memset(ptr, 0xff, PADDING)`, when 
PADDING is a macro defined to be 0.
- Some platforms #define bzero to `__builtin_memset`, so we should recognize 
when we're in a macro expansion to bzero and emit a more accurate diagnostic 
for `bzero(ary, 0);`. We might as well add support for `__builtin_bzero` too.




Comment at: clang/lib/Sema/SemaChecking.cpp:7975-7976
+static void CheckMemsetSizeof(Sema , unsigned BId, const CallExpr *Call) {
+  if (BId != Builtin::BImemset)
+return;
+

aaron.ballman wrote:
> This functionality should apply equally to `wmemset()` as well, should it 
> not? The only difference I can think of would be that the type should be cast 
> to `wchar_t` instead of `int` to silence the warning.
Looks like clang doesn't have a builtin for wmemset, its just defined as a 
normal function. I suppose that we could still try to diagnose calls to 
top-level functions named wmemset, but thats unprecedented and doesn't really 
seem worth the trouble.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-13 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added inline comments.



Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:662
+def note_suspicious_sizeof_memset_silence : Note<
+  "%select{parenthesize the third argument|cast the second argument to 'int'}0 
to silence">;
+

aaron.ballman wrote:
> erik.pilkington wrote:
> > Quuxplusone wrote:
> > > If it were my codebase, I'd rather see a cast to `(unsigned char)` than a 
> > > cast to `(int)`. (The second argument to memset is supposed to be a 
> > > single byte.) Why did you pick `(int)` specifically?
> > I chose `int` because that's the actual type of the second parameter to 
> > `memset`, it just gets casted down to `unsigned char` internally. FWIW, 
> > either type will suppress the warning. I'm fine with recommending `unsigned 
> > char` if you have a strong preference for it.
> My preference is for `int` as well.
Personally I have a strong preference for "any 8-bit type" — the important 
thing is that it needs to indicate to the reader that it's intended to be the 
single-byte value that `memset` expects. I even want my compiler to diagnose 
`memset(x, 0x1234, z)` as a bug!
But I'm not inclined to fight hard, because this is all about the mechanism of 
*suppressing* of the warning, and I don't imagine we'll see too many people 
trying to suppress it.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-13 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:662
+def note_suspicious_sizeof_memset_silence : Note<
+  "%select{parenthesize the third argument|cast the second argument to 'int'}0 
to silence">;
+

erik.pilkington wrote:
> Quuxplusone wrote:
> > If it were my codebase, I'd rather see a cast to `(unsigned char)` than a 
> > cast to `(int)`. (The second argument to memset is supposed to be a single 
> > byte.) Why did you pick `(int)` specifically?
> I chose `int` because that's the actual type of the second parameter to 
> `memset`, it just gets casted down to `unsigned char` internally. FWIW, 
> either type will suppress the warning. I'm fine with recommending `unsigned 
> char` if you have a strong preference for it.
My preference is for `int` as well.



Comment at: clang/lib/Sema/SemaChecking.cpp:7975-7976
+static void CheckMemsetSizeof(Sema , unsigned BId, const CallExpr *Call) {
+  if (BId != Builtin::BImemset)
+return;
+

This functionality should apply equally to `wmemset()` as well, should it not? 
The only difference I can think of would be that the type should be cast to 
`wchar_t` instead of `int` to silence the warning.



Comment at: clang/lib/Sema/SemaChecking.cpp:7997
+
+
+  // If the function is defined as a builtin macro, do not show macro 
expansion.

Spurious whitespace.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-11 Thread Nico Weber via Phabricator via cfe-commits
thakis accepted this revision.
thakis added a comment.
This revision is now accepted and ready to land.

lgtm assuming you ran this on some large code base to make sure it doesn't have 
false positives.




Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:659
+def warn_suspicious_sizeof_memset : Warning<
+  "%select{'size' argument to memset is '0'|setting buffer to a 'sizeof' 
expression}0; did you mean to transpose the last two arguments?">,
+  InGroup;

nit: stay below 80 columns


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-10 Thread Erik Pilkington via Phabricator via cfe-commits
erik.pilkington added inline comments.



Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:662
+def note_suspicious_sizeof_memset_silence : Note<
+  "%select{parenthesize the third argument|cast the second argument to 'int'}0 
to silence">;
+

Quuxplusone wrote:
> If it were my codebase, I'd rather see a cast to `(unsigned char)` than a 
> cast to `(int)`. (The second argument to memset is supposed to be a single 
> byte.) Why did you pick `(int)` specifically?
I chose `int` because that's the actual type of the second parameter to 
`memset`, it just gets casted down to `unsigned char` internally. FWIW, either 
type will suppress the warning. I'm fine with recommending `unsigned char` if 
you have a strong preference for it.



Comment at: clang/lib/Sema/SemaChecking.cpp:7962
+  if (auto *BO = dyn_cast(SizeofExpr)) {
+if (BO->getOpcode() != BO_Mul)
+  return false;

Quuxplusone wrote:
> Honestly, if the second argument to `memset` involves `sizeof` *at all*, it's 
> probably a bug, isn't it?
> 
> memset(, sizeof foo + 10, 0xff);
> memset(, sizeof foo * sizeof bar, 0xff);
> 
> Should Clang really go out of its way to silence the warning in these cases?
Sure, that seems reasonable. The new patch makes this check less conservative.


https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-10 Thread Erik Pilkington via Phabricator via cfe-commits
erik.pilkington updated this revision to Diff 154833.
erik.pilkington added a comment.

Address @Quuxplusone comments.


https://reviews.llvm.org/D49112

Files:
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaChecking.cpp
  clang/test/Sema/transpose-memset.c

Index: clang/test/Sema/transpose-memset.c
===
--- /dev/null
+++ clang/test/Sema/transpose-memset.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1   -Wmemset-transposed-args -verify %s
+// RUN: %clang_cc1 -xc++ -Wmemset-transposed-args -verify %s
+
+#define memset(...) __builtin_memset(__VA_ARGS__)
+
+int array[10];
+int *ptr;
+
+int main() {
+  memset(array, sizeof(array), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(array, sizeof(array), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(ptr), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(ptr, sizeof(*ptr) * 10, 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *), 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *) + 10, 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(char) * sizeof(int *), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(array, sizeof(array), sizeof(array)); // Uh... fine I guess.
+  memset(array, 0, sizeof(array));
+  memset(ptr, 0, sizeof(int *) * 10);
+  memset(array, (int)sizeof(array), (0)); // no warning
+  memset(array, (int)sizeof(array), 32); // no warning
+  memset(array, 32, (0)); // no warning
+}
Index: clang/lib/Sema/SemaChecking.cpp
===
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -7839,24 +7839,26 @@
   return nullptr;
 }
 
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+  if (const auto *Unary = dyn_cast(E))
+if (Unary->getKind() == UETT_SizeOf)
+  return Unary;
+  return nullptr;
+}
+
 /// If E is a sizeof expression, returns its argument expression,
 /// otherwise returns NULL.
 static const Expr *getSizeOfExprArg(const Expr *E) {
-  if (const UnaryExprOrTypeTraitExpr *SizeOf =
-  dyn_cast(E))
-if (SizeOf->getKind() == UETT_SizeOf && !SizeOf->isArgumentType())
+  if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+if (!SizeOf->isArgumentType())
   return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
-
   return nullptr;
 }
 
 /// If E is a sizeof expression, returns its argument type.
 static QualType getSizeOfArgType(const Expr *E) {
-  if (const UnaryExprOrTypeTraitExpr *SizeOf =
-  dyn_cast(E))
-if (SizeOf->getKind() == UETT_SizeOf)
-  return SizeOf->getTypeOfArgument();
-
+  if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+return SizeOf->getTypeOfArgument();
   return QualType();
 }
 
@@ -7952,6 +7954,56 @@
 
 }
 
+/// Detect if \c SizeofExpr is likely to calculate the sizeof an object.
+static bool doesExprLikelyComputeSize(const Expr *SizeofExpr) {
+  SizeofExpr = SizeofExpr->IgnoreParenImpCasts();
+
+  if (auto *BO = dyn_cast(SizeofExpr)) {
+if (BO->getOpcode() != BO_Mul && BO->getOpcode() != BO_Add)
+  return false;
+
+return doesExprLikelyComputeSize(BO->getLHS()) ||
+   doesExprLikelyComputeSize(BO->getRHS());
+  }
+
+  return getAsSizeOfExpr(SizeofExpr) != nullptr;
+}
+
+/// Diagnose cases like 'memset(buf, sizeof(buf), 0)', which should have the
+/// last two arguments transposed.
+static void CheckMemsetSizeof(Sema , unsigned BId, const CallExpr *Call) {
+  if (BId != Builtin::BImemset)
+return;
+
+  int WarningKind;
+  SourceLocation DiagLoc;
+
+  // If we're memsetting 0 bytes, then this is likely a programmer error.
+  const Expr *ThirdArg = Call->getArg(2)->IgnoreImpCasts();
+  if (isa(ThirdArg) &&
+  cast(ThirdArg)->getValue() == 0) {
+WarningKind = 0;
+DiagLoc = ThirdArg->getExprLoc();
+  }
+  // If the second argument is a sizeof expression and the third isn't, 

[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-09 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added inline comments.



Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:662
+def note_suspicious_sizeof_memset_silence : Note<
+  "%select{parenthesize the third argument|cast the second argument to 'int'}0 
to silence">;
+

If it were my codebase, I'd rather see a cast to `(unsigned char)` than a cast 
to `(int)`. (The second argument to memset is supposed to be a single byte.) 
Why did you pick `(int)` specifically?



Comment at: clang/lib/Sema/SemaChecking.cpp:7962
+  if (auto *BO = dyn_cast(SizeofExpr)) {
+if (BO->getOpcode() != BO_Mul)
+  return false;

Honestly, if the second argument to `memset` involves `sizeof` *at all*, it's 
probably a bug, isn't it?

memset(, sizeof foo + 10, 0xff);
memset(, sizeof foo * sizeof bar, 0xff);

Should Clang really go out of its way to silence the warning in these cases?


Repository:
  rC Clang

https://reviews.llvm.org/D49112



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49112: [Sema] Implement -Wmemset-transposed-args

2018-07-09 Thread Erik Pilkington via Phabricator via cfe-commits
erik.pilkington created this revision.
erik.pilkington added reviewers: rsmith, aaron.ballman, arphaman.
Herald added a subscriber: dexonsmith.

This warning tries to catch programs that incorrectly call memset with the 
second and third arguments transposed, ie `memset(ary, sizeof(ary), 0)` instead 
of `memset(ary, 0, sizeof(ary))`. This is done by looking at two factors: 1) if 
the last argument is `0`, then this is likely a bug (looks this is what GCC's 
implementation does) and 2) if the last argument isn't `0`, but the second 
argument is a `sizeof`expression. This catches cases like `memset(ary, 
sizeof(ary), 0xff)`. I also grouped a couple of related diagnostics that deal 
with these c functions into a new group, -Wsuspicious-memaccess.

llvm.org/PR36242

Thanks for taking a look!
Erik


Repository:
  rC Clang

https://reviews.llvm.org/D49112

Files:
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaChecking.cpp
  clang/test/Sema/transpose-memset.c

Index: clang/test/Sema/transpose-memset.c
===
--- /dev/null
+++ clang/test/Sema/transpose-memset.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1   -Wmemset-transposed-args -verify %s
+// RUN: %clang_cc1 -xc++ -Wmemset-transposed-args -verify %s
+
+#define memset(...) __builtin_memset(__VA_ARGS__)
+
+int array[10];
+int *ptr;
+
+int main() {
+  memset(array, sizeof(array), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(array, sizeof(array), 0xff); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, sizeof(ptr), 0); // expected-warning{{'size' argument to memset is '0'; did you mean to transpose the last two arguments?}} expected-note{{parenthesize the third argument to silence}}
+  memset(ptr, sizeof(*ptr) * 10, 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(ptr, 10 * sizeof(int *), 1); // expected-warning{{setting buffer to a 'sizeof' expression; did you mean to transpose the last two arguments?}} expected-note{{cast the second argument to 'int' to silence}}
+  memset(array, sizeof(array), sizeof(array)); // Uh... fine I guess.
+  memset(array, 0, sizeof(array));
+  memset(ptr, 0, sizeof(int *) * 10);
+  memset(array, (int)sizeof(array), (0)); // no warning
+  memset(array, (int)sizeof(array), 32); // no warning
+  memset(array, 32, (0)); // no warning
+}
Index: clang/lib/Sema/SemaChecking.cpp
===
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -7839,24 +7839,26 @@
   return nullptr;
 }
 
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+  if (const auto *Unary = dyn_cast(E))
+if (Unary->getKind() == UETT_SizeOf)
+  return Unary;
+  return nullptr;
+}
+
 /// If E is a sizeof expression, returns its argument expression,
 /// otherwise returns NULL.
 static const Expr *getSizeOfExprArg(const Expr *E) {
-  if (const UnaryExprOrTypeTraitExpr *SizeOf =
-  dyn_cast(E))
-if (SizeOf->getKind() == UETT_SizeOf && !SizeOf->isArgumentType())
+  if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+if (!SizeOf->isArgumentType())
   return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
-
   return nullptr;
 }
 
 /// If E is a sizeof expression, returns its argument type.
 static QualType getSizeOfArgType(const Expr *E) {
-  if (const UnaryExprOrTypeTraitExpr *SizeOf =
-  dyn_cast(E))
-if (SizeOf->getKind() == UETT_SizeOf)
-  return SizeOf->getTypeOfArgument();
-
+  if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+return SizeOf->getTypeOfArgument();
   return QualType();
 }
 
@@ -7952,6 +7954,57 @@
 
 }
 
+/// Detect if \c E is likely to calculate the sizeof an object.
+static bool doesExprLikelyComputeSize(const Expr *SizeofExpr) {
+  SizeofExpr = SizeofExpr->IgnoreParenImpCasts();
+
+  if (auto *BO = dyn_cast(SizeofExpr)) {
+if (BO->getOpcode() != BO_Mul)
+  return false;
+
+const Expr *LHSSizeof = getAsSizeOfExpr(BO->getLHS()),
+   *RHSSizeof = getAsSizeOfExpr(BO->getRHS());
+return static_cast(LHSSizeof) != static_cast(RHSSizeof);
+  }
+
+  return getAsSizeOfExpr(SizeofExpr) != nullptr;
+}
+
+/// Diagnose cases like 'memset(buf, sizeof(buf), 0)', which should have the
+/// last two arguments transposed.
+static void CheckMemsetSizeof(Sema , unsigned BId, const CallExpr *Call) {
+  if (BId != Builtin::BImemset)
+return;
+
+  int WarningKind;
+  SourceLocation DiagLoc;
+
+  // If we're memsetting 0 bytes, then this