leonardchan updated this revision to Diff 156167.
leonardchan added a comment.

- Added checks for expressions in statements other than declarations or 
expression statements


Repository:
  rC Clang

https://reviews.llvm.org/D49511

Files:
  include/clang/AST/Type.h
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/AST/Type.cpp
  lib/AST/TypePrinter.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaType.cpp
  test/Frontend/noderef.c

Index: test/Frontend/noderef.c
===================================================================
--- /dev/null
+++ test/Frontend/noderef.c
@@ -0,0 +1,161 @@
+// RUN: %clang_cc1 -x c -verify %s
+// RUN: %clang_cc1 -x c++ -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+struct S {
+  int a;
+  int b;
+};
+
+struct S2 {
+  int a[2];
+  int NODEREF a2[2];
+  int *b;
+  int NODEREF *b2;
+  struct S *s;
+  struct S NODEREF *s2;
+};
+
+int NODEREF *func(int NODEREF *arg) {
+  int y = *arg; // expected-warning{{dereference of noderef expression}}
+  return arg;
+}
+
+void func2(int x) {}
+
+int test() {
+  int NODEREF *p;
+  int *p2;
+
+  int x = *p;               // expected-warning{{dereference of noderef expression}}
+  x = *((int NODEREF *)p2); // expected-warning{{dereference of noderef expression}}
+
+  int NODEREF **q;
+  int *NODEREF *q2;
+
+  // Indirection
+  p2 = *q;  // ok
+  x = **q;  // expected-warning{{dereference of noderef expression}}
+  p2 = *q2; // expected-warning{{dereference of noderef expression}}
+
+  **q; // expected-warning{{dereference of noderef expression}}
+       // expected-warning@-1{{expression result unused}}
+
+  p = *&*q;
+  p = **&q;
+  q = &**&q;
+  p = &*p;
+  p = *&p;
+  p = &(*p);
+  p = *(&p);
+  x = **&p; // expected-warning{{dereference of noderef expression}}
+
+  *p = 2;   // expected-warning{{dereference of noderef expression}}
+  *q = p;   // ok
+  **q = 2;  // expected-warning{{dereference of noderef expression}}
+  *q2 = p2; // expected-warning{{dereference of noderef expression}}
+
+  p2 = p;
+  p = p + 1;
+  p = &*(p + 1);
+
+  // Struct member access
+  struct S NODEREF *s;
+  x = s->a;   // expected-warning{{dereference of noderef expression}}
+  x = (*s).b; // expected-warning{{dereference of noderef expression}}
+  p = &s->a;
+  p = &(*s).b;
+
+  // Nested struct access
+  struct S2 NODEREF *s2_noderef;
+  p = s2_noderef->a;  // ok since result is an array in a struct
+  p = s2_noderef->a2; // ok
+  p = s2_noderef->b;  // expected-warning{{dereference of noderef expression}}
+  p = s2_noderef->b2; // expected-warning{{dereference of noderef expression}}
+  s = s2_noderef->s;  // expected-warning{{dereference of noderef expression}}
+  s = s2_noderef->s2; // expected-warning{{dereference of noderef expression}}
+  p = s2_noderef->a + 1;
+
+  struct S2 *s2;
+  p = s2->a;
+  p = s2->a2;
+  p = s2->b;
+  p = s2->b2;
+  s = s2->s;
+  s = s2->s2;
+
+  // Subscript access
+  x = p[1];    // expected-warning{{dereference of noderef expression}}
+  p2 = q[0];   // ok
+  x = q[0][0]; // expected-warning{{dereference of noderef expression}}
+  p2 = q2[0];  // expected-warning{{dereference of noderef expression}}
+
+  int NODEREF arr[10];
+  x = arr[1]; // expected-warning{{dereference of noderef expression}}
+
+  int NODEREF *(arr2[10]);
+  int NODEREF *elem = *arr2;
+
+  int NODEREF(*arr3)[10];
+  elem = *arr3;
+
+  // Combinations between indirection, subscript, and member access
+  struct S2 NODEREF *s2_arr[10];
+  s2 = s2_arr[1];
+  struct S2 NODEREF *s2_arr2[10][10];
+  s2 = s2_arr2[1][1];
+
+  p = s2_arr[1]->a;
+  p = s2_arr[1]->b; // expected-warning{{dereference of noderef expression}}
+  int **bptr = &s2_arr[1]->b;
+
+  x = s2->s2->a;        // expected-warning{{dereference of noderef expression}}
+  x = s2_noderef->a[1]; // expected-warning{{dereference of noderef expression}}
+  p = &s2_noderef->a[1];
+
+  // Functions
+  x = *(func(p)); // expected-warning{{dereference of noderef expression}}
+
+  // Casting is ok
+  q = (int NODEREF **)&p;
+  q = (int NODEREF **)&p2;
+  q = &p;
+  q = &p2;
+  x = s2->s2->a; // expected-warning{{dereference of noderef expression}}
+
+  // Other expressions
+  func2(*p);         // expected-warning{{dereference of noderef expression}}
+  func2(*p + 1);     // expected-warning{{dereference of noderef expression}}
+  func2(!*p);        // expected-warning{{dereference of noderef expression}}
+  func2((x = *p));   // expected-warning{{dereference of noderef expression}}
+  func2((char)(*p)); // expected-warning{{dereference of noderef expression}}
+
+  // Other statements
+  if (*p) {}          // expected-warning{{dereference of noderef expression}}
+  else if (*p) {}     // expected-warning{{dereference of noderef expression}}
+  switch (*p){}       // expected-warning{{dereference of noderef expression}}
+  for (*p; *p; *p){}  // expected-warning{{dereference of noderef expression}}
+                      // expected-warning@-1{{dereference of noderef expression}}
+                      // expected-warning@-2{{dereference of noderef expression}}
+                      // expected-warning@-3{{expression result unused}}
+                      // expected-warning@-4{{expression result unused}}
+  for (*p; *p;){}     // expected-warning{{dereference of noderef expression}}
+                      // expected-warning@-1{{dereference of noderef expression}}
+                      // expected-warning@-2{{expression result unused}}
+  for (*p;; *p){}     // expected-warning{{dereference of noderef expression}}
+                      // expected-warning@-1{{dereference of noderef expression}}
+                      // expected-warning@-2{{expression result unused}}
+                      // expected-warning@-3{{expression result unused}}
+  for (; *p; *p){}    // expected-warning{{dereference of noderef expression}}
+                      // expected-warning@-1{{dereference of noderef expression}}
+                      // expected-warning@-2{{expression result unused}}
+  for (*p;;){}        // expected-warning{{dereference of noderef expression}}
+                      // expected-warning@-1{{expression result unused}}
+  for (;*p;){}        // expected-warning{{dereference of noderef expression}}
+  for (;;*p){}        // expected-warning{{dereference of noderef expression}}
+                      // expected-warning@-1{{expression result unused}}
+  while (*p){}        // expected-warning{{dereference of noderef expression}}
+  do {} while (*p);   // expected-warning{{dereference of noderef expression}}
+  return *p;          // expected-warning{{dereference of noderef expression}}
+}
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -5215,6 +5215,8 @@
     return ParsedAttr::AT_PreserveMost;
   case AttributedType::attr_preserve_all:
     return ParsedAttr::AT_PreserveAll;
+  case AttributedType::attr_noderef:
+    return ParsedAttr::AT_NoDeref;
   case AttributedType::attr_ptr32:
     return ParsedAttr::AT_Ptr32;
   case AttributedType::attr_ptr64:
@@ -7299,6 +7301,12 @@
       attr.setUsedAsTypeAttr();
       break;
 
+    case ParsedAttr::AT_NoDeref:
+      type = state.getSema().Context.getAttributedType(
+          AttributedType::attr_noderef, type, type);
+      attr.setUsedAsTypeAttr();
+      break;
+
     MS_TYPE_ATTRS_CASELIST:
       if (!handleMSPointerTypeQualifierAttr(state, attr, type))
         attr.setUsedAsTypeAttr();
Index: lib/Sema/SemaStmt.cpp
===================================================================
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -55,6 +55,9 @@
   // void expression for its side effects.  Conversion to void allows any
   // operand, even incomplete types.
 
+  if (!FE.isInvalid())
+    NoDerefChecker(*this).Visit(FE.get());
+
   // Same thing in for stmt first clause (when expr) and third clause.
   return StmtResult(FE.getAs<Stmt>());
 }
@@ -77,7 +80,12 @@
   // If we have an invalid decl, just return an error.
   if (DG.isNull()) return StmtError();
 
-  return new (Context) DeclStmt(DG, StartLoc, EndLoc);
+  StmtResult Res = new (Context) DeclStmt(DG, StartLoc, EndLoc);
+
+  if (!Res.isInvalid())
+    NoDerefChecker(*this).Visit(Res.get());
+
+  return Res;
 }
 
 void Sema::ActOnForEachDeclStmt(DeclGroupPtrTy dg) {
@@ -545,6 +553,9 @@
     DiagnoseEmptyStmtBody(CondExpr->getLocEnd(), thenStmt,
                           diag::warn_empty_if_body);
 
+  if (!Cond.isInvalid())
+    NoDerefChecker(*this).Visit(Cond.get().second);
+
   return BuildIfStmt(IfLoc, IsConstexpr, InitStmt, Cond, thenStmt, ElseLoc,
                      elseStmt);
 }
@@ -679,6 +690,9 @@
   if (CondResult.isInvalid())
     return ExprError();
 
+  if (!CondResult.isInvalid())
+    NoDerefChecker(*this).Visit(CondResult.get());
+
   // C99 6.8.4.2p5 - Integer promotions are performed on the controlling expr.
   return UsualUnaryConversions(CondResult.get());
 }
@@ -1303,6 +1317,9 @@
   if (isa<NullStmt>(Body))
     getCurCompoundScope().setHasEmptyLoopBodies();
 
+  if (CondVal.second)
+    NoDerefChecker(*this).Visit(CondVal.second);
+
   return new (Context)
       WhileStmt(Context, CondVal.first, CondVal.second, Body, WhileLoc);
 }
@@ -1326,6 +1343,9 @@
 
   DiagnoseUnusedExprResult(Body);
 
+  if (Cond)
+    NoDerefChecker(*this).Visit(Cond);
+
   return new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen);
 }
 
@@ -1772,6 +1792,13 @@
   if (isa<NullStmt>(Body))
     getCurCompoundScope().setHasEmptyLoopBodies();
 
+  // Do not check the first stmt because it is already handled by the check in
+  // ExprStmt
+  if (!Second.isInvalid() && Second.get().second)
+    NoDerefChecker(*this).Visit(Second.get().second);
+  if (Third)
+    NoDerefChecker(*this).Visit(Third);
+
   return new (Context)
       ForStmt(Context, First, Second.get().second, Second.get().first, Third,
               Body, ForLoc, LParenLoc, RParenLoc);
@@ -3464,6 +3491,9 @@
 
   CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
 
+  if (RetValExp)
+    NoDerefChecker(*this).Visit(RetValExp);
+
   return R;
 }
 
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -4153,6 +4153,103 @@
   return isa<MSPropertySubscriptExpr>(BaseNoParens);
 }
 
+/// Raise a warning when we land on a dereference whose resulting type is marked
+/// with noderef. Any top level expressions that are an address of operator do
+/// not raise this warning.
+void NoDerefChecker::VisitUnaryOperator(const UnaryOperator *E) {
+  if (VisitedExprs.find(E) != VisitedExprs.end())
+    return;
+
+  if (E->getOpcode() == UO_AddrOf) {
+    VisitedExprs.insert(E);
+    return;
+  } else if (E->getOpcode() == UO_Deref && TypeHasNoDeref(E->getType())) {
+    S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+        << E->getSourceRange();
+    VisitedExprs.insert(E);
+    return;
+  }
+
+  Visit(E->getSubExpr());
+}
+
+/// Raise a warning if we dereference and the result is not an array. Otherwise
+/// keep traversing down.
+void NoDerefChecker::VisitMemberExpr(const MemberExpr *E) {
+  if (VisitedExprs.find(E) != VisitedExprs.end())
+    return;
+
+  if (E->isArrow()) {
+    // We only care if the type is a pointer to a struct that has noderef
+    if (const PointerType *Ptr =
+            dyn_cast<PointerType>(E->getBase()->getType())) {
+      if (TypeHasNoDeref(Ptr->getPointeeType()) &&
+          !isa<ArrayType>(E->getType())) {
+        S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+            << E->getSourceRange();
+        VisitedExprs.insert(E);
+        return;
+      }
+    }
+  }
+
+  Visit(E->getBase());
+}
+
+/// Raise a warning if the resulting type is noderef or we index the field of a
+/// struct that was marked with noderef.
+void NoDerefChecker::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+  if (VisitedExprs.find(E) != VisitedExprs.end())
+    return;
+
+  if (TypeHasNoDeref(E->getType())) {
+    S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+        << E->getSourceRange();
+    VisitedExprs.insert(E);
+    return;
+  }
+
+  const Expr *Base = E->getBase();
+  const QualType &BaseTy = Base->getType();
+
+  // Raise an error if the base type is a pointer to a member access of a struct
+  // marked with noderef.
+  QualType ElemTy;
+  if (const ArrayType *ArrayTy = dyn_cast<ArrayType>(BaseTy)) {
+    ElemTy = ArrayTy->getElementType();
+  } else if (const PointerType *PointerTy = dyn_cast<PointerType>(BaseTy)) {
+    ElemTy = PointerTy->getPointeeType();
+  } else {
+    // Not a pointer access
+    Visit(Base);
+    return;
+  }
+
+  if (const MemberExpr *Member =
+          dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) {
+    QualType MemberBaseTy = Member->getBase()->getType();
+    if (const PointerType *Ptr = dyn_cast<PointerType>(MemberBaseTy)) {
+      if (TypeHasNoDeref(Ptr->getPointeeType())) {
+        S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+            << E->getSourceRange();
+        VisitedExprs.insert(E);
+        return;
+      }
+    }
+  }
+
+  // We do not need to check the index since it is an integer
+  Visit(E->getBase());
+}
+
+bool NoDerefChecker::TypeHasNoDeref(const QualType &Ty) {
+  if (const AttributedType *AT = dyn_cast<AttributedType>(Ty)) {
+    AttributedType::Kind AttrKind = AT->getAttrKind();
+    return AttrKind == AttributedType::attr_noderef;
+  }
+  return false;
+}
+
 ExprResult
 Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
                               Expr *idx, SourceLocation rbLoc) {
Index: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -1557,6 +1557,9 @@
   case AttributedType::attr_preserve_all:
     OS << "preserve_all";
     break;
+  case AttributedType::attr_noderef:
+    OS << "noderef";
+    break;
   }
   OS << "))";
 }
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -3192,6 +3192,7 @@
   case AttributedType::attr_preserve_all:
   case AttributedType::attr_ms_abi:
   case AttributedType::attr_sysv_abi:
+  case AttributedType::attr_noderef:
   case AttributedType::attr_ptr32:
   case AttributedType::attr_ptr64:
   case AttributedType::attr_sptr:
@@ -3218,6 +3219,7 @@
 
 bool AttributedType::isCallingConv() const {
   switch (getAttrKind()) {
+  case attr_noderef:
   case attr_ptr32:
   case attr_ptr64:
   case attr_sptr:
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -20,6 +20,7 @@
 #include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
@@ -58,6 +59,7 @@
 #include <deque>
 #include <memory>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 namespace llvm {
@@ -10782,6 +10784,25 @@
   Decl *D;
 };
 
+/// Report warnings on expressions that dereference a type marked with a
+/// noderef attribute.
+class NoDerefChecker : public ConstEvaluatedExprVisitor<NoDerefChecker> {
+public:
+  typedef ConstEvaluatedExprVisitor<NoDerefChecker> Inherited;
+
+  NoDerefChecker(Sema &S) : Inherited(S.Context), S(S) {}
+
+  void VisitUnaryOperator(const UnaryOperator *E);
+  void VisitMemberExpr(const MemberExpr *E);
+  void VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
+
+private:
+  static bool TypeHasNoDeref(const QualType &Ty);
+
+  Sema &S;
+  std::unordered_set<const Expr *> VisitedExprs;
+};
+
 } // end namespace clang
 
 namespace llvm {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -9367,4 +9367,7 @@
    "member '%2' is missing|"
    "the type is not trivially copyable|"
    "the type does not have the expected form}1">;
+
+def warn_dereference_of_noderef_type : Warning<
+  "dereference of noderef expression">, InGroup<IgnoredAttributes>;
 } // end of sema component.
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -3348,3 +3348,49 @@
 corresponding line within the inlined callee.
   }];
 }
+
+def NoDerefDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``noderef`` attribute allows for showing a warning whenever a pointer marked with
+this attribute is dereferenced. This is ideally used with pointers that point to special
+memory which cannot be read from or written to, but allowing for the pointer to be used in
+pointer arithmetic. The following are examples of valid expressions where a warning may
+be raised:
+
+.. code-block:: c
+
+  int __attribute__((noderef)) *p;
+  int x = *p;  // warning
+
+  int __attribute__((noderef)) **p2;
+  x = **p2;  // warning
+
+  int * __attribute__((noderef)) *p3;
+  p = *p3;  // warning
+
+  struct S {
+    int a;
+  };
+  struct S __attribute__((noderef)) *s;
+  x = s->a;    // warning
+  x = (*s).a;  // warning
+
+Not all dereferences may raise a warning if the value directed by the pointer may not be
+accessed. The following are examples of valid expressions where no warning may be raised:
+
+.. code-block:: c
+
+  int *q;
+  int __attribute__((noderef)) *p;
+  q = &*p;
+  q = *&p;
+
+  struct S {
+    int a;
+  };
+  struct S __attribute__((noderef)) *s;
+  p = &s->a;
+  p = &(*s).a;
+}];
+}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1727,6 +1727,11 @@
   let Documentation = [RegparmDocs];
 }
 
+def NoDeref : TypeAttr {
+  let Spellings = [Clang<"noderef">];
+  let Documentation = [NoDerefDocs];
+}
+
 def ReqdWorkGroupSize : InheritableAttr {
   // Does not have a [[]] spelling because it is an OpenCL-related attribute.
   let Spellings = [GNU<"reqd_work_group_size">];
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -4206,6 +4206,7 @@
     LastEnumOperandKind = attr_pcs_vfp,
 
     // No operand.
+    attr_noderef,
     attr_noreturn,
     attr_nocf_check,
     attr_cdecl,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to