NoQ updated this revision to Diff 552560.
NoQ added a comment.

Rebase.

Make warnings a bit more verbose than before. This plays nicely with our 
attempts to categorize unemitted fixits via `-mllvm -debug-only=SafeBuffers`, 
but this time it doesn't make sense to make the extra information debug-only; 
it makes perfect sense as part of the warning.

TODO: Some new code paths aren't covered by tests yet (captured variables, 
static member variables).


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146773/new/

https://reviews.llvm.org/D146773

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-c-linkage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-debug.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-main.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -41,34 +41,34 @@
 // expected-warning@-2{{'pp' is an unsafe pointer used for buffer access}}
   foo(p[1],             // expected-note{{used in buffer access here}}
       pp[1][1],         // expected-note{{used in buffer access here}}
-                        // expected-warning@-1{{unsafe buffer access}}
+                        // expected-warning@-1{{unsafe buffer access through raw pointer parameter variable 'pp'}}
       1[1[pp]],         // expected-note{{used in buffer access here}}
-                        // expected-warning@-1{{unsafe buffer access}}
+                        // expected-warning@-1{{unsafe buffer access through raw pointer parameter variable 'pp'}}
       1[pp][1]          // expected-note{{used in buffer access here}}
-                        // expected-warning@-1{{unsafe buffer access}}
+                        // expected-warning@-1{{unsafe buffer access through raw pointer parameter variable 'pp'}}
       );
 
   if (p[3]) {           // expected-note{{used in buffer access here}}
     void * q = p;
 
-    foo(((int*)q)[10]); // expected-warning{{unsafe buffer access}}
+    foo(((int*)q)[10]); // expected-warning{{unsafe buffer access through raw pointer expression}}
   }
 
-  foo(((int*)voidPtrCall())[3], // expected-warning{{unsafe buffer access}}
-      3[(int*)voidPtrCall()],   // expected-warning{{unsafe buffer access}}
-      charPtrCall()[3],         // expected-warning{{unsafe buffer access}}
-      3[charPtrCall()]          // expected-warning{{unsafe buffer access}}
+  foo(((int*)voidPtrCall())[3], // expected-warning{{unsafe buffer access through raw pointer expression}}
+      3[(int*)voidPtrCall()],   // expected-warning{{unsafe buffer access through raw pointer expression}}
+      charPtrCall()[3],         // expected-warning{{unsafe buffer access through raw pointer return value of function 'charPtrCall'}}
+      3[charPtrCall()]          // expected-warning{{unsafe buffer access through raw pointer return value of function 'charPtrCall'}}
       );
 
     int a[10];          // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
     int b[10][10];      // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
 
   foo(a[1], 1[a],   // expected-note2{{used in buffer access here}}
-      b[3][4],      // expected-warning{{unsafe buffer access}}
+      b[3][4],      // expected-warning{{unsafe buffer access into raw array local variable 'b'}}
                     // expected-note@-1{{used in buffer access here}}
-      4[b][3],      // expected-warning{{unsafe buffer access}}
+      4[b][3],      // expected-warning{{unsafe buffer access into raw array local variable 'b'}}
                     // expected-note@-1{{used in buffer access here}}
-      4[3[b]]);     // expected-warning{{unsafe buffer access}}
+      4[3[b]]);     // expected-warning{{unsafe buffer access into raw array local variable 'b'}}
                     // expected-note@-1{{used in buffer access here}}
 
   // Not to warn when index is zero
@@ -108,7 +108,7 @@
       q[1], 1[q], q[-1],    // expected-note3{{used in buffer access here}}
       a[1],                 // expected-note{{used in buffer access here}}     `a` is of pointer type
       b[1][2]               // expected-note{{used in buffer access here}}     `b[1]` is of array type
-                            // expected-warning@-1{{unsafe buffer access}}
+                            // expected-warning@-1{{unsafe buffer access into raw array parameter variable 'b'}}
       );
 }
 
@@ -127,26 +127,26 @@
 T_t * funRetTStar();
 
 void testStructMembers(struct T * sp, struct T s, T_t * sp2, T_t s2) {
-  foo(sp->a[1],     // expected-warning{{unsafe buffer access}}
-      sp->b[1],     // expected-warning{{unsafe buffer access}}
-      sp->c.a[1],   // expected-warning{{unsafe buffer access}}
-      sp->c.b[1],   // expected-warning{{unsafe buffer access}}
-      s.a[1],       // expected-warning{{unsafe buffer access}}
-      s.b[1],       // expected-warning{{unsafe buffer access}}
-      s.c.a[1],     // expected-warning{{unsafe buffer access}}
-      s.c.b[1],     // expected-warning{{unsafe buffer access}}
-      sp2->a[1],    // expected-warning{{unsafe buffer access}}
-      sp2->b[1],    // expected-warning{{unsafe buffer access}}
-      sp2->c.a[1],  // expected-warning{{unsafe buffer access}}
-      sp2->c.b[1],  // expected-warning{{unsafe buffer access}}
-      s2.a[1],      // expected-warning{{unsafe buffer access}}
-      s2.b[1],      // expected-warning{{unsafe buffer access}}
-      s2.c.a[1],           // expected-warning{{unsafe buffer access}}
-      s2.c.b[1],           // expected-warning{{unsafe buffer access}}
-      funRetT().a[1],      // expected-warning{{unsafe buffer access}}
-      funRetT().b[1],      // expected-warning{{unsafe buffer access}}
-      funRetTStar()->a[1], // expected-warning{{unsafe buffer access}}
-      funRetTStar()->b[1]  // expected-warning{{unsafe buffer access}}
+  foo(sp->a[1],     // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      sp->b[1],     // expected-warning{{unsafe buffer access through raw pointer member variable 'b}}
+      sp->c.a[1],   // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      sp->c.b[1],   // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      s.a[1],       // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      s.b[1],       // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      s.c.a[1],     // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      s.c.b[1],     // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      sp2->a[1],    // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      sp2->b[1],    // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      sp2->c.a[1],  // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      sp2->c.b[1],  // expected-warning{{nsafe buffer access through raw pointer member variable 'b'}}
+      s2.a[1],      // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      s2.b[1],      // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      s2.c.a[1],           // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      s2.c.b[1],           // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      funRetT().a[1],      // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      funRetT().b[1],      // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
+      funRetTStar()->a[1], // expected-warning{{unsafe buffer access into raw array member variable 'a'}}
+      funRetTStar()->b[1]  // expected-warning{{unsafe buffer access through raw pointer member variable 'b'}}
       );
 }
 
@@ -209,9 +209,9 @@
   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
   foo(p[1],       // expected-note{{used in buffer access here}}
       p[1].a[1],  // expected-note{{used in buffer access here}}
-                  // expected-warning@-1{{unsafe buffer access}}
+                  // expected-warning@-1{{unsafe buffer access into raw array member variable 'a'}}
       p[1].b[1]   // expected-note{{used in buffer access here}}
-                  // expected-warning@-1{{unsafe buffer access}}
+                  // expected-warning@-1{{unsafe buffer access through raw pointer member variable 'b'}}
       );
 }
 
@@ -238,26 +238,25 @@
   auto y = &a[0]; // expected-warning{{'y' is an unsafe pointer used for buffer access}}
 
   foo(p + 1, 1 + p, p - 1,      // expected-note3{{used in pointer arithmetic here}}
-      *q + 1, 1 + *q, *q - 1,   // expected-warning3{{unsafe pointer arithmetic}}
+      *q + 1, 1 + *q, *q - 1,   // expected-warning3{{unsafe arithmetic over raw pointer expression}}
       x + 1, 1 + x, x - 1,      // expected-note3{{used in pointer arithmetic here}}
       y + 1, 1 + y, y - 1,      // expected-note3{{used in pointer arithmetic here}}
-      getPtr() + 1, 1 + getPtr(), getPtr() - 1 // expected-warning3{{unsafe pointer arithmetic}}
+      getPtr() + 1, 1 + getPtr(), getPtr() - 1 // expected-warning3{{unsafe arithmetic over raw pointer return value of function 'getPtr'}}
       );
 
   p += 1;  p -= 1;  // expected-note2{{used in pointer arithmetic here}}
-  *q += 1; *q -= 1; // expected-warning2{{unsafe pointer arithmetic}}
+  *q += 1; *q -= 1; // expected-warning2{{unsafe arithmetic over raw pointer expression}}
   y += 1; y -= 1;   // expected-note2{{used in pointer arithmetic here}}
   x += 1; x -= 1;   // expected-note2{{used in pointer arithmetic here}}
 }
 
 void testTemplate(int * p) {
   int *a[10];
-  foo(f(p, &p, a, a)[1]); // expected-warning{{unsafe buffer access}}
-                          // FIXME: expected note@-1{{in instantiation of function template specialization 'f<int *, 10>' requested here}}
+  foo(f(p, &p, a, a)[1]); // expected-warning{{unsafe buffer access through raw pointer return value of function 'f<int *, 10>'}}
 
   const int **q = const_cast<const int **>(&p);
 
-  testPointerArithmetic(p, q, p); //FIXME: expected note{{in instantiation of}}
+  testPointerArithmetic(p, q, p);
 }
 
 void testPointerToMember() {
@@ -270,7 +269,7 @@
   int * S_t::* q = &S_t::y;
 
   foo(S.*p,
-      (S.*q)[1]);  // expected-warning{{unsafe buffer access}}
+      (S.*q)[1]);  // expected-warning{{unsafe buffer access through raw pointer expression}}
 }
 
 // test that nested callable definitions are scanned only once
@@ -362,9 +361,9 @@
     int d = cArr[0][0];
     foo(cArr[0][0]);
     foo(cArr[1][2]);        // expected-note{{used in buffer access here}}
-                            // expected-warning@-1{{unsafe buffer access}}
+                            // expected-warning@-1{{unsafe buffer access into raw array local variable 'cArr'}}
     auto cPtr = cArr[1][2]; // expected-note{{used in buffer access here}}
-                            // expected-warning@-1{{unsafe buffer access}}
+                            // expected-warning@-1{{unsafe buffer access into raw array local variable 'cArr'}}
     foo(cPtr);
 
     // Typdefs
@@ -372,7 +371,7 @@
     const A tArr = {4, 5, 6};
     // expected-warning@-1{{'tArr' is an unsafe buffer that does not perform bounds checks}}
     foo(tArr[0], tArr[1]);  // expected-note{{used in buffer access here}}
-    return cArr[0][1];      // expected-warning{{unsafe buffer access}}
+    return cArr[0][1];      // expected-warning{{unsafe buffer access into raw array local variable 'cArr'}}
 }
 
 void testArrayPtrArithmetic(int x[]) { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp
@@ -56,11 +56,11 @@
 
   x += 5;  // \
   // ON-note    {{used in pointer arithmetic here}} \
-  // OFF-warning{{unsafe pointer arithmetic}} \
+  // OFF-warning{{unsafe arithmetic over raw pointer parameter variable 'x'}} \
   // OFF-note   {{pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions}}
 
   bar(x);  // \
-  // ON-warning{{function introduces unsafe buffer manipulation}} \
-  // OFF-warning{{function introduces unsafe buffer manipulation}} \
+  // ON-warning{{function call introduces unsafe buffer manipulation over expression}} \
+  // OFF-warning{{function call introduces unsafe buffer manipulation over expression}} \
   // OFF-note   {{pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
@@ -184,7 +184,7 @@
   local = ptr;
   local++;  // expected-note{{used in pointer arithmetic here}}
 
-  (local = ptr) += 5;  // expected-warning{{unsafe pointer arithmetic}}
+  (local = ptr) += 5;  // expected-warning{{unsafe arithmetic over raw pointer expression}}
 }
 
 void check_rhs_fix() {
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp
@@ -19,11 +19,11 @@
 void deprecatedFunction4(int z);
 
 void caller(int z, int* x, int size, char c[]) {
-    deprecatedFunction3(); // expected-warning{{function introduces unsafe buffer manipulation}}
-    deprecatedFunction4(z); // expected-warning{{function introduces unsafe buffer manipulation}}
+    deprecatedFunction3(); // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
+    deprecatedFunction4(z); // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
     someFunction();
 
-    overloading(x); // expected-warning{{function introduces unsafe buffer manipulation}}
+    overloading(x); // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
     overloading(x, size);
     overloading(c);
 }
@@ -47,12 +47,12 @@
 void foo<int *>(int *t) {}
 
 void caller1(int *p, int *q) {
-    testVariadics(p, q);  // expected-warning{{function introduces unsafe buffer manipulation}}
-    adder(p, q);  // expected-warning{{function introduces unsafe buffer manipulation}}
-    
+    testVariadics(p, q);  // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
+    adder(p, q);  // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
+
     int x;
     foo(x);
-    foo(&x);  // expected-warning{{function introduces unsafe buffer manipulation}}
+    foo(&x);  // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
 }
 
 // Test virtual functions
@@ -75,13 +75,13 @@
 void testInheritance() {
     DerivedClass DC;
     DC.func();
-    DC.func1();  // expected-warning{{function introduces unsafe buffer manipulation}}
-    
+    DC.func1();  // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
+
     BaseClass *BC;
-    BC->func();  // expected-warning{{function introduces unsafe buffer manipulation}}
+    BC->func();  // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
     BC->func1();
-    
+
     BC = &DC;
-    BC->func();  // expected-warning{{function introduces unsafe buffer manipulation}}
+    BC->func();  // expected-warning{{function call introduces unsafe buffer manipulation over expression}}
     BC->func1();
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
@@ -113,12 +113,15 @@
 
 // The following two unsupported cases are not specific to
 // parm-fixits. Adding them here in case they get forgotten.
-void isArrayDecayToPointerUPC(int a[][10], int (*b)[10]) {
-// expected-warning@-1{{'a' is an unsafe pointer used for buffer access}}
-// expected-warning@-2{{'b' is an unsafe pointer used for buffer access}}
+void isArrayDecayToPointerUPC(int a[][10], int (*b)[10]) { // \
+  // expected-warning{{'a' is an unsafe pointer used for buffer access}} \
+  // expected-warning{{'b' is an unsafe pointer used for buffer access}}
   int tmp;
 
-  tmp = a[5][5] + b[5][5];  // expected-warning2{{unsafe buffer access}}  expected-note2{{used in buffer access here}}
+  tmp = a[5][5] + b[5][5];  // \
+  // expected-warning{{unsafe buffer access into raw array parameter variable 'a'}} \
+  // expected-warning{{unsafe buffer access into raw array parameter variable 'b'}} \
+  // expected-note2{{used in buffer access here}}
 }
 
 // parameter having default values:
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-main.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-main.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-main.cpp
@@ -9,5 +9,5 @@
 int main(int argc, char *argv[]) { // expected-warning{{'argv' is an unsafe pointer used for buffer access}}
   char tmp;
   tmp = argv[5][5];                // expected-note{{used in buffer access here}} \
-				      expected-warning{{unsafe buffer access}}
+                                   // expected-warning{{unsafe buffer access through raw pointer parameter variable 'argv'}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-debug.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-debug.cpp
@@ -95,6 +95,6 @@
 
 void test_struct_claim_use() {
   auto [x] = f();
-  x[6] = 8;  // expected-warning{{unsafe buffer access}}
-  x++;  // expected-warning{{unsafe pointer arithmetic}}
+  x[6] = 8;  // expected-warning{{unsafe buffer access through raw pointer structured binding 'x'}}
+  x++;  // expected-warning{{unsafe arithmetic over raw pointer structured binding 'x'}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-c-linkage.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-c-linkage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-c-linkage.cpp
@@ -2,7 +2,7 @@
 
 extern "C" {
 void foo(int *ptr) {
-  ptr[5] = 10;  // expected-warning{{unsafe buffer access}}
+  ptr[5] = 10;  // expected-warning{{unsafe buffer access through raw pointer parameter variable 'ptr'}}
 }
 
 void bar(int *ptr);
@@ -13,12 +13,12 @@
 }
 
 void bar(int *ptr) {
-  ptr[5] = 10;  // expected-warning{{unsafe buffer access}}
+  ptr[5] = 10;  // expected-warning{{unsafe buffer access through raw pointer parameter variable 'ptr'}}
 }
 
 void call_foo(int *p) {
   foo(p);
   struct c_struct str;
-  str.name[7] = 9;  // expected-warning{{unsafe buffer access}}
+  str.name[7] = 9;  // expected-warning{{unsafe buffer access through raw pointer member variable 'name'}}
   bar(p);
 }
Index: clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp
===================================================================
--- clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp
+++ clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp
@@ -48,7 +48,7 @@
 
 void testRefersPtrStructFieldDecl(int i) {
   Struct1 s1;
-  s1.ptr + i;     // expected-warning{{unsafe pointer arithmetic}}
+  s1.ptr + i;     // expected-warning{{unsafe arithmetic over raw pointer member variable 'ptr'}}
   s1.ptr[i];      // expected-warning{{unsafe buffer access}}
 }
 
@@ -67,7 +67,7 @@
   int * ptr;      // FIXME: per-declaration warning aggregated at the struct definition
 
   void testRefersPtrStructFieldDecl(int i) {
-    ptr + i;     // expected-warning{{unsafe pointer arithmetic}}
+    ptr + i;     // expected-warning{{unsafe arithmetic over raw pointer member variable 'ptr'}}
     ptr[i];      // expected-warning{{unsafe buffer access}}
   }
 };
@@ -104,6 +104,6 @@
 int * return_ptr();
 
 void testNoDeclRef(int i) {
-  return_ptr() + i;   // expected-warning{{unsafe pointer arithmetic}}
+  return_ptr() + i;   // expected-warning{{unsafe arithmetic over raw pointer return value of function 'return_ptr'}}
   return_ptr()[i];    // expected-warning{{unsafe buffer access}}
 }
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2210,57 +2210,259 @@
     return AllVars;
   }
 
-public:
-  UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions)
-    : S(S), SuggestSuggestions(SuggestSuggestions) {}
+  // These enums map to diagnostic %select directives.
+  enum class OperationKindTy: unsigned {
+    AnyOperation = 0,
+    PointerArithmetic = 1,
+    BufferAccessThroughPointer = 2,
+    BufferAccessIntoArray = 3,
+    CallToUnsafeFunction = 4
+  };
 
-  void handleUnsafeOperation(const Stmt *Operation,
-                             bool IsRelatedToDecl) override {
+  enum class IsCapturedTy: unsigned {
+    NoOrUnclear = 0,
+    Yes = 1
+  };
+
+  enum class LayoutKindTy: unsigned {
+    Unclear = 0,
+    Pointer = 1,
+    Array = 2,
+  };
+
+  enum class StorageKindTy: unsigned {
+    AnyExpression = 0,
+    AnyVariable = 1,
+    LocalVariable = 2,
+    ParameterVariable = 3,
+    StaticLocalVariable = 4,
+    GlobalVariable = 5,
+    MemberVariable = 6,
+    StaticMemberVariable = 7,
+    StructuredBinding = 8,
+    FunctionReturnValue = 9
+  };
+
+  struct OperandStorageWarningInfo {
+    IsCapturedTy IsCaptured;
+    StorageKindTy StorageKind;
+    const NamedDecl *Object;
+  };
+
+  struct OperandWarningInfo {
+    LayoutKindTy LayoutKind;
+    OperandStorageWarningInfo StorageInfo;
     SourceLocation Loc;
     SourceRange Range;
-    unsigned MsgParam = 0;
+  };
+
+  struct OperationWarningInfo {
+    OperationKindTy OperationKind;
+    OperandWarningInfo OperandInfo;
+  };
+
+  StorageKindTy describeVariableStorageKind(const VarDecl *VD) {
+    if (VD->isLocalVarDecl())
+      return StorageKindTy::LocalVariable;
+
+    if (isa<ParmVarDecl>(VD))
+      return StorageKindTy::ParameterVariable;
+
+    if (VD->isStaticLocal())
+      return StorageKindTy::StaticLocalVariable;
+
+    if (VD->hasGlobalStorage())
+      return StorageKindTy::GlobalVariable;
+
+    // FIXME: Implement more cases?
+    // What about __block variables?
+    // Thread locals?
+    return StorageKindTy::AnyVariable;
+  }
+
+  OperandStorageWarningInfo dontDescribeOperandStorage() {
+    return {IsCapturedTy::NoOrUnclear /*unclear*/, StorageKindTy::AnyExpression,
+            nullptr /*no object declaration*/};
+  }
+
+  OperandStorageWarningInfo describeOperandStorage(const Expr *Operand) {
+    if (const auto *DRE = dyn_cast<DeclRefExpr>(Operand)) {
+      const ValueDecl *ObjD = DRE->getDecl();
+      IsCapturedTy IsCaptured = DRE->refersToEnclosingVariableOrCapture()
+                                    ? IsCapturedTy::Yes
+                                    : IsCapturedTy::NoOrUnclear /*no*/;
+
+      if (const auto *VD = dyn_cast<VarDecl>(ObjD))
+        return {IsCaptured, describeVariableStorageKind(VD), VD};
+
+      if (const auto *BD = dyn_cast<BindingDecl>(ObjD))
+        return {IsCaptured, StorageKindTy::StructuredBinding, BD};
+
+      // FIXME: Are there other storage kinds that we don't support?
+    }
+
+    if (const auto *ME = dyn_cast<MemberExpr>(Operand)) {
+      const ValueDecl *MemberD = ME->getMemberDecl();
+
+      if (const auto *VD = dyn_cast<VarDecl>(MemberD)) {
+        assert(VD->isStaticDataMember() &&
+               "Non-static member VarDecl!");
+        return {IsCapturedTy::NoOrUnclear /*unclear (FIXME!)*/,
+                StorageKindTy::StaticMemberVariable, VD};
+      }
+
+      if (const auto *FD = dyn_cast<FieldDecl>(MemberD))
+        return {IsCapturedTy::NoOrUnclear /*unclear (FIXME!)*/,
+                StorageKindTy::MemberVariable, FD};
+
+      // FIXME: Documentation says that the other two cases are:
+      //   * a CXXMethodDecl (producing pointer-to-member-method) and
+      //   * an EnumConstantDecl (enum value).
+      // They probably can't appear here, make this an assert?
+      // Are we sure there aren't other cases?
+    }
+
+    if (const auto *CE = dyn_cast<CallExpr>(Operand)) {
+      const auto *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl());
+      return {IsCapturedTy::NoOrUnclear /*no*/,
+              StorageKindTy::FunctionReturnValue, ND};
+    }
+
+    // Multi-dimensional case.
+    // FIXME: Say it out loud, and then explain if it's array of arrays or
+    // array of pointers etc.
+    if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(Operand)) {
+      return describeOperandStorage(ASE->getBase()->IgnoreParenImpCasts());
+    }
+
+    // Default behavior: say nothing.
+    return dontDescribeOperandStorage();
+  }
+
+  OperandWarningInfo dontDescribeOperand(const Stmt *Operation) {
+    return {
+      LayoutKindTy::Unclear,
+      dontDescribeOperandStorage(),
+      Operation->getBeginLoc(),
+      Operation->getSourceRange()
+    };
+  }
+
+  OperandWarningInfo describeOperand(const Expr *Operand) {
+    Operand = Operand->IgnoreParenImpCasts();
+
+    LayoutKindTy LayoutKind = Operand->getType()->isAnyPointerType()
+                                  ? LayoutKindTy::Pointer
+                                  : LayoutKindTy::Array;
+    return {
+      LayoutKind,
+      describeOperandStorage(Operand),
+      Operand->getBeginLoc(),
+      Operand->getSourceRange()
+    };
+  }
+
+  OperationWarningInfo describeOperation(const Stmt *Operation) {
     if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(Operation)) {
-      Loc = ASE->getBase()->getExprLoc();
-      Range = ASE->getBase()->getSourceRange();
-      MsgParam = 2;
-    } else if (const auto *BO = dyn_cast<BinaryOperator>(Operation)) {
+      OperandWarningInfo OpI = describeOperand(ASE->getBase());
+      switch (OpI.LayoutKind) {
+      case LayoutKindTy::Unclear:
+        llvm_unreachable("Operand layout actively described as unclear!");
+      case LayoutKindTy::Pointer:
+        return {OperationKindTy::BufferAccessThroughPointer, OpI};
+      case LayoutKindTy::Array:
+        return {OperationKindTy::BufferAccessIntoArray, OpI};
+      }
+    }
+
+    if (const auto *BO = dyn_cast<BinaryOperator>(Operation)) {
       BinaryOperator::Opcode Op = BO->getOpcode();
       if (Op == BO_Add || Op == BO_AddAssign || Op == BO_Sub ||
           Op == BO_SubAssign) {
         if (BO->getRHS()->getType()->isIntegerType()) {
-          Loc = BO->getLHS()->getExprLoc();
-          Range = BO->getLHS()->getSourceRange();
+          return {OperationKindTy::PointerArithmetic,
+                  describeOperand(BO->getLHS())};
         } else {
-          Loc = BO->getRHS()->getExprLoc();
-          Range = BO->getRHS()->getSourceRange();
+          return {OperationKindTy::PointerArithmetic,
+                  describeOperand(BO->getRHS())};
         }
-        MsgParam = 1;
       }
-    } else if (const auto *UO = dyn_cast<UnaryOperator>(Operation)) {
+    }
+
+    if (const auto *UO = dyn_cast<UnaryOperator>(Operation)) {
       UnaryOperator::Opcode Op = UO->getOpcode();
       if (Op == UO_PreInc || Op == UO_PreDec || Op == UO_PostInc ||
           Op == UO_PostDec) {
-        Loc = UO->getSubExpr()->getExprLoc();
-        Range = UO->getSubExpr()->getSourceRange();
-        MsgParam = 1;
-      }
-    } else {
-      if (isa<CallExpr>(Operation)) {
-        // note_unsafe_buffer_operation doesn't have this mode yet.
-        assert(!IsRelatedToDecl && "Not implemented yet!");
-        MsgParam = 3;
+        return {OperationKindTy::PointerArithmetic,
+                describeOperand(UO->getSubExpr())};
       }
-      Loc = Operation->getBeginLoc();
-      Range = Operation->getSourceRange();
     }
+
+    if (isa<CallExpr>(Operation)) {
+      return {OperationKindTy::CallToUnsafeFunction,
+              dontDescribeOperand(Operation)};
+    }
+
+    // Every time this is reached, it means we needed to do better above.
+    return {OperationKindTy::AnyOperation, dontDescribeOperand(Operation)};
+  }
+
+public:
+  UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions)
+    : S(S), SuggestSuggestions(SuggestSuggestions) {}
+
+  void handleUnsafeOperation(const Stmt *Operation,
+                             bool IsRelatedToDecl) override {
+    OperationWarningInfo Info = describeOperation(Operation);
+
     if (IsRelatedToDecl) {
       assert(!SuggestSuggestions &&
              "Variables blamed for unsafe buffer usage without suggestions!");
-      S.Diag(Loc, diag::note_unsafe_buffer_operation) << MsgParam << Range;
+      assert(Info.OperationKind != OperationKindTy::CallToUnsafeFunction &&
+             "Not implemented yet!");
+
+      unsigned OperationKind;
+      switch (Info.OperationKind) {
+        case OperationKindTy::AnyOperation:
+          OperationKind = 0;
+          break;
+        case OperationKindTy::PointerArithmetic:
+          OperationKind = 1;
+          break;
+        case OperationKindTy::BufferAccessThroughPointer:
+          OperationKind = 2;
+          break;
+        case OperationKindTy::BufferAccessIntoArray:
+          OperationKind = 2; // We don't have a separate mode for this.
+          break;
+        case OperationKindTy::CallToUnsafeFunction:
+          llvm_unreachable("Not implemented yet!");
+      }
+
+      S.Diag(Info.OperandInfo.Loc, diag::note_unsafe_buffer_operation)
+          << OperationKind << Info.OperandInfo.Range;
     } else {
-      S.Diag(Loc, diag::warn_unsafe_buffer_operation) << MsgParam << Range;
+      // Introduce a scope so that the warning got emitted first.
+      {
+        auto D =
+            S.Diag(Info.OperandInfo.Loc, diag::warn_unsafe_buffer_operation);
+        D << (unsigned)Info.OperationKind
+          << (unsigned)Info.OperandInfo.StorageInfo.IsCaptured
+          << (unsigned)Info.OperandInfo.LayoutKind
+          << (unsigned)Info.OperandInfo.StorageInfo.StorageKind;
+        if (Info.OperandInfo.StorageInfo.Object) {
+          D << 1 /*provide the object name*/
+            << Info.OperandInfo.StorageInfo.Object;
+
+        } else {
+          D << 0 /*don't provide object name*/;
+        }
+        D << Info.OperandInfo.Range;
+      }
+
       if (SuggestSuggestions) {
-        S.Diag(Loc, diag::note_safe_buffer_usage_suggestions_disabled);
+        S.Diag(Info.OperandInfo.Loc,
+               diag::note_safe_buffer_usage_suggestions_disabled);
       }
     }
   }
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11902,9 +11902,27 @@
   "does not perform bounds checks}1">,
   InGroup<UnsafeBufferUsage>, DefaultIgnore;
 def warn_unsafe_buffer_operation : Warning<
-  "%select{unsafe pointer operation|unsafe pointer arithmetic|"
-  "unsafe buffer access|function introduces unsafe buffer manipulation}0">,
-  InGroup<UnsafeBufferUsage>, DefaultIgnore;
+  "%select{"
+    "unsafe buffer operation in|"
+    "unsafe arithmetic over|"
+    "unsafe buffer access through|"
+    "unsafe buffer access into|"
+    "function call introduces unsafe buffer manipulation over"
+  "}0%select{"
+    "|" // Either not captured or unclear.
+    " captured"
+  "}1 %select{|raw pointer |raw array }2%select{"
+    "expression|"
+    "variable|"
+    "local variable|"
+    "parameter variable|"
+    "static local variable|"
+    "global variable|"
+    "member variable|"
+    "static member variable|"
+    "structured binding|"
+    "return value of function"
+  "}3%select{| %5}4">, InGroup<UnsafeBufferUsage>, DefaultIgnore;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
 def note_unsafe_buffer_variable_fixit_group : Note<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D146773: [-Wunsafe... Artem Dergachev via Phabricator via cfe-commits

Reply via email to