ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: t-rasmud, NoQ, jkorous, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- Use `Strategy` to determine whether to fix a parameter.
- Fix the `Strategy` construction so that only variables on the graph are 
assigned the `std::span` strategy
- Extend `PointerAssignmentGadget` and `PointerInitGadget` to generate fix-its 
for cases where the left-hand side has `won't fix` strategy and the right-hand 
side has `std::span` strategy.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D157441

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-multi-parm-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-ptr-init.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.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
@@ -36,9 +36,11 @@
 void * voidPtrCall(void);
 char * charPtrCall(void);
 
-void testArraySubscripts(int *p, int **pp) {
-// expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
-// expected-warning@-2{{'pp' is an unsafe pointer used for buffer access}}
+void testArraySubscripts(int *p, int **pp) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} \
+					        expected-warning{{'pp' is an unsafe pointer used for buffer access}} \
+                                                expected-note{{change type of 'p' to 'std::span' to preserve bounds information, and change 'pp' to safe types to make function 'testArraySubscripts' bounds-safe}} \
+                                                expected-note{{change type of 'pp' to 'std::span' to preserve bounds information, and change 'p' to safe types to make function 'testArraySubscripts' bounds-safe}}
+  
   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}}
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
@@ -65,7 +65,8 @@
 
 void rhs_span2() {
   int *q = new int[6];
-  int *p = q;  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  int *p = q;  // expected-warning{{'p' is an unsafe pointer used for buffer access}} \
+	          expected-note{{change type of 'p' to 'std::span' to preserve bounds information, and change 'q' to 'std::span' to propagate bounds information between them}}
   p[5] = 10;  // expected-note{{used in buffer access here}}
   int *r = q;
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-ptr-init.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-ptr-init.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-ptr-init.cpp
@@ -25,7 +25,8 @@
 // FIXME: Suggest fixits for p, q, and r since span a valid fixit for r.
 void rhs_span3() {
   int *q = new int[6];
-  int *p = q;  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  int *p = q;  // expected-warning{{'p' is an unsafe pointer used for buffer access}} \
+	          expected-note{{change type of 'p' to 'std::span' to preserve bounds information, and change 'q' to 'std::span' to propagate bounds information between them}}
   p[5] = 10;  // expected-note{{used in buffer access here}}
   int *r = q;
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-multi-parm-span.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-multi-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-multi-parm-span.cpp
@@ -2,11 +2,12 @@
 // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits -fblocks -fsafe-buffer-usage-suggestions \
 // RUN:            %s 2>&1 | FileCheck %s
 
-// FIXME: what about possible diagnostic message non-determinism?
-
 // CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]:
 void parmsNoFix(int *p, int *q) {
   int * a = new int[10];
+  // CHECK: fix-it:{{.*}}:{[[@LINE+3]]:3-[[@LINE+3]]:12}:"std::span<int> b"
+  // CHECK: fix-it:{{.*}}:{[[@LINE+2]]:13-[[@LINE+2]]:13}:"{"
+  // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:24-[[@LINE+1]]:24}:", 10}"  
   int * b = new int[10]; //expected-warning{{'b' is an unsafe pointer used for buffer access}} \
 			   expected-note{{change type of 'b' to 'std::span' to preserve bounds information}}
 
@@ -27,7 +28,7 @@
   int * b; //expected-warning{{'b' is an unsafe pointer used for buffer access}} \
 	     expected-note{{change type of 'b' to 'std::span' to preserve bounds information, and change 'a' to 'std::span' to propagate bounds information between them}}
 
-  b = a;
+  b = a;    // CHECK-NOT: fix-it:{{.*}}:{[[@LINE]]
   b[5] = 5; // expected-note{{used in buffer access here}}
   p[5] = 5; // expected-note{{used in buffer access here}}
 }
@@ -78,11 +79,11 @@
               expected-note{{change type of 'z' to 'std::span' to preserve bounds information, and change 'x', 'p', and 'r' to safe types to make function 'multiParmLocalAllFix' bounds-safe}}
   int * y;
 
-  x = p;
-  y = x;
+  x = p;  // CHECK-NOT: fix-it:{{.*}}:{[[@LINE]]
+  y = x;  // CHECK: fix-it:{{.*}}:{[[@LINE]]:8-[[@LINE]]:8}:".data()"
   // `x` needs to be fixed so does the pointer assigned to `x`, i.e.,`p`
   x[5] = 5; // expected-note{{used in buffer access here}}
-  z = r;
+  z = r;  // CHECK-NOT: fix-it:{{.*}}:{[[@LINE]]
   // `z` needs to be fixed so does the pointer assigned to `z`, i.e.,`r`
   z[5] = 5; // expected-note{{used in buffer access here}}
   // Since `p` and `r` are parameters need to be fixed together,
@@ -109,10 +110,10 @@
   // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
   int * y;
 
-  p = x;
-  y = x;
+  p = x;    // CHECK-NOT: fix-it:{{.*}}:{[[@LINE]]
+  y = x;    // CHECK: fix-it:{{.*}}:{[[@LINE]]:8-[[@LINE]]:8}:".data()"
   p[5] = 5; // expected-note{{used in buffer access here}}
-  r = z;
+  r = z;    // CHECK-NOT: fix-it:{{.*}}:{[[@LINE]]
   r[5] = 5; // expected-note{{used in buffer access here}}
 }
 // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void  multiParmLocalAllFix2(int *p, int * r) {return multiParmLocalAllFix2(std::span<int>(p, <# size #>), std::span<int>(r, <# size #>));}\n"
@@ -285,7 +286,7 @@
   // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:3-[[@LINE+1]]:10}:"std::span<int> y"
   int * y;   // expected-warning{{'y' is an unsafe pointer used for buffer access}} \
 	        expected-note{{change type of 'y' to 'std::span' to preserve bounds information, and change 'x' to 'std::span' to propagate bounds information between them}}
-  y = x;
+  y = x;       // CHECK-NOT: fix-it:{{.*}}:{[[@LINE]]
   tmp = y[5];  // expected-note{{used in buffer access here}}
 }
 // CHECK-NOT: fix-it:{{.*}}:{[[@LINE-1]]
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===================================================================
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -13,6 +13,7 @@
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/iterator_range.h"
 #include <memory>
 #include <optional>
 #include <sstream>
@@ -1218,44 +1219,6 @@
   return false;
 }
 
-std::optional<FixItList>
-PointerAssignmentGadget::getFixits(const Strategy &S) const {
-  const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
-  const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
-  switch (S.lookup(LeftVD)) {
-    case Strategy::Kind::Span:
-      if (S.lookup(RightVD) == Strategy::Kind::Span)
-        return FixItList{};
-      return std::nullopt;
-    case Strategy::Kind::Wontfix:
-      return std::nullopt;
-    case Strategy::Kind::Iterator:
-    case Strategy::Kind::Array:
-    case Strategy::Kind::Vector:
-      llvm_unreachable("unsupported strategies for FixableGadgets");
-  }
-  return std::nullopt;
-}
-
-std::optional<FixItList>
-PointerInitGadget::getFixits(const Strategy &S) const {
-  const auto *LeftVD = PtrInitLHS;
-  const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
-  switch (S.lookup(LeftVD)) {
-    case Strategy::Kind::Span:
-      if (S.lookup(RightVD) == Strategy::Kind::Span)
-        return FixItList{};
-      return std::nullopt;
-    case Strategy::Kind::Wontfix:
-      return std::nullopt;
-    case Strategy::Kind::Iterator:
-    case Strategy::Kind::Array:
-    case Strategy::Kind::Vector:
-    llvm_unreachable("unsupported strategies for FixableGadgets");
-  }
-  return std::nullopt;
-}
-
 std::optional<FixItList>
 ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
   if (const auto *DRE =
@@ -1530,6 +1493,59 @@
   return SpanOpen + EltTyText.str() + '>';
 }
 
+std::optional<FixItList>
+PointerAssignmentGadget::getFixits(const Strategy &S) const {
+  const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
+  const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
+  switch (S.lookup(LeftVD)) {
+  case Strategy::Kind::Span:
+    if (S.lookup(RightVD) == Strategy::Kind::Span)
+      return FixItList{};
+    return std::nullopt;
+  case Strategy::Kind::Wontfix: {
+    if (S.lookup(RightVD) == Strategy::Kind::Span) {
+      const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
+          RightVD->getASTContext();
+      if (auto EndLocOfRHS =
+              getPastLoc(PtrRHS, Ctx.getSourceManager(), Ctx.getLangOpts()))
+        return FixItList{FixItHint::CreateInsertion(*EndLocOfRHS, ".data()")};
+    }
+    return std::nullopt;
+  }
+  case Strategy::Kind::Iterator:
+  case Strategy::Kind::Array:
+  case Strategy::Kind::Vector:
+    llvm_unreachable("unsupported strategies for FixableGadgets");
+  }
+  return std::nullopt;
+}
+
+std::optional<FixItList> PointerInitGadget::getFixits(const Strategy &S) const {
+  const auto *LeftVD = PtrInitLHS;
+  const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
+  switch (S.lookup(LeftVD)) {
+  case Strategy::Kind::Span:
+    if (S.lookup(RightVD) == Strategy::Kind::Span)
+      return FixItList{};
+    return std::nullopt;
+  case Strategy::Kind::Wontfix: {
+    if (S.lookup(RightVD) == Strategy::Kind::Span) {
+      const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
+          RightVD->getASTContext();
+      if (auto EndLocOfRHS =
+              getPastLoc(PtrInitRHS, Ctx.getSourceManager(), Ctx.getLangOpts()))
+        return FixItList{FixItHint::CreateInsertion(*EndLocOfRHS, ".data()")};
+    }
+    return std::nullopt;
+  }
+  case Strategy::Kind::Iterator:
+  case Strategy::Kind::Array:
+  case Strategy::Kind::Vector:
+    llvm_unreachable("unsupported strategies for FixableGadgets");
+  }
+  return std::nullopt;
+}
+
 std::optional<FixItList>
 DerefSimplePtrArithFixableGadget::getFixits(const Strategy &s) const {
   const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
@@ -1605,12 +1621,10 @@
     CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
         Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
     // Inserts the [0]
-    std::optional<SourceLocation> EndOfOperand =
-        getEndCharLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts());
-    if (EndOfOperand) {
+    if (auto LocPastOperand =
+            getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
       return FixItList{{FixItHint::CreateRemoval(derefRange),
-                        FixItHint::CreateInsertion(
-                            (*EndOfOperand).getLocWithOffset(1), "[0]")}};
+                        FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
     }
     break;
   }
@@ -1948,19 +1962,9 @@
 // [[clang::unsafe_buffer_usage]] void f(int *p) {  // added def
 //   return f(std::span(p, <# size #>));
 // }
-//
-// The actual fix-its may contain more details, e.g., the attribute may be guard
-// by a macro
-//   #if __has_cpp_attribute(clang::unsafe_buffer_usage)
-//   [[clang::unsafe_buffer_usage]]
-//   #endif
-//
-// `ParmsMask` is an array of size of `FD->getNumParams()` such
-// that `ParmsMask[i]` is true iff the `i`-th parameter will be fixed with some
-// strategy.
 static std::optional<FixItList>
-createOverloadsForFixedParams(const std::vector<bool> &ParmsMask, const Strategy &S,
-                              const FunctionDecl *FD, const ASTContext &Ctx,
+createOverloadsForFixedParams(const Strategy &S, const FunctionDecl *FD,
+                              const ASTContext &Ctx,
                               UnsafeBufferUsageHandler &Handler) {
   // FIXME: need to make this conflict checking better:
   if (hasConflictingOverload(FD))
@@ -1970,21 +1974,36 @@
   const LangOptions &LangOpts = Ctx.getLangOpts();
   const unsigned NumParms = FD->getNumParams();
   std::vector<std::string> NewTysTexts(NumParms);
+  std::vector<bool> ParmsMask(NumParms, false);
+  bool AtLeastOneParmToFix = false;
 
   for (unsigned i = 0; i < NumParms; i++) {
-    if (!ParmsMask[i])
-      continue;
+    const ParmVarDecl *PVD = FD->getParamDecl(i);
 
+    if (S.lookup(PVD) == Strategy::Kind::Wontfix)
+      continue;
+    if (S.lookup(PVD) != Strategy::Kind::Span) {
+      // Not supported, not suppose to happen:
+      DEBUG_NOTE_UNEXPECTED_ISSUE(PVD,
+                                  "unsupported strategy kind for parameter " +
+                                      PVD->getNameAsString());
+      return std::nullopt;
+    }
     std::optional<Qualifiers> PteTyQuals = std::nullopt;
     std::optional<std::string> PteTyText =
-        getPointeeTypeText(FD->getParamDecl(i), SM, LangOpts, &PteTyQuals);
+        getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
 
     if (!PteTyText)
       // something wrong in obtaining the text of the pointee type, give up
       return std::nullopt;
     // FIXME: whether we should create std::span type depends on the Strategy.
     NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
+    ParmsMask[i] = true;
+    AtLeastOneParmToFix = true;
   }
+  if (!AtLeastOneParmToFix)
+    // No need to create function overloads:
+    return {};
   // FIXME Respect indentation of the original code.
 
   // A lambda that creates the text representation of a function declaration
@@ -2287,28 +2306,17 @@
     const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
     const Strategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler) {
   FixItList FixItsSharedByParms{};
-  std::vector<bool> ParmsNeedFixMask(FD->getNumParams(), false);
-  const VarDecl *FirstParmNeedsFix = nullptr;
-
-  for (unsigned i = 0; i < FD->getNumParams(); i++)
-    if (FixItsForVariable.count(FD->getParamDecl(i))) {
-      ParmsNeedFixMask[i] = true;
-      FirstParmNeedsFix = FD->getParamDecl(i);
-    }
-  if (FirstParmNeedsFix) {
-    // In case at least one parameter needs to be fixed:
-    std::optional<FixItList> OverloadFixes =
-        createOverloadsForFixedParams(ParmsNeedFixMask, S, FD, Ctx, Handler);
+  std::optional<FixItList> OverloadFixes =
+      createOverloadsForFixedParams(S, FD, Ctx, Handler);
 
-    if (OverloadFixes) {
-      FixItsSharedByParms.append(*OverloadFixes);
-    } else {
-      // Something wrong in generating `OverloadFixes`, need to remove the
-      // whole group, where parameters are in, from `FixItsForVariable` (Note
-      // that all parameters should be in the same group):
-      for (auto *Member : VarGrpMgr.getGroupOfVar(FirstParmNeedsFix))
-        FixItsForVariable.erase(Member);
-    }
+  if (OverloadFixes) {
+    FixItsSharedByParms.append(*OverloadFixes);
+  } else {
+    // Something wrong in generating `OverloadFixes`, need to remove the
+    // whole group, where parameters are in, from `FixItsForVariable` (Note
+    // that all parameters should be in the same group):
+    for (auto *Member : VarGrpMgr.getGroupOfParms())
+      FixItsForVariable.erase(Member);
   }
   return FixItsSharedByParms;
 }
@@ -2413,8 +2421,9 @@
   return FinalFixItsForVariable;
 }
 
+template <typename VarDeclIterTy>
 static Strategy
-getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) {
+getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
   Strategy S;
   for (const VarDecl *VD : UnsafeVars) {
     S.set(VD, Strategy::Kind::Span);
@@ -2448,6 +2457,10 @@
       return std::nullopt;
     return Groups[VarGrpMap.at(Var)];
   }
+
+  VarGrpRef getGroupOfParms() const override {
+    return GrpsUnionForParms.getArrayRef();
+  }
 };
 
 void clang::checkUnsafeBufferUsage(const Decl *D,
@@ -2700,7 +2713,10 @@
       ++I;
   }
 
-  Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
+  // We assign strategies to variables on the graph.  Other variables have the
+  // default "Won't fix" strategy.
+  Strategy NaiveStrategy = getNaiveStrategy(
+      llvm::make_range(VisitedVars.begin(), VisitedVars.end()));
   VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
 
   if (isa<NamedDecl>(D))
Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
===================================================================
--- clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -35,6 +35,10 @@
   /// variables, where `Var` is in, contains parameters.
   virtual VarGrpRef getGroupOfVar(const VarDecl *Var,
                                   bool *HasParm = nullptr) const;
+
+  /// Returns the non-empty group of variables that include parameters of the
+  /// analyzing function, if such a group exists.  An empty group, otherwise.
+  virtual VarGrpRef getGroupOfParms() const;
 };
 
 /// The interface that lets the caller handle unsafe buffer usage analysis
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to