[clang] [-Wunsafe-buffer-usage] Add FixableGadget for AddAssign in UnspecifiedUntypedContext (PR #71862)

2023-11-13 Thread Ziqing Luo via cfe-commits


@@ -1028,6 +1028,50 @@ class UPCPreIncrementGadget : public FixableGadget {
   }
 };
 
+// Representing a pointer type expression of the form `Ptr += n` in an
+// Unspecified Untyped Context (UUC):
+class UUCAddAssignGadget : public FixableGadget {
+private:
+  static constexpr const char *const UUCAddAssignTag =
+  "PointerAddAssignUnderUUC";
+  static constexpr const char *const IntOffsetTag = "IntOffset";
+  static constexpr const char *const OffsetTag = "Offset";
+
+  const BinaryOperator *Node; // the `Ptr += n` node
+  const IntegerLiteral *IntOffset = nullptr;
+  const DeclRefExpr *Offset = nullptr;
+
+public:
+  UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
+  : FixableGadget(Kind::UUCAddAssign),
+Node(Result.Nodes.getNodeAs(UUCAddAssignTag)),
+IntOffset(Result.Nodes.getNodeAs(IntOffsetTag)),
+Offset(Result.Nodes.getNodeAs(OffsetTag)) {
+assert(Node != nullptr && "Expecting a non-null matching result");
+  }
+
+  static bool classof(const Gadget *G) {
+return G->getKind() == Kind::UUCAddAssign;
+  }
+
+  static Matcher matcher() {
+return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
+binaryOperator(
+hasOperatorName("+="), hasLHS(declRefExpr(toSupportedVariable())),
+hasRHS(expr(anyOf(ignoringImpCasts(declRefExpr().bind(OffsetTag)),
+  integerLiteral().bind(IntOffsetTag)

ziqingluo-90 wrote:

For RHS,  if it is not a constant,  all we need to know about it is whether it 
is an unsigned integer.It doesn't have to be a DRE, e.g., `p += (x + 1)` 
can be transformed to `p = p.subspan(x + 1)` as long as `(x + 1)` has unsigned 
integer type. 

https://github.com/llvm/llvm-project/pull/71862
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-04 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/74020

>From 00083bb1573561361ff0782f425ebec2a5218f34 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 4 Dec 2023 14:37:10 -0800
Subject: [PATCH] Thread safety analysis: Fix a bug in handling temporary
 constructors

Extends the lifetime of the map `ConstructedObjects` to be of the
whole CFG so that the map can connect temporary Ctor and Dtor in
different CFG blocks.
---
 clang/lib/Analysis/ThreadSafety.cpp   | 27 +--
 .../SemaCXX/warn-thread-safety-analysis.cpp   |  8 ++
 2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
b/clang/lib/Analysis/ThreadSafety.cpp
index 7fdf22c2f3919..b65d4dfab8dea 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1010,6 +1010,8 @@ class ThreadSafetyAnalyzer {
   ThreadSafetyHandler &Handler;
   const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
+  // Maps constructed objects to `this` placeholder prior to initialization.
+  llvm::SmallDenseMap ConstructedObjects;
   FactManager FactMan;
   std::vector BlockInfo;
 
@@ -1530,7 +1532,6 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
 }
 
 namespace {
-
 /// We use this class to visit different types of expressions in
 /// CFGBlocks, and build up the lockset.
 /// An expression may cause us to add or remove locks from the lockset, or else
@@ -1543,8 +1544,6 @@ class BuildLockset : public 
ConstStmtVisitor {
   FactSet FSet;
   // The fact set for the function on exit.
   const FactSet &FunctionExitFSet;
-  /// Maps constructed objects to `this` placeholder prior to initialization.
-  llvm::SmallDenseMap ConstructedObjects;
   LocalVariableMap::Context LVarCtx;
   unsigned CtxIndex;
 
@@ -1808,7 +1807,7 @@ void BuildLockset::handleCall(const Expr *Exp, const 
NamedDecl *D,
   std::pair Placeholder =
   Analyzer->SxBuilder.createThisPlaceholder(Exp);
   [[maybe_unused]] auto inserted =
-  ConstructedObjects.insert({Exp, Placeholder.first});
+  Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
   assert(inserted.second && "Are we visiting the same expression again?");
   if (isa(Exp))
 Self = Placeholder.first;
@@ -2128,10 +2127,10 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
 E = EWC->getSubExpr()->IgnoreParens();
   E = UnpackConstruction(E);
 
-  if (auto Object = ConstructedObjects.find(E);
-  Object != ConstructedObjects.end()) {
+  if (auto Object = Analyzer->ConstructedObjects.find(E);
+  Object != Analyzer->ConstructedObjects.end()) {
 Object->second->setClangDecl(VD);
-ConstructedObjects.erase(Object);
+Analyzer->ConstructedObjects.erase(Object);
   }
 }
   }
@@ -2140,11 +2139,11 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
 void BuildLockset::VisitMaterializeTemporaryExpr(
 const MaterializeTemporaryExpr *Exp) {
   if (const ValueDecl *ExtD = Exp->getExtendingDecl()) {
-if (auto Object =
-ConstructedObjects.find(UnpackConstruction(Exp->getSubExpr()));
-Object != ConstructedObjects.end()) {
+if (auto Object = Analyzer->ConstructedObjects.find(
+UnpackConstruction(Exp->getSubExpr()));
+Object != Analyzer->ConstructedObjects.end()) {
   Object->second->setClangDecl(ExtD);
-  ConstructedObjects.erase(Object);
+  Analyzer->ConstructedObjects.erase(Object);
 }
   }
 }
@@ -2487,15 +2486,15 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
 
   // Clean up constructed object even if there are no attributes to
   // keep the number of objects in limbo as small as possible.
-  if (auto Object = LocksetBuilder.ConstructedObjects.find(
+  if (auto Object = LocksetBuilder.Analyzer->ConstructedObjects.find(
   TD.getBindTemporaryExpr()->getSubExpr());
-  Object != LocksetBuilder.ConstructedObjects.end()) {
+  Object != LocksetBuilder.Analyzer->ConstructedObjects.end()) {
 const auto *DD = TD.getDestructorDecl(AC.getASTContext());
 if (DD->hasAttrs())
   // TODO: the location here isn't quite correct.
   LocksetBuilder.handleCall(nullptr, DD, Object->second,
 
TD.getBindTemporaryExpr()->getEndLoc());
-LocksetBuilder.ConstructedObjects.erase(Object);
+LocksetBuilder.Analyzer->ConstructedObjects.erase(Object);
   }
   break;
 }
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp 
b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 205cfa284f6c9..dfb966d3b5902 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1702,6 +1702,8 @@ struct TestScopedLockable {
 
   bool 

[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-04 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

@aaronpuchert Thanks for your comment!  I have addressed them.   Do you mind to 
take an another look and approve it if it looks good to you?

https://github.com/llvm/llvm-project/pull/74020
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-04 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/74020

>From 81701bfc9052eed567abb76ac05d44cb99298303 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 4 Dec 2023 14:37:10 -0800
Subject: [PATCH] Thread safety analysis: Fix a bug in handling temporary
 constructors

Extends the lifetime of the map `ConstructedObjects` to be of the
whole CFG so that the map can connect temporary Ctor and Dtor in
different CFG blocks.
---
 clang/lib/Analysis/ThreadSafety.cpp   | 27 +--
 .../SemaCXX/warn-thread-safety-analysis.cpp   |  8 ++
 2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
b/clang/lib/Analysis/ThreadSafety.cpp
index 7fdf22c2f3919..f3c07faa90eb6 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1010,6 +1010,8 @@ class ThreadSafetyAnalyzer {
   ThreadSafetyHandler &Handler;
   const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
+  // Maps constructed objects to `this` placeholder prior to initialization.
+  llvm::SmallDenseMap ConstructedObjects;
   FactManager FactMan;
   std::vector BlockInfo;
 
@@ -1530,7 +1532,6 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
 }
 
 namespace {
-
 /// We use this class to visit different types of expressions in
 /// CFGBlocks, and build up the lockset.
 /// An expression may cause us to add or remove locks from the lockset, or else
@@ -1543,8 +1544,6 @@ class BuildLockset : public 
ConstStmtVisitor {
   FactSet FSet;
   // The fact set for the function on exit.
   const FactSet &FunctionExitFSet;
-  /// Maps constructed objects to `this` placeholder prior to initialization.
-  llvm::SmallDenseMap ConstructedObjects;
   LocalVariableMap::Context LVarCtx;
   unsigned CtxIndex;
 
@@ -1808,7 +1807,7 @@ void BuildLockset::handleCall(const Expr *Exp, const 
NamedDecl *D,
   std::pair Placeholder =
   Analyzer->SxBuilder.createThisPlaceholder(Exp);
   [[maybe_unused]] auto inserted =
-  ConstructedObjects.insert({Exp, Placeholder.first});
+  Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
   assert(inserted.second && "Are we visiting the same expression again?");
   if (isa(Exp))
 Self = Placeholder.first;
@@ -2128,10 +2127,10 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
 E = EWC->getSubExpr()->IgnoreParens();
   E = UnpackConstruction(E);
 
-  if (auto Object = ConstructedObjects.find(E);
-  Object != ConstructedObjects.end()) {
+  if (auto Object = Analyzer->ConstructedObjects.find(E);
+  Object != Analyzer->ConstructedObjects.end()) {
 Object->second->setClangDecl(VD);
-ConstructedObjects.erase(Object);
+Analyzer->ConstructedObjects.erase(Object);
   }
 }
   }
@@ -2140,11 +2139,11 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
 void BuildLockset::VisitMaterializeTemporaryExpr(
 const MaterializeTemporaryExpr *Exp) {
   if (const ValueDecl *ExtD = Exp->getExtendingDecl()) {
-if (auto Object =
-ConstructedObjects.find(UnpackConstruction(Exp->getSubExpr()));
-Object != ConstructedObjects.end()) {
+if (auto Object = Analyzer->ConstructedObjects.find(
+UnpackConstruction(Exp->getSubExpr()));
+Object != Analyzer->ConstructedObjects.end()) {
   Object->second->setClangDecl(ExtD);
-  ConstructedObjects.erase(Object);
+  Analyzer->ConstructedObjects.erase(Object);
 }
   }
 }
@@ -2487,15 +2486,15 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
 
   // Clean up constructed object even if there are no attributes to
   // keep the number of objects in limbo as small as possible.
-  if (auto Object = LocksetBuilder.ConstructedObjects.find(
+  if (auto Object = ConstructedObjects.find(
   TD.getBindTemporaryExpr()->getSubExpr());
-  Object != LocksetBuilder.ConstructedObjects.end()) {
+  Object != ConstructedObjects.end()) {
 const auto *DD = TD.getDestructorDecl(AC.getASTContext());
 if (DD->hasAttrs())
   // TODO: the location here isn't quite correct.
   LocksetBuilder.handleCall(nullptr, DD, Object->second,
 
TD.getBindTemporaryExpr()->getEndLoc());
-LocksetBuilder.ConstructedObjects.erase(Object);
+ConstructedObjects.erase(Object);
   }
   break;
 }
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp 
b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 205cfa284f6c9..dfb966d3b5902 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1702,6 +1702,8 @@ struct TestScopedLockable {
 
   bool getBool();
 
+  bool lock2Bool(MutexLock);
+
   void foo1() {
 MutexLoc

[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-04 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/74020

>From 9c2718abce31d61c079e0945313fe14ad0f334b3 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 4 Dec 2023 14:37:10 -0800
Subject: [PATCH] Thread safety analysis: Fix a bug in handling temporary
 constructors

Extends the lifetime of the map `ConstructedObjects` to be of the
whole CFG so that the map can connect temporary Ctor and Dtor in
different CFG blocks.
---
 clang/lib/Analysis/ThreadSafety.cpp   | 26 +--
 .../SemaCXX/warn-thread-safety-analysis.cpp   |  8 ++
 2 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
b/clang/lib/Analysis/ThreadSafety.cpp
index 7fdf22c2f3919..e25b843c9bf83 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1010,6 +1010,8 @@ class ThreadSafetyAnalyzer {
   ThreadSafetyHandler &Handler;
   const FunctionDecl *CurrentFunction;
   LocalVariableMap LocalVarMap;
+  // Maps constructed objects to `this` placeholder prior to initialization.
+  llvm::SmallDenseMap ConstructedObjects;
   FactManager FactMan;
   std::vector BlockInfo;
 
@@ -1543,8 +1545,6 @@ class BuildLockset : public 
ConstStmtVisitor {
   FactSet FSet;
   // The fact set for the function on exit.
   const FactSet &FunctionExitFSet;
-  /// Maps constructed objects to `this` placeholder prior to initialization.
-  llvm::SmallDenseMap ConstructedObjects;
   LocalVariableMap::Context LVarCtx;
   unsigned CtxIndex;
 
@@ -1808,7 +1808,7 @@ void BuildLockset::handleCall(const Expr *Exp, const 
NamedDecl *D,
   std::pair Placeholder =
   Analyzer->SxBuilder.createThisPlaceholder(Exp);
   [[maybe_unused]] auto inserted =
-  ConstructedObjects.insert({Exp, Placeholder.first});
+  Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
   assert(inserted.second && "Are we visiting the same expression again?");
   if (isa(Exp))
 Self = Placeholder.first;
@@ -2128,10 +2128,10 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
 E = EWC->getSubExpr()->IgnoreParens();
   E = UnpackConstruction(E);
 
-  if (auto Object = ConstructedObjects.find(E);
-  Object != ConstructedObjects.end()) {
+  if (auto Object = Analyzer->ConstructedObjects.find(E);
+  Object != Analyzer->ConstructedObjects.end()) {
 Object->second->setClangDecl(VD);
-ConstructedObjects.erase(Object);
+Analyzer->ConstructedObjects.erase(Object);
   }
 }
   }
@@ -2140,11 +2140,11 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
 void BuildLockset::VisitMaterializeTemporaryExpr(
 const MaterializeTemporaryExpr *Exp) {
   if (const ValueDecl *ExtD = Exp->getExtendingDecl()) {
-if (auto Object =
-ConstructedObjects.find(UnpackConstruction(Exp->getSubExpr()));
-Object != ConstructedObjects.end()) {
+if (auto Object = Analyzer->ConstructedObjects.find(
+UnpackConstruction(Exp->getSubExpr()));
+Object != Analyzer->ConstructedObjects.end()) {
   Object->second->setClangDecl(ExtD);
-  ConstructedObjects.erase(Object);
+  Analyzer->ConstructedObjects.erase(Object);
 }
   }
 }
@@ -2487,15 +2487,15 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
 
   // Clean up constructed object even if there are no attributes to
   // keep the number of objects in limbo as small as possible.
-  if (auto Object = LocksetBuilder.ConstructedObjects.find(
+  if (auto Object = ConstructedObjects.find(
   TD.getBindTemporaryExpr()->getSubExpr());
-  Object != LocksetBuilder.ConstructedObjects.end()) {
+  Object != ConstructedObjects.end()) {
 const auto *DD = TD.getDestructorDecl(AC.getASTContext());
 if (DD->hasAttrs())
   // TODO: the location here isn't quite correct.
   LocksetBuilder.handleCall(nullptr, DD, Object->second,
 
TD.getBindTemporaryExpr()->getEndLoc());
-LocksetBuilder.ConstructedObjects.erase(Object);
+ConstructedObjects.erase(Object);
   }
   break;
 }
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp 
b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 205cfa284f6c9..dfb966d3b5902 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1702,6 +1702,8 @@ struct TestScopedLockable {
 
   bool getBool();
 
+  bool lock2Bool(MutexLock);
+
   void foo1() {
 MutexLock mulock(&mu1);
 a = 5;
@@ -1718,6 +1720,12 @@ struct TestScopedLockable {
 MutexLock{&mu1}, a = 5;
   }
 
+  void temporary_cfg(int x) {
+// test the case where a pair of temporary Ctor and Dtor is in different 
CFG blocks
+lock2Bool(MutexLock{&mu1}) || x;
+MutexLock{&

[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-04 Thread Ziqing Luo via cfe-commits


@@ -2487,15 +2486,15 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
 
   // Clean up constructed object even if there are no attributes to
   // keep the number of objects in limbo as small as possible.
-  if (auto Object = LocksetBuilder.ConstructedObjects.find(
+  if (auto Object = LocksetBuilder.Analyzer->ConstructedObjects.find(
   TD.getBindTemporaryExpr()->getSubExpr());
-  Object != LocksetBuilder.ConstructedObjects.end()) {
+  Object != LocksetBuilder.Analyzer->ConstructedObjects.end()) {
 const auto *DD = TD.getDestructorDecl(AC.getASTContext());
 if (DD->hasAttrs())
   // TODO: the location here isn't quite correct.
   LocksetBuilder.handleCall(nullptr, DD, Object->second,
 
TD.getBindTemporaryExpr()->getEndLoc());
-LocksetBuilder.ConstructedObjects.erase(Object);
+LocksetBuilder.Analyzer->ConstructedObjects.erase(Object);

ziqingluo-90 wrote:

Done

https://github.com/llvm/llvm-project/pull/74020
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-07 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

Plan to merge this PR tomorrow.

https://github.com/llvm/llvm-project/pull/74020
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-12-08 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/74020
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Add a subgroup `-Wunsafe-buffer-usage-in-container` (PR #75665)

2023-12-15 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 created 
https://github.com/llvm/llvm-project/pull/75665

Add a sub diagnostic group under `-Wunsafe-buffer-usage` controlled by 
`-Wunsafe-buffer-usage-in-container`.  The subgroup will include warnings on 
misuses of `std::span`, `std::vector`, and `std::array`.

>From 9501ffef8db9bf52879a815ad99033f17635796b Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 15 Dec 2023 14:04:57 -0800
Subject: [PATCH] [-Wunsafe-buffer-usage] Add a subgroup
 `-Wunsafe-buffer-usage-in-container`

Add a sub diagnostic group under `-Wunsafe-buffer-usage` controlled by
`-Wunsafe-buffer-usage-in-container`.  The subgroup will include
warnings on misuses of `std::span`, `std::vector`, and `std::array`.
---
 clang/include/clang/Basic/DiagnosticGroups.td | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 80b5680b94f6ca..7cf347e92d9972 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1488,4 +1488,5 @@ def DXILValidation : DiagGroup<"dxil-validation">;
 def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
 
 // Warnings and fixes to support the "safe buffers" programming model.
-def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage">;
+def UnsafeBufferUsageInContainer : 
DiagGroup<"unsafe-buffer-usage-in-container">;
+def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", 
[UnsafeBufferUsageInContainer]>;

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


[clang] Warning for unsafe invocation of span::data (PR #75650)

2023-12-15 Thread Ziqing Luo via cfe-commits


@@ -721,6 +721,33 @@ class UnsafeBufferUsageAttrGadget : public WarningGadget {
   DeclUseList getClaimedVarUseSites() const override { return {}; }
 };
 
+// Warning gadget for unsafe invocation of span::data method.
+// Triggers when the pointer returned by the invocation is immediately
+// cast to a larger type.
+
+class DataInvocationGadget : public WarningGadget {
+  constexpr static const char *const OpTag = "data_invocation_expr";

ziqingluo-90 wrote:

a nitpick:  I noticed most tag strings use camel cases.   We better be 
consistent on this?

https://github.com/llvm/llvm-project/pull/75650
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Warning for unsafe invocation of span::data (PR #75650)

2023-12-15 Thread Ziqing Luo via cfe-commits


@@ -2261,6 +2261,21 @@ class UnsafeBufferUsageReporter : public 
UnsafeBufferUsageHandler {
 // note_unsafe_buffer_operation doesn't have this mode yet.
 assert(!IsRelatedToDecl && "Not implemented yet!");
 MsgParam = 3;
+  } else if (const auto *ECE = dyn_cast(Operation)) {
+QualType destType = ECE->getType();
+const uint64_t dSize = 
Ctx.getTypeSize(destType.getTypePtr()->getPointeeType());
+if(const auto *CE =dyn_cast(ECE->getSubExpr())) {
+
+  
if(CE->getRecordDecl()->getQualifiedNameAsString().compare("std::span"))

ziqingluo-90 wrote:

I'm a tiny bit skeptical that if the qualified name is always `std::span`.  
Maybe we can have a test like this:
```
using namespace std;
void f(span buf) {
   BigTy *p = (BigTy *) buf.data();
}
```

https://github.com/llvm/llvm-project/pull/75650
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Add a subgroup `-Wunsafe-buffer-usage-in-container` (PR #75665)

2023-12-18 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/75665
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Thread safety analysis: Fix a bug in handling temporary constructors (PR #74020)

2023-11-30 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 created 
https://github.com/llvm/llvm-project/pull/74020

The commit 54bfd04846156dbd5e0a6b88f539c3d4569a455f introduces general handling 
of constructor and destructor calls.  Consider a pair of TEMPORARY constructor 
and destructor calls of a "SCOPED_CAPABILITY" object.  At the constructor call, 
a resource representing the object itself is added to the state.  But since it 
is a temporary object, the analyzer may not be able to find an AST part to 
identify the resource (As opposed to the normal case of using `VarDecl` as an 
identifier). Later at the destructor call, the resource needs to be removed 
from the state.  Since the resource cannot be identified by anything other than 
itself, the analyzer maintains a map from constructor calls to their created 
resources so that the analyzer could get the proper resource for removal at the 
destructor call via look up the map.  The issue is that the map lives within a 
CFG block.  That means in case the pair of temporary constructor and destructor 
calls are not in one CFG block, the analyzer is not able to remove the proper 
resource at the destructor call.  Although the two calls stay in one CFG block 
usually, they separate in the following example:

```
if (f(MutexLock{lock}) || x) { // <-- the temporary Ctor and Dtor of MutexLock 
are in different CFG blocks

}
```

The fix is simple. It extends the life of the map to that of the entire CFG.

>From ecb13c381d99010dc04a2da4e945dfd1132777fd Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Thu, 30 Nov 2023 14:15:03 -0800
Subject: [PATCH] Thread safety analysis: Fix a bug in handling temporary
 constructors & destructors

The commit 54bfd04846156dbd5e0a6b88f539c3d4569a455f introduces general
handling of constructor and destructor calls.  Consider a pair of
TEMPORARY constructor and destructor calls of a "SCOPED_CAPABILITY"
object.  At the constructor call, a resource representing the object
itself is added to the state.  But since it is a temporary object, the
analyzer may not be able to find an AST part to identify the resource
(As opposed to the normal case of using `VarDecl` as an identifier).
Later at the destructor call, the resource needs to be removed from
the state.  Since the resource cannot be identified by anything other
than itself, the analyzer maintains a map from constructor calls to
their created resources so that the analyzer could get the proper
resource for removal at the destructor call via look up the map.  The
issue is that the map lives within a CFG block.  That means in case
the pair of temporary constructor and destructor calls are not in one
CFG block, the analyzer is not able to remove the proper resource at
the destructor call.  Although the two calls stay in one CFG block
usually, they separate in the following example:

```
if (f(MutexLock{lock}) || x) { // <-- the temporary Ctor and Dtor of MutexLock 
are in different CFG blocks

}
```

The fix is simple. It extends the life of the map to that of the
entire CFG.
---
 clang/lib/Analysis/ThreadSafety.cpp| 18 +-
 .../SemaCXX/warn-thread-safety-analysis.cpp|  9 +
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
b/clang/lib/Analysis/ThreadSafety.cpp
index 7fdf22c2f3919cb..9549d1b398dfa08 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1530,7 +1530,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
 }
 
 namespace {
-
+using ConstructedObjectMapTy = llvm::SmallDenseMap;
 /// We use this class to visit different types of expressions in
 /// CFGBlocks, and build up the lockset.
 /// An expression may cause us to add or remove locks from the lockset, or else
@@ -1544,7 +1544,10 @@ class BuildLockset : public 
ConstStmtVisitor {
   // The fact set for the function on exit.
   const FactSet &FunctionExitFSet;
   /// Maps constructed objects to `this` placeholder prior to initialization.
-  llvm::SmallDenseMap ConstructedObjects;
+  /// The map lives longer than this builder so this is a reference to an
+  /// existing one. The map lives so long as the CFG while this builder is for
+  /// one CFG block.
+  ConstructedObjectMapTy &ConstructedObjects;
   LocalVariableMap::Context LVarCtx;
   unsigned CtxIndex;
 
@@ -1569,9 +1572,11 @@ class BuildLockset : public 
ConstStmtVisitor {
 
 public:
   BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
-   const FactSet &FunctionExitFSet)
+   const FactSet &FunctionExitFSet,
+   ConstructedObjectMapTy &ConstructedObjects)
   : ConstStmtVisitor(), Analyzer(Anlzr), FSet(Info.EntrySet),
-FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
+FunctionExitFSet(FunctionExitFSet),
+ConstructedObjects(ConstructedObjects), LVarCtx(Info.EntryContext),
 CtxIndex(Info.EntryIndex) {}
 
   void VisitUnaryOperator(const UnaryOperator *

[clang] Warning for unsafe invocation of span::data (PR #75650)

2023-12-20 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM! 

https://github.com/llvm/llvm-project/pull/75650
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-05 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 created 
https://github.com/llvm/llvm-project/pull/77148

The PR contains two commits:
1. adding a new waring under a sub-group of `-Wunsafe-buffer-usage`
2. teach the analyzer to be quiet on some benign cases

>From 475d918fb0b5991be7ce559ef6ef7f20c6b231e5 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 5 Jan 2024 13:39:39 -0800
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add a new warning for use of
 two-parameter std::span constructors

Constructing `std::span` objects with the two parameter constructors
could introduce mismatched bounds information, which defeats the
purpose of using `std::span`.  Therefore, we warn every use of such
constructors.

We also plan to incrementally teach `-Wunsafe-buffer-usage` about benign
usages of those constructors.

rdar://115817781
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |   8 +-
 .../Analyses/UnsafeBufferUsageGadgets.def |   8 ++
 .../clang/Basic/DiagnosticSemaKinds.td|   3 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  |  46 +++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  13 +-
 ...ffer-usage-in-container-span-construct.cpp | 118 ++
 6 files changed, 194 insertions(+), 2 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index b28f2c6b99c50e..0d0a2e2b5bc998 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
 
 namespace clang {
@@ -98,9 +99,14 @@ class UnsafeBufferUsageHandler {
 #endif
 
 public:
-  /// Returns a reference to the `Preprocessor`:
+  /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
 
+  /// \return true iff unsafe uses in containers should NOT be reported at 
`Loc`;
+  /// false otherwise.
+  virtual bool
+  ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
+
   virtual std::string
   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
   StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index c9766168836510..07f805ebb11013 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -18,6 +18,12 @@
 #define WARNING_GADGET(name) GADGET(name)
 #endif
 
+/// A `WARNING_GADGET` subset, where the code pattern of each gadget
+/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`).
+#ifndef WARNING_CONTAINER_GADGET
+#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name)
+#endif
+
 /// Safe gadgets correspond to code patterns that aren't unsafe but need to be
 /// properly recognized in order to emit correct warnings and fixes over unsafe
 /// gadgets.
@@ -31,6 +37,7 @@ WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
@@ -43,4 +50,5 @@ FIXABLE_GADGET(PointerInit)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET
+#undef WARNING_CONTAINER_GADGET
 #undef GADGET
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e54f969c19039d..940de9629d7986 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12075,6 +12075,9 @@ def note_unsafe_buffer_variable_fixit_together : Note<
   "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
+def warn_unsafe_buffer_usage_in_container : Warning<
+  "%select{the two-parameter std::span construction is unsafe as it can 
introduce mismatch between buffer size and the bound information}0">,
+  InGroup, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 724c4304a07242..3b6d69cac1afd8 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/l

[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-05 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/77148

>From fb93d83d65ef1c52857b4471ccda2e82447d45dc Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 5 Jan 2024 13:39:39 -0800
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add a new warning for use of
 two-parameter std::span constructors

Constructing `std::span` objects with the two parameter constructors
could introduce mismatched bounds information, which defeats the
purpose of using `std::span`.  Therefore, we warn every use of such
constructors.

We also plan to incrementally teach `-Wunsafe-buffer-usage` about benign
usages of those constructors.

rdar://115817781
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |   8 +-
 .../Analyses/UnsafeBufferUsageGadgets.def |   8 ++
 .../clang/Basic/DiagnosticSemaKinds.td|   3 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  |  46 +++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  15 ++-
 ...ffer-usage-in-container-span-construct.cpp | 118 ++
 ...e-buffer-usage-warning-data-invocation.cpp |   2 +-
 7 files changed, 197 insertions(+), 3 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index b28f2c6b99c50e..aca1ad998822c5 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
 
 namespace clang {
@@ -98,9 +99,14 @@ class UnsafeBufferUsageHandler {
 #endif
 
 public:
-  /// Returns a reference to the `Preprocessor`:
+  /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
 
+  /// \return true iff unsafe uses in containers should NOT be reported at
+  /// `Loc`; false otherwise.
+  virtual bool
+  ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
+
   virtual std::string
   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
   StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index c9766168836510..07f805ebb11013 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -18,6 +18,12 @@
 #define WARNING_GADGET(name) GADGET(name)
 #endif
 
+/// A `WARNING_GADGET` subset, where the code pattern of each gadget
+/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`).
+#ifndef WARNING_CONTAINER_GADGET
+#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name)
+#endif
+
 /// Safe gadgets correspond to code patterns that aren't unsafe but need to be
 /// properly recognized in order to emit correct warnings and fixes over unsafe
 /// gadgets.
@@ -31,6 +37,7 @@ WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
@@ -43,4 +50,5 @@ FIXABLE_GADGET(PointerInit)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET
+#undef WARNING_CONTAINER_GADGET
 #undef GADGET
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e54f969c19039d..940de9629d7986 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12075,6 +12075,9 @@ def note_unsafe_buffer_variable_fixit_together : Note<
   "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
+def warn_unsafe_buffer_usage_in_container : Warning<
+  "%select{the two-parameter std::span construction is unsafe as it can 
introduce mismatch between buffer size and the bound information}0">,
+  InGroup, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 724c4304a07242..3b6d69cac1afd8 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -232,6 +232,11 @@ AST_MATCHER_P(Stmt, notInSafeBufferOptOut,

[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-05 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/77148

>From 4714b0c03897ab61322e3a16288994ccb01dbfac Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 5 Jan 2024 13:39:39 -0800
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add a new warning for use of
 two-parameter std::span constructors

Constructing `std::span` objects with the two parameter constructors
could introduce mismatched bounds information, which defeats the
purpose of using `std::span`.  Therefore, we warn every use of such
constructors.

We also plan to incrementally teach `-Wunsafe-buffer-usage` about benign
usages of those constructors.

rdar://115817781
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |   8 +-
 .../Analyses/UnsafeBufferUsageGadgets.def |   8 ++
 .../clang/Basic/DiagnosticSemaKinds.td|   3 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  |  46 +++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  16 ++-
 ...ffer-usage-in-container-span-construct.cpp | 118 ++
 ...e-buffer-usage-warning-data-invocation.cpp |   2 +-
 7 files changed, 197 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index b28f2c6b99c50e..aca1ad998822c5 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
 
 namespace clang {
@@ -98,9 +99,14 @@ class UnsafeBufferUsageHandler {
 #endif
 
 public:
-  /// Returns a reference to the `Preprocessor`:
+  /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
 
+  /// \return true iff unsafe uses in containers should NOT be reported at
+  /// `Loc`; false otherwise.
+  virtual bool
+  ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
+
   virtual std::string
   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
   StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index c9766168836510..07f805ebb11013 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -18,6 +18,12 @@
 #define WARNING_GADGET(name) GADGET(name)
 #endif
 
+/// A `WARNING_GADGET` subset, where the code pattern of each gadget
+/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`).
+#ifndef WARNING_CONTAINER_GADGET
+#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name)
+#endif
+
 /// Safe gadgets correspond to code patterns that aren't unsafe but need to be
 /// properly recognized in order to emit correct warnings and fixes over unsafe
 /// gadgets.
@@ -31,6 +37,7 @@ WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
@@ -43,4 +50,5 @@ FIXABLE_GADGET(PointerInit)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET
+#undef WARNING_CONTAINER_GADGET
 #undef GADGET
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e54f969c19039d..940de9629d7986 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12075,6 +12075,9 @@ def note_unsafe_buffer_variable_fixit_together : Note<
   "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
+def warn_unsafe_buffer_usage_in_container : Warning<
+  "%select{the two-parameter std::span construction is unsafe as it can 
introduce mismatch between buffer size and the bound information}0">,
+  InGroup, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 724c4304a07242..3b6d69cac1afd8 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -232,6 +232,11 @@ AST_MATCHER_P(Stmt, notInSafeBufferOptOut,

[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-08 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/77148

>From 6ba957670ca593094b4545c35801585da2ee02a8 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 5 Jan 2024 13:39:39 -0800
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add a new warning for use of
 two-parameter std::span constructors

Constructing `std::span` objects with the two parameter constructors
could introduce mismatched bounds information, which defeats the
purpose of using `std::span`.  Therefore, we warn every use of such
constructors.

We also plan to incrementally teach `-Wunsafe-buffer-usage` about benign
usages of those constructors.

rdar://115817781
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |   8 +-
 .../Analyses/UnsafeBufferUsageGadgets.def |   8 ++
 .../clang/Basic/DiagnosticSemaKinds.td|   3 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  |  46 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  16 ++-
 ...ffer-usage-in-container-span-construct.cpp | 136 ++
 ...e-buffer-usage-warning-data-invocation.cpp |   2 +-
 7 files changed, 215 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index b28f2c6b99c50e..aca1ad998822c5 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
 
 namespace clang {
@@ -98,9 +99,14 @@ class UnsafeBufferUsageHandler {
 #endif
 
 public:
-  /// Returns a reference to the `Preprocessor`:
+  /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
 
+  /// \return true iff unsafe uses in containers should NOT be reported at
+  /// `Loc`; false otherwise.
+  virtual bool
+  ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
+
   virtual std::string
   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
   StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index c9766168836510..07f805ebb11013 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -18,6 +18,12 @@
 #define WARNING_GADGET(name) GADGET(name)
 #endif
 
+/// A `WARNING_GADGET` subset, where the code pattern of each gadget
+/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`).
+#ifndef WARNING_CONTAINER_GADGET
+#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name)
+#endif
+
 /// Safe gadgets correspond to code patterns that aren't unsafe but need to be
 /// properly recognized in order to emit correct warnings and fixes over unsafe
 /// gadgets.
@@ -31,6 +37,7 @@ WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
@@ -43,4 +50,5 @@ FIXABLE_GADGET(PointerInit)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET
+#undef WARNING_CONTAINER_GADGET
 #undef GADGET
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e54f969c19039d..940de9629d7986 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12075,6 +12075,9 @@ def note_unsafe_buffer_variable_fixit_together : Note<
   "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
+def warn_unsafe_buffer_usage_in_container : Warning<
+  "%select{the two-parameter std::span construction is unsafe as it can 
introduce mismatch between buffer size and the bound information}0">,
+  InGroup, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 724c4304a07242..3b6d69cac1afd8 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -232,6 +232,11 @@ AST_MATCHER_P(Stmt, notInSafeBufferOptOut, 

[clang] b17baa1 - [ASTMatchers] Adding a new matcher for callee declarations of Obj-C

2022-07-21 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2022-07-21T13:35:31-07:00
New Revision: b17baa1db613a2ce777aa122feb87488750a64d0

URL: 
https://github.com/llvm/llvm-project/commit/b17baa1db613a2ce777aa122feb87488750a64d0
DIFF: 
https://github.com/llvm/llvm-project/commit/b17baa1db613a2ce777aa122feb87488750a64d0.diff

LOG: [ASTMatchers] Adding a new matcher for callee declarations of Obj-C
message expressions

For an Obj-C message expression `[o m]`, the adding matcher will match
the declaration of the method `m`.  This commit overloads the existing
`callee` ASTMatcher, which originally was only for C/C++ nodes but
also applies to Obj-C messages now.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D129398

Added: 


Modified: 
clang/docs/LibASTMatchersReference.html
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp

Removed: 




diff  --git a/clang/docs/LibASTMatchersReference.html 
b/clang/docs/LibASTMatchersReference.html
index 03ca48cc1a9b3..4ac9ab0090cd4 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -7255,14 +7255,24 @@ AST Traversal Matchers
 
 
 
-MatcherCallExpr>calleeMatcherDecl> 
InnerMatcher
-Matches if the call 
expression's callee's declaration matches the
-given matcher.
+MatcherCallExpr>calleeMatcherDecl> 
InnerMatcher
+Matches 1) if the call 
expression's callee's declaration matches the
+given matcher; or 2) if the Obj-C message expression's callee's method
+declaration matches the given matcher.
 
 Example matches y.x() (matcher = callExpr(callee(
cxxMethodDecl(hasName("x")
   class Y { public: void x(); };
   void z() { Y y; y.x(); }
+
+Example 2. Matches [I foo] with
+objcMessageExpr(callee(objcMethodDecl(hasName("foo"
+
+  @interface I: NSObject
+  +(void)foo;
+  @end
+  ...
+  [I foo]
 
 
 
@@ -8814,6 +8824,27 @@ AST Traversal Matchers
 
 
 
+MatcherObjCMessageExpr>calleeMatcherDecl> 
InnerMatcher
+Matches 1) if the call 
expression's callee's declaration matches the
+given matcher; or 2) if the Obj-C message expression's callee's method
+declaration matches the given matcher.
+
+Example matches y.x() (matcher = callExpr(callee(
+   cxxMethodDecl(hasName("x")
+  class Y { public: void x(); };
+  void z() { Y y; y.x(); }
+
+Example 2. Matches [I foo] with
+objcMessageExpr(callee(objcMethodDecl(hasName("foo"
+
+  @interface I: NSObject
+  +(void)foo;
+  @end
+  ...
+  [I foo]
+
+
+
 MatcherObjCMessageExpr>hasAnyArgumentMatcherExpr> 
InnerMatcher
 Matches any argument 
of a call expression or a constructor call
 expression, or an ObjC-message-send expression.

diff  --git a/clang/include/clang/ASTMatchers/ASTMatchers.h 
b/clang/include/clang/ASTMatchers/ASTMatchers.h
index ae5502d7af71b..9f4d807c232dc 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,8 +3838,9 @@ AST_MATCHER_P(CallExpr, callee, internal::Matcher,
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
-/// Matches if the call expression's callee's declaration matches the
-/// given matcher.
+/// Matches 1) if the call expression's callee's declaration matches the
+/// given matcher; or 2) if the Obj-C message expression's callee's method
+/// declaration matches the given matcher.
 ///
 /// Example matches y.x() (matcher = callExpr(callee(
 ///cxxMethodDecl(hasName("x")
@@ -3847,9 +3848,31 @@ AST_MATCHER_P(CallExpr, callee, internal::Matcher,
 ///   class Y { public: void x(); };
 ///   void z() { Y y; y.x(); }
 /// \endcode
-AST_MATCHER_P_OVERLOAD(CallExpr, callee, internal::Matcher, InnerMatcher,
-   1) {
-  return callExpr(hasDeclaration(InnerMatcher)).matches(Node, Finder, Builder);
+///
+/// Example 2. Matches [I foo] with
+/// objcMessageExpr(callee(objcMethodDecl(hasName("foo"
+///
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
+callee, AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCMessageExpr, CallExpr),
+internal::Matcher, InnerMatcher, 1) {
+  if (const auto *CallNode = dyn_cast(&Node))
+return callExpr(hasDeclaration(InnerMatcher))
+

[clang] [-Wunsafe-buffer-usage] Fix debug notes for unclaimed DREs (PR #80787)

2024-02-06 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM!  
I think I did something similar locally when I collected data of unclaimed DREs 
on those big adopter projects.   So I guess the data that we used to find major 
missing patterns for fix-its is still valid.

https://github.com/llvm/llvm-project/pull/80787
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-07 Thread Ziqing Luo via cfe-commits


@@ -2495,10 +2471,100 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
 }
 
+static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
+ UnsafeBufferUsageHandler &Handler) {
+  FixItList FixIts{};
+
+  if (auto CAT = dyn_cast(D->getType())) {
+const QualType &ArrayEltT = CAT->getElementType();
+assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
+// FIXME: support multi-dimensional arrays
+if (isa(ArrayEltT))

ziqingluo-90 wrote:

is it possible that the element type is an array type but not a constant array 
type?

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-07 Thread Ziqing Luo via cfe-commits


@@ -2495,10 +2471,100 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
 }
 
+static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
+ UnsafeBufferUsageHandler &Handler) {
+  FixItList FixIts{};
+
+  if (auto CAT = dyn_cast(D->getType())) {
+const QualType &ArrayEltT = CAT->getElementType();
+assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
+// FIXME: support multi-dimensional arrays
+if (isa(ArrayEltT))
+  return {};
+
+const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D);
+
+// Get the spelling of the element type as written in the source file
+// (including macros, etc.).
+auto MaybeElemTypeTxt =
+getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(),
+ Ctx.getLangOpts());
+if (!MaybeElemTypeTxt)
+  return {};
+const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
+
+// Find the '[' token.
+std::optional NextTok = Lexer::findNextToken(
+IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts());
+while (NextTok && !NextTok->is(tok::l_square))
+  NextTok = Lexer::findNextToken(NextTok->getLocation(),
+ Ctx.getSourceManager(), 
Ctx.getLangOpts());
+if (!NextTok)
+  return {};
+const SourceLocation LSqBracketLoc = NextTok->getLocation();
+
+// Get the spelling of the array size as written in the source file
+// (including macros, etc.).
+auto MaybeArraySizeTxt = getRangeText(
+{LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
+Ctx.getSourceManager(), Ctx.getLangOpts());
+if (!MaybeArraySizeTxt)
+  return {};
+const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
+if (ArraySizeTxt.empty()) {
+  // FIXME: Support array size getting determined from the initializer.

ziqingluo-90 wrote:

In this case, maybe we can get the concrete size value from the array type 
directly since there is no spelling text for us to preserve?

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-07 Thread Ziqing Luo via cfe-commits


@@ -2495,10 +2471,100 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
 }
 
+static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
+ UnsafeBufferUsageHandler &Handler) {
+  FixItList FixIts{};
+
+  if (auto CAT = dyn_cast(D->getType())) {
+const QualType &ArrayEltT = CAT->getElementType();
+assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
+// FIXME: support multi-dimensional arrays
+if (isa(ArrayEltT))
+  return {};
+
+const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D);
+
+// Get the spelling of the element type as written in the source file
+// (including macros, etc.).
+auto MaybeElemTypeTxt =
+getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(),
+ Ctx.getLangOpts());
+if (!MaybeElemTypeTxt)
+  return {};
+const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
+
+// Find the '[' token.
+std::optional NextTok = Lexer::findNextToken(
+IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts());
+while (NextTok && !NextTok->is(tok::l_square))
+  NextTok = Lexer::findNextToken(NextTok->getLocation(),
+ Ctx.getSourceManager(), 
Ctx.getLangOpts());
+if (!NextTok)
+  return {};
+const SourceLocation LSqBracketLoc = NextTok->getLocation();
+
+// Get the spelling of the array size as written in the source file
+// (including macros, etc.).
+auto MaybeArraySizeTxt = getRangeText(
+{LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
+Ctx.getSourceManager(), Ctx.getLangOpts());
+if (!MaybeArraySizeTxt)
+  return {};
+const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
+if (ArraySizeTxt.empty()) {
+  // FIXME: Support array size getting determined from the initializer.
+  // Examples:
+  //int arr1[] = {0, 1, 2};
+  //int arr2{3, 4, 5};
+  // We might be able to preserve the non-specified size with `auto` and
+  // `std::to_array`:
+  //auto arr1 = std::to_array({0, 1, 2});
+  return {};
+}
+
+std::optional IdentText =
+getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
+
+if (!IdentText) {
+  DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
+  return {};
+}
+
+SmallString<32> Replacement;
+raw_svector_ostream OS(Replacement);
+OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
+   << IdentText->str();
+
+FixIts.push_back(FixItHint::CreateReplacement(
+SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));

ziqingluo-90 wrote:

I'm wondering if it is possible to declare a constant array in a way that the 
type specifier is completely at the left-hand side of the identifier?   Like 
the form `T a;` while `T` represents a constant array type.   

How about this?  
```
int a[10];
decltype(a) b;
```

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-07 Thread Ziqing Luo via cfe-commits


@@ -2495,10 +2471,100 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
 }
 
+static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
+ UnsafeBufferUsageHandler &Handler) {
+  FixItList FixIts{};
+
+  if (auto CAT = dyn_cast(D->getType())) {

ziqingluo-90 wrote:

nitpick:  can we replace this check with an assertion?   Because it is already 
checked at the caller:
```
 case FixitStrategy::Kind::Array: {
if (VD->isLocalVarDecl() && isa(VD->getType()))
  return fixVariableWithArray(VD, Tracker, Ctx, Handler);
```

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-07 Thread Ziqing Luo via cfe-commits


@@ -2495,10 +2471,100 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
 }
 
+static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
+ UnsafeBufferUsageHandler &Handler) {
+  FixItList FixIts{};
+
+  if (auto CAT = dyn_cast(D->getType())) {
+const QualType &ArrayEltT = CAT->getElementType();
+assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
+// FIXME: support multi-dimensional arrays
+if (isa(ArrayEltT))
+  return {};
+
+const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D);
+
+// Get the spelling of the element type as written in the source file
+// (including macros, etc.).
+auto MaybeElemTypeTxt =
+getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(),
+ Ctx.getLangOpts());
+if (!MaybeElemTypeTxt)
+  return {};
+const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
+
+// Find the '[' token.
+std::optional NextTok = Lexer::findNextToken(
+IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts());
+while (NextTok && !NextTok->is(tok::l_square))
+  NextTok = Lexer::findNextToken(NextTok->getLocation(),
+ Ctx.getSourceManager(), 
Ctx.getLangOpts());
+if (!NextTok)
+  return {};
+const SourceLocation LSqBracketLoc = NextTok->getLocation();
+
+// Get the spelling of the array size as written in the source file
+// (including macros, etc.).
+auto MaybeArraySizeTxt = getRangeText(
+{LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
+Ctx.getSourceManager(), Ctx.getLangOpts());
+if (!MaybeArraySizeTxt)
+  return {};
+const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
+if (ArraySizeTxt.empty()) {
+  // FIXME: Support array size getting determined from the initializer.
+  // Examples:
+  //int arr1[] = {0, 1, 2};
+  //int arr2{3, 4, 5};
+  // We might be able to preserve the non-specified size with `auto` and
+  // `std::to_array`:
+  //auto arr1 = std::to_array({0, 1, 2});
+  return {};
+}
+
+std::optional IdentText =
+getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
+
+if (!IdentText) {
+  DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
+  return {};
+}
+
+SmallString<32> Replacement;
+raw_svector_ostream OS(Replacement);
+OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
+   << IdentText->str();
+
+FixIts.push_back(FixItHint::CreateReplacement(
+SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));

ziqingluo-90 wrote:

and this maybe?
```
typedef int ArrayT[10];
ArrayT a;
```

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-07 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

I left all my comments here.  I didn't think quite through for some of them so 
feel free to ignore if they don't make sense to you.  

Other parts LGTM!

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Fixits for array decayed to pointer (PR #80347)

2024-02-13 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM!
(We probably do not need to use `CHECK-DAG` at most places in our tests since 
we have fixed the non-deterministic issue, but it's irrelevant to this PR.)

https://github.com/llvm/llvm-project/pull/80347
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage][NFC] clang-format UnsafeBufferUsage.cpp (PR #82027)

2024-02-27 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM!  Thanks for re-formatting the code.

https://github.com/llvm/llvm-project/pull/82027
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-02 Thread Ziqing Luo via cfe-commits


@@ -0,0 +1,164 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \
+// RUN:-fsafe-buffer-usage-suggestions \
+// RUN:-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void simple(unsigned idx) {
+  int buffer[10];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::array 
buffer"
+  buffer[idx] = 0;
+}
+
+void whitespace_in_declaration(unsigned idx) {
+  int  buffer_w   [   10 ];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:35}:"std::array 
buffer_w"
+  buffer_w[idx] = 0;
+}
+
+void comments_in_declaration(unsigned idx) {
+  int   /* [A] */   buffer_w  /* [B] */ [  /* [C] */ 10 /* [D] */  ] ;
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:69}:"std::array buffer_w"
+  buffer_w[idx] = 0;
+}
+
+void initializer(unsigned idx) {
+  int buffer[3] = {0};
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::array 
buffer"
+
+  int buffer2[3] = {0, 1, 2};
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::array 
buffer2"
+
+  buffer[idx] = 0;
+  buffer2[idx] = 0;
+}
+
+void auto_size(unsigned idx) {
+  int buffer[] = {0, 1, 2};
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+// FIXME: implement support
+
+  buffer[idx] = 0;
+}
+
+void universal_initialization(unsigned idx) {
+  int buffer[] {0, 1, 2};
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+// FIXME: implement support
+
+  buffer[idx] = 0;
+}
+
+void multi_decl1(unsigned idx) {
+  int a, buffer[10];
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+// FIXME: implement support
+
+  buffer[idx] = 0;
+}
+
+void multi_decl2(unsigned idx) {
+  int buffer[10], b;
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+// FIXME: implement support
+
+  buffer[idx] = 0;
+}
+
+void local_array_ptr_to_const(unsigned idx, const int*& a) {
+  const int * buffer[10] = {a};
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:25}:"std::array buffer"
+  a = buffer[idx];
+}
+
+void local_array_const_ptr(unsigned idx, int*& a) {
+  int * const buffer[10] = {a};
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:25}:"std::array buffer"
+
+  a = buffer[idx];
+}
+
+void local_array_const_ptr_via_typedef(unsigned idx, int*& a) {
+  typedef int * const my_const_ptr;
+  my_const_ptr buffer[10] = {a};
+// CHECK: 
fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:26}:"std::array 
buffer"
+
+  a = buffer[idx];
+}
+
+void local_array_const_ptr_to_const(unsigned idx, const int*& a) {
+  const int * const buffer[10] = {a};
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:31}:"std::array buffer"
+
+  a = buffer[idx];
+
+}
+
+template
+void unsupported_local_array_in_template(unsigned idx) {
+  T buffer[10];
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:.*-[[@LINE-1]]:.*}
+  buffer[idx] = 0;
+}
+// Instantiate the template function to force its analysis.
+template void unsupported_local_array_in_template(unsigned);
+
+typedef unsigned int my_uint;
+void typedef_as_elem_type(unsigned idx) {
+  my_uint buffer[10];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:21}:"std::array buffer"
+  buffer[idx] = 0;
+}
+
+void macro_as_elem_type(unsigned idx) {
+#define MY_INT int
+  MY_INT buffer[10];
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:.*-[[@LINE-1]]:.*}
+// FIXME: implement support
+
+  buffer[idx] = 0;
+#undef MY_INT
+}
+
+void macro_as_identifier(unsigned idx) {
+#define MY_BUFFER buffer
+  int MY_BUFFER[10];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:20}:"std::array 
MY_BUFFER"
+  MY_BUFFER[idx] = 0;
+#undef MY_BUFFER
+}
+
+void macro_as_size(unsigned idx) {
+#define MY_TEN 10
+  int buffer[MY_TEN];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:21}:"std::array buffer"
+  buffer[idx] = 0;
+#undef MY_TEN
+}
+
+void constant_as_size(unsigned idx) {
+  const unsigned my_const = 10;
+  int buffer[my_const];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:23}:"std::array buffer"
+  buffer[idx] = 0;
+}
+
+void subscript_negative() {
+  int buffer[10];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::array 
buffer"
+
+  // For constant-size arrays any negative index will lead to buffer underflow.
+  // std::array::operator[] has unsigned parameter so the value will be casted 
to unsigned.
+  // This will very likely be buffer overflow but hardened std::array catch 
these at runtime.
+  buffer[-5] = 0;
+}
+
+void subscript_signed(int signed_idx) {
+  int buffer[10];
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::array 
buffer"
+
+  // For constant-size arrays any negative index will lead to buffer underflow.
+  // std::array::operator[] has unsigned parameter so the value will be casted 
to unsigned.
+  // This will very likely be buffer overflow but hardened std::array catches 
these at runtime.
+  buffer[signed_idx] = 0;
+}

ziqingluo-90 wrote:

maybe we can have a test showing that multi-dimentional arrays will not be 
fixed?

https://github.com/llvm/llvm-project/pull/80084
__

[clang] [-Wunsafe-buffer-usage] Introduce std::array fixits (PR #80084)

2024-02-02 Thread Ziqing Luo via cfe-commits


@@ -2495,10 +2470,97 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
   return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
 }
 
+static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
+ UnsafeBufferUsageHandler &Handler) {
+  FixItList FixIts{};
+
+  if (auto CAT = dyn_cast(D->getType())) {
+const QualType &ArrayEltT = CAT->getElementType();
+assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
+
+const SourceLocation IdentifierLoc = getVarDeclIdentifierLoc(D);
+
+// Get the spelling of the element type as written in the source file
+// (including macros, etc.).
+auto MaybeElemTypeTxt =
+getRangeText({D->getBeginLoc(), IdentifierLoc}, Ctx.getSourceManager(),
+ Ctx.getLangOpts());
+if (!MaybeElemTypeTxt)
+  return {};
+const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
+
+// Find the '[' token.
+std::optional NextTok = Lexer::findNextToken(
+IdentifierLoc, Ctx.getSourceManager(), Ctx.getLangOpts());
+while (NextTok && !NextTok->is(tok::l_square))
+  NextTok = Lexer::findNextToken(NextTok->getLocation(),
+ Ctx.getSourceManager(), 
Ctx.getLangOpts());
+if (!NextTok)
+  return {};
+const SourceLocation LSqBracketLoc = NextTok->getLocation();
+
+// Get the spelling of the array size as written in the source file
+// (including macros, etc.).

ziqingluo-90 wrote:

I wish we could have a more elegant way to get the spelling of the array size.  
I feel like `TypeLoc` is suppose to offer it but it does not behave as I expect 
in this case.  (`TypeLoc` also disappointed me in dealing with cv-qualifiers. 
`¯\_(ツ)_/¯`) 

https://github.com/llvm/llvm-project/pull/80084
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-23 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/77148

>From 1b169f49a129a5a69a82386b486c8a46ecfc815a Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 5 Jan 2024 13:39:39 -0800
Subject: [PATCH 1/3] [-Wunsafe-buffer-usage] Add a new warning for use of
 two-parameter std::span constructors

Constructing `std::span` objects with the two parameter constructors
could introduce mismatched bounds information, which defeats the
purpose of using `std::span`.  Therefore, we warn every use of such
constructors.

We also plan to incrementally teach `-Wunsafe-buffer-usage` about benign
usages of those constructors.

rdar://115817781
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |   8 +-
 .../Analyses/UnsafeBufferUsageGadgets.def |   8 ++
 .../clang/Basic/DiagnosticSemaKinds.td|   3 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  |  46 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  15 +-
 ...ffer-usage-in-container-span-construct.cpp | 136 ++
 ...e-buffer-usage-warning-data-invocation.cpp |   2 +-
 7 files changed, 214 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index b28f2c6b99c50e..aca1ad998822c5 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
 
 namespace clang {
@@ -98,9 +99,14 @@ class UnsafeBufferUsageHandler {
 #endif
 
 public:
-  /// Returns a reference to the `Preprocessor`:
+  /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
 
+  /// \return true iff unsafe uses in containers should NOT be reported at
+  /// `Loc`; false otherwise.
+  virtual bool
+  ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
+
   virtual std::string
   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
   StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index c9766168836510..07f805ebb11013 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -18,6 +18,12 @@
 #define WARNING_GADGET(name) GADGET(name)
 #endif
 
+/// A `WARNING_GADGET` subset, where the code pattern of each gadget
+/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`).
+#ifndef WARNING_CONTAINER_GADGET
+#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name)
+#endif
+
 /// Safe gadgets correspond to code patterns that aren't unsafe but need to be
 /// properly recognized in order to emit correct warnings and fixes over unsafe
 /// gadgets.
@@ -31,6 +37,7 @@ WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
@@ -43,4 +50,5 @@ FIXABLE_GADGET(PointerInit)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET
+#undef WARNING_CONTAINER_GADGET
 #undef GADGET
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e54f969c19039d..438c2fcdb1c77b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12075,6 +12075,9 @@ def note_unsafe_buffer_variable_fixit_together : Note<
   "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
+def warn_unsafe_buffer_usage_in_container : Warning<
+  "the two-parameter std::span construction is unsafe as it can introduce 
mismatch between buffer size and the bound information">,
+  InGroup, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 724c4304a07242..3b6d69cac1afd8 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -232,6 +232,11 @@ AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const 
Unsa

[clang] [-Wunsafe-buffer-usage] Fix AST matcher of UUCAddAssignGadget (PR #79392)

2024-01-25 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM,  It's great to catch and fix this bug!

https://github.com/llvm/llvm-project/pull/79392
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wcompletion-handler] Fix a non-termination issue (PR #78380)

2024-01-25 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/78380

>From e7c3a3fc2a4f5d5714044a1c407bfe56f328680d Mon Sep 17 00:00:00 2001
From: Ziqing Luo 
Date: Tue, 16 Jan 2024 17:45:01 -0800
Subject: [PATCH 1/3] [-Wcompletion-handler] Fix a non-termination issue

The Called-Once dataflow analysis could never terminate as a
consequence of non-monotonic update on states.  States of kind Escape
can override states leading to non-monotonic update.

The fix uses finite state sets instead of single states to represent
CFG block outputs, which grows in uni-direction.  To preserve the
behavior of the analyzer, a single state can be extracted from a state
set. The extraction follows the idea that Escape can override error
states within one block but obeys to the partial-order for join.

rdar://119671856
---
 clang/lib/Analysis/CalledOnceCheck.cpp | 120 ++---
 clang/test/SemaObjC/warn-called-once.m |  10 +++
 2 files changed, 120 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp 
b/clang/lib/Analysis/CalledOnceCheck.cpp
index 04c5f6aa9c7450..3fab93b1d09cdf 100644
--- a/clang/lib/Analysis/CalledOnceCheck.cpp
+++ b/clang/lib/Analysis/CalledOnceCheck.cpp
@@ -163,7 +163,7 @@ class ParameterStatus {
 NotVisited = 0x8, /* 1000 */
 // We already reported a violation and stopped tracking calls for this
 // parameter.
-Reported = 0x15, /*  */
+Reported = 0xF, /*  */
 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Reported)
   };
 
@@ -215,6 +215,55 @@ class ParameterStatus {
   const Expr *Call = nullptr;
 };
 
+// Represents a set of `ParameterStatus`s collected in a single CFGBlock.
+class ParamStatusSet {
+private:
+  // The kinds of `States` spans from 0x0 to 0x15, so 16 bits are enough:
+  llvm::BitVector Set{16, false};
+  // Following the exisitng idea: if there are multiple calls of interest in 
one
+  // block, recording one of them is good enough.
+  const Expr *Call = nullptr;
+
+public:
+  ParamStatusSet() {}
+
+  // Add a new `ParameterStatus` to the set.  Returns true iff the adding 
status
+  // was new to the set.
+  bool add(ParameterStatus PS) {
+assert(PS.getKind() != ParameterStatus::Kind::NotVisited &&
+   "the status cannot be NotVisited after visiting a block");
+if (Set.test(PS.getKind()))
+  return false;
+Set.set(PS.getKind());
+if (PS.seenAnyCalls())
+  Call = &PS.getCall();
+return true;
+  }
+
+  // Get one `ParameterStatus` to represent the set of them.  The idea is to
+  // take a join on them but let ESCAPE overrides error statuses.
+  ParameterStatus summaryStatus() {
+unsigned Summary = 0x0;
+
+for (unsigned Idx :
+ llvm::make_range(Set.set_bits_begin(), Set.set_bits_end()))
+  Summary |= Idx;
+assert(Summary == 0x0 || Summary == 0x1 || Summary == 0x3 ||
+   Summary == 0x5 || Summary == 0x7 ||
+   Summary == 0xF && "expecting a defined value");
+
+ParameterStatus::Kind SummaryKind = ParameterStatus::Kind(Summary);
+
+if (SummaryKind > ParameterStatus::Kind::NON_ERROR_STATUS &&
+Set.test(ParameterStatus::Kind::Escaped)) {
+  SummaryKind = ParameterStatus::Kind::Escaped;
+}
+if (ParameterStatus::seenAnyCalls(SummaryKind))
+  return {SummaryKind, Call};
+return {SummaryKind};
+  }
+};
+
 /// State aggregates statuses of all tracked parameters.
 class State {
 public:
@@ -274,6 +323,53 @@ class State {
 
 private:
   ParamSizedVector ParamData;
+
+  friend class CFGBlockOutputState;
+
+  State(ParamSizedVector ParamData) : ParamData(ParamData) {}
+
+  unsigned size() const { return ParamData.size(); }
+};
+
+// A different kind of "state" in addition to `State`.  `CFGBlockOutputState`
+// are created for dealing with the non-termination issue due to `State`s are
+// not being updated monotonically at the output of each CFGBlock.
+// A `CFGBlockOutputState` is in fact a finite set of `State`s that
+// grows monotonically.  One can extract a `State` from a 
`CFGBlockOutputState`.
+// Note that the `State`-extraction  does NOT guarantee monotone but it
+// respects the existing semantics.  Specifically, ESCAPE is "greater than"
+// other error states in a single path but is "less than" them at JOIN.
+//
+// `CFGBlockOutputState` will be used to terminate the fix-point computation.
+class CFGBlockOutputState {
+private:
+  ParamSizedVector StatusSets;
+
+public:
+  CFGBlockOutputState(unsigned Size) : StatusSets{Size} {};
+
+  // Update this `CFGBlockOutputState` with a newly computed `State`.  Return
+  // true iff `CFGBlockOutputState` changed.
+  bool update(const State &NewState) {
+bool Changed = false;
+
+for (unsigned Idx = 0; Idx < NewState.size(); ++Idx) {
+  if (StatusSets[Idx].add(NewState.getStatusFor(Idx))) {
+Changed = true;
+  }
+}
+return Changed;
+  }
+
+  // Return a `State` that represents the `CFGBlockOutputState`.
+  State ge

[clang] [-Wcompletion-handler] Fix a non-termination issue (PR #78380)

2024-01-25 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/78380

>From e7c3a3fc2a4f5d5714044a1c407bfe56f328680d Mon Sep 17 00:00:00 2001
From: Ziqing Luo 
Date: Tue, 16 Jan 2024 17:45:01 -0800
Subject: [PATCH 1/4] [-Wcompletion-handler] Fix a non-termination issue

The Called-Once dataflow analysis could never terminate as a
consequence of non-monotonic update on states.  States of kind Escape
can override states leading to non-monotonic update.

The fix uses finite state sets instead of single states to represent
CFG block outputs, which grows in uni-direction.  To preserve the
behavior of the analyzer, a single state can be extracted from a state
set. The extraction follows the idea that Escape can override error
states within one block but obeys to the partial-order for join.

rdar://119671856
---
 clang/lib/Analysis/CalledOnceCheck.cpp | 120 ++---
 clang/test/SemaObjC/warn-called-once.m |  10 +++
 2 files changed, 120 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp 
b/clang/lib/Analysis/CalledOnceCheck.cpp
index 04c5f6aa9c7450..3fab93b1d09cdf 100644
--- a/clang/lib/Analysis/CalledOnceCheck.cpp
+++ b/clang/lib/Analysis/CalledOnceCheck.cpp
@@ -163,7 +163,7 @@ class ParameterStatus {
 NotVisited = 0x8, /* 1000 */
 // We already reported a violation and stopped tracking calls for this
 // parameter.
-Reported = 0x15, /*  */
+Reported = 0xF, /*  */
 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Reported)
   };
 
@@ -215,6 +215,55 @@ class ParameterStatus {
   const Expr *Call = nullptr;
 };
 
+// Represents a set of `ParameterStatus`s collected in a single CFGBlock.
+class ParamStatusSet {
+private:
+  // The kinds of `States` spans from 0x0 to 0x15, so 16 bits are enough:
+  llvm::BitVector Set{16, false};
+  // Following the exisitng idea: if there are multiple calls of interest in 
one
+  // block, recording one of them is good enough.
+  const Expr *Call = nullptr;
+
+public:
+  ParamStatusSet() {}
+
+  // Add a new `ParameterStatus` to the set.  Returns true iff the adding 
status
+  // was new to the set.
+  bool add(ParameterStatus PS) {
+assert(PS.getKind() != ParameterStatus::Kind::NotVisited &&
+   "the status cannot be NotVisited after visiting a block");
+if (Set.test(PS.getKind()))
+  return false;
+Set.set(PS.getKind());
+if (PS.seenAnyCalls())
+  Call = &PS.getCall();
+return true;
+  }
+
+  // Get one `ParameterStatus` to represent the set of them.  The idea is to
+  // take a join on them but let ESCAPE overrides error statuses.
+  ParameterStatus summaryStatus() {
+unsigned Summary = 0x0;
+
+for (unsigned Idx :
+ llvm::make_range(Set.set_bits_begin(), Set.set_bits_end()))
+  Summary |= Idx;
+assert(Summary == 0x0 || Summary == 0x1 || Summary == 0x3 ||
+   Summary == 0x5 || Summary == 0x7 ||
+   Summary == 0xF && "expecting a defined value");
+
+ParameterStatus::Kind SummaryKind = ParameterStatus::Kind(Summary);
+
+if (SummaryKind > ParameterStatus::Kind::NON_ERROR_STATUS &&
+Set.test(ParameterStatus::Kind::Escaped)) {
+  SummaryKind = ParameterStatus::Kind::Escaped;
+}
+if (ParameterStatus::seenAnyCalls(SummaryKind))
+  return {SummaryKind, Call};
+return {SummaryKind};
+  }
+};
+
 /// State aggregates statuses of all tracked parameters.
 class State {
 public:
@@ -274,6 +323,53 @@ class State {
 
 private:
   ParamSizedVector ParamData;
+
+  friend class CFGBlockOutputState;
+
+  State(ParamSizedVector ParamData) : ParamData(ParamData) {}
+
+  unsigned size() const { return ParamData.size(); }
+};
+
+// A different kind of "state" in addition to `State`.  `CFGBlockOutputState`
+// are created for dealing with the non-termination issue due to `State`s are
+// not being updated monotonically at the output of each CFGBlock.
+// A `CFGBlockOutputState` is in fact a finite set of `State`s that
+// grows monotonically.  One can extract a `State` from a 
`CFGBlockOutputState`.
+// Note that the `State`-extraction  does NOT guarantee monotone but it
+// respects the existing semantics.  Specifically, ESCAPE is "greater than"
+// other error states in a single path but is "less than" them at JOIN.
+//
+// `CFGBlockOutputState` will be used to terminate the fix-point computation.
+class CFGBlockOutputState {
+private:
+  ParamSizedVector StatusSets;
+
+public:
+  CFGBlockOutputState(unsigned Size) : StatusSets{Size} {};
+
+  // Update this `CFGBlockOutputState` with a newly computed `State`.  Return
+  // true iff `CFGBlockOutputState` changed.
+  bool update(const State &NewState) {
+bool Changed = false;
+
+for (unsigned Idx = 0; Idx < NewState.size(); ++Idx) {
+  if (StatusSets[Idx].add(NewState.getStatusFor(Idx))) {
+Changed = true;
+  }
+}
+return Changed;
+  }
+
+  // Return a `State` that represents the `CFGBlockOutputState`.
+  State ge

[clang] [-Wcompletion-handler] Fix a non-termination issue (PR #78380)

2024-01-25 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

@SavchenkoValeriy, sorry for the late response.  
Your suggested fix works for my minimal example as well as my local project 
where this bug was originated.
And I failed to come up with a new counter-example for the fix so I believe it 
will also prevent future non-termination issues. 
I have updated the PR with respect to the simpler fix.

https://github.com/llvm/llvm-project/pull/78380
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wcompletion-handler] Fix a non-termination issue (PR #78380)

2024-01-26 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/78380
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-26 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/77148
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Fix the crash introduced by the unsafe invocation of span::data warning (PR #78815)

2024-01-19 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 edited 
https://github.com/llvm/llvm-project/pull/78815
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Fix the crash introduced by the unsafe invocation of span::data warning (PR #78815)

2024-01-19 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM!   Feel free to ignore my comment if it doesn't make sense to you.

https://github.com/llvm/llvm-project/pull/78815
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Fix the crash introduced by the unsafe invocation of span::data warning (PR #78815)

2024-01-19 Thread Ziqing Luo via cfe-commits


@@ -2263,15 +2263,27 @@ class UnsafeBufferUsageReporter : public 
UnsafeBufferUsageHandler {
 MsgParam = 3;
   } else if (const auto *ECE = dyn_cast(Operation)) {
 QualType destType = ECE->getType();
-const uint64_t dSize =
-Ctx.getTypeSize(destType.getTypePtr()->getPointeeType());
-if (const auto *CE = dyn_cast(ECE->getSubExpr())) {
-  QualType srcType = CE->getType();
-  const uint64_t sSize =
-  Ctx.getTypeSize(srcType.getTypePtr()->getPointeeType());
-  if (sSize >= dSize)
+if (!isa(destType))
+  return;
+
+const Expr *subExpr = ECE->getSubExpr();
+// Check if related to DataInvocation warning gadget.

ziqingluo-90 wrote:

nitpick: not sure if we need this check.  `DataInvocation` warning gadget is 
the only possible explicit-cast kind `Operation` here.This is completely my 
personal taste: this function sort of implies unique correspondence between 
warning gadgets and `Operation` kinds if without the check.

https://github.com/llvm/llvm-project/pull/78815
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Add a new warning for uses of std::span two-parameter constructors (PR #77148)

2024-01-22 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/77148

>From 6ba957670ca593094b4545c35801585da2ee02a8 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Fri, 5 Jan 2024 13:39:39 -0800
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add a new warning for use of
 two-parameter std::span constructors

Constructing `std::span` objects with the two parameter constructors
could introduce mismatched bounds information, which defeats the
purpose of using `std::span`.  Therefore, we warn every use of such
constructors.

We also plan to incrementally teach `-Wunsafe-buffer-usage` about benign
usages of those constructors.

rdar://115817781
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |   8 +-
 .../Analyses/UnsafeBufferUsageGadgets.def |   8 ++
 .../clang/Basic/DiagnosticSemaKinds.td|   3 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  |  46 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  16 ++-
 ...ffer-usage-in-container-span-construct.cpp | 136 ++
 ...e-buffer-usage-warning-data-invocation.cpp |   2 +-
 7 files changed, 215 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index b28f2c6b99c50ec..aca1ad998822c58 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
 
 namespace clang {
@@ -98,9 +99,14 @@ class UnsafeBufferUsageHandler {
 #endif
 
 public:
-  /// Returns a reference to the `Preprocessor`:
+  /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
 
+  /// \return true iff unsafe uses in containers should NOT be reported at
+  /// `Loc`; false otherwise.
+  virtual bool
+  ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
+
   virtual std::string
   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
   StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index c97661688365102..07f805ebb110132 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -18,6 +18,12 @@
 #define WARNING_GADGET(name) GADGET(name)
 #endif
 
+/// A `WARNING_GADGET` subset, where the code pattern of each gadget
+/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`).
+#ifndef WARNING_CONTAINER_GADGET
+#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name)
+#endif
+
 /// Safe gadgets correspond to code patterns that aren't unsafe but need to be
 /// properly recognized in order to emit correct warnings and fixes over unsafe
 /// gadgets.
@@ -31,6 +37,7 @@ WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
@@ -43,4 +50,5 @@ FIXABLE_GADGET(PointerInit)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET
+#undef WARNING_CONTAINER_GADGET
 #undef GADGET
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e54f969c19039dd..940de9629d79862 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12075,6 +12075,9 @@ def note_unsafe_buffer_variable_fixit_together : Note<
   "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
+def warn_unsafe_buffer_usage_in_container : Warning<
+  "%select{the two-parameter std::span construction is unsafe as it can 
introduce mismatch between buffer size and the bound information}0">,
+  InGroup, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 724c4304a072420..3b6d69cac1afd86 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -232,6 +232,11 @@ AST_MATCHER_P(Stmt, notInSafeBuffer

[clang] b2ac5fd - [-Wunsafe-buffer-usage] Add a new `forEachDescendant` matcher that skips callable declarations

2023-01-04 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-01-04T15:51:56-08:00
New Revision: b2ac5fd724c44cf662caed84bd8f84af574b981d

URL: 
https://github.com/llvm/llvm-project/commit/b2ac5fd724c44cf662caed84bd8f84af574b981d
DIFF: 
https://github.com/llvm/llvm-project/commit/b2ac5fd724c44cf662caed84bd8f84af574b981d.diff

LOG: [-Wunsafe-buffer-usage] Add a new `forEachDescendant` matcher that skips 
callable declarations

Note this is a change local to -Wunsafe-buffer-usage checks.

Add a new matcher `forEveryDescendant` that recursively matches
descendants of a `Stmt` but skips nested callable definitions.  This
matcher has same effect as using `forEachDescendant` and skipping
`forCallable` explicitly but does not require the AST construction to be
complete.

Reviewed by: NoQ, xazax.hun

Differential revision: https://reviews.llvm.org/D138329

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 7de2de1c84bc2..6a8e38297ebe2 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -8,12 +8,111 @@
 
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "llvm/ADT/SmallVector.h"
 
 using namespace llvm;
 using namespace clang;
 using namespace ast_matchers;
 
+namespace clang::ast_matchers::internal {
+// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
+// except for those belonging to a 
diff erent callable of "n".
+class MatchDescendantVisitor
+: public RecursiveASTVisitor {
+public:
+  typedef RecursiveASTVisitor VisitorBase;
+
+  // Creates an AST visitor that matches `Matcher` on all
+  // descendants of a given node "n" except for the ones
+  // belonging to a 
diff erent callable of "n".
+  MatchDescendantVisitor(const DynTypedMatcher *Matcher, ASTMatchFinder 
*Finder,
+ BoundNodesTreeBuilder *Builder,
+ ASTMatchFinder::BindKind Bind)
+  : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
+Matches(false) {}
+
+  // Returns true if a match is found in a subtree of `DynNode`, which belongs
+  // to the same callable of `DynNode`.
+  bool findMatch(const DynTypedNode &DynNode) {
+Matches = false;
+if (const Stmt *StmtNode = DynNode.get()) {
+  TraverseStmt(const_cast(StmtNode));
+  *Builder = ResultBindings;
+  return Matches;
+}
+return false;
+  }
+
+  // The following are overriding methods from the base visitor class.
+  // They are public only to allow CRTP to work. They are *not *part
+  // of the public API of this class.
+
+  // For the matchers so far used in safe buffers, we only need to match
+  // `Stmt`s.  To override more as needed.
+
+  bool TraverseDecl(Decl *Node) {
+if (!Node)
+  return true;
+if (!match(*Node))
+  return false;
+// To skip callables:
+if (isa(Node))
+  return true;
+// Traverse descendants
+return VisitorBase::TraverseDecl(Node);
+  }
+
+  bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
+if (!Node)
+  return true;
+if (!match(*Node))
+  return false;
+// To skip callables:
+if (isa(Node))
+  return true;
+return VisitorBase::TraverseStmt(Node);
+  }
+
+  bool shouldVisitTemplateInstantiations() const { return true; }
+  bool shouldVisitImplicitCode() const {
+// TODO: let's ignore implicit code for now
+return false;
+  }
+
+private:
+  // Sets 'Matched' to true if 'Matcher' matches 'Node'
+  //
+  // Returns 'true' if traversal should continue after this function
+  // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
+  template  bool match(const T &Node) {
+BoundNodesTreeBuilder RecursiveBuilder(*Builder);
+
+if (Matcher->matches(DynTypedNode::create(Node), Finder,
+ &RecursiveBuilder)) {
+  ResultBindings.addMatch(RecursiveBuilder);
+  Matches = true;
+  if (Bind != ASTMatchFinder::BK_All)
+return false; // Abort as soon as a match is found.
+}
+return true;
+  }
+
+  const DynTypedMatcher *const Matcher;
+  ASTMatchFinder *const Finder;
+  BoundNodesTreeBuilder *const Builder;
+  BoundNodesTreeBuilder ResultBindings;
+  const ASTMatchFinder::BindKind Bind;
+  bool Matches;
+};
+
+AST_MATCHER_P(Stmt, forEveryDescendant, Matcher, innerMatcher) {
+  MatchDescendantVisitor Visitor(new DynTypedMatcher(innerMatcher), Finder,
+ Builder, ASTMatchFinder::BK_All);
+  return Visitor.findMatch(DynTypedNode::create(Node));
+}
+} // namespace clang::ast_matchers::internal
+
 namespace {
 // Because the analysis revolves around variables and their types, we'll need 
t

[clang] f84f17c - [-Wunsafe-buffer-usage] Add an unsafe gadget for pointer-arithmetic operations

2023-01-04 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-01-04T16:50:21-08:00
New Revision: f84f17c489f7cb84d72e84a6b1b6c54bd8d52717

URL: 
https://github.com/llvm/llvm-project/commit/f84f17c489f7cb84d72e84a6b1b6c54bd8d52717
DIFF: 
https://github.com/llvm/llvm-project/commit/f84f17c489f7cb84d72e84a6b1b6c54bd8d52717.diff

LOG: [-Wunsafe-buffer-usage] Add an unsafe gadget for pointer-arithmetic 
operations

For -Wunsafe-buffer-usage diagnostics, we want to warn about pointer
arithmetics since resulting pointers can be used to access buffers.
Therefore, I add an `UnsafeGadget` representing general pointer
arithmetic operations.

Reviewed by: NoQ
Differential revision: https://reviews.llvm.org/D139233

Added: 


Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git 
a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index fc8800fed3953..f24e925966094 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -28,6 +28,7 @@
 UNSAFE_GADGET(Increment)
 UNSAFE_GADGET(Decrement)
 UNSAFE_GADGET(ArraySubscript)
+UNSAFE_GADGET(PointerArithmetic)
 
 #undef SAFE_GADGET
 #undef UNSAFE_GADGET

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 6a8e38297ebe2..29c8dbb45fe9f 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -304,6 +304,55 @@ class ArraySubscriptGadget : public UnsafeGadget {
 return {};
   }
 };
+
+/// A pointer arithmetic expression of one of the forms:
+///  \code
+///  ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
+///  \endcode
+class PointerArithmeticGadget : public UnsafeGadget {
+  static constexpr const char *const PointerArithmeticTag = "ptrAdd";
+  static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
+  const BinaryOperator *PA; // pointer arithmetic expression
+  const Expr * Ptr; // the pointer expression in `PA`
+
+public:
+PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
+  : UnsafeGadget(Kind::PointerArithmetic),
+PA(Result.Nodes.getNodeAs(PointerArithmeticTag)),
+Ptr(Result.Nodes.getNodeAs(PointerArithmeticPointerTag)) {}
+
+  static bool classof(const Gadget *G) {
+return G->getKind() == Kind::PointerArithmetic;
+  }
+
+  static Matcher matcher() {
+auto HasIntegerType = anyOf(
+  hasType(isInteger()), hasType(enumType()));
+auto PtrAtRight = allOf(hasOperatorName("+"),
+
hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
+hasLHS(HasIntegerType));
+auto PtrAtLeft = allOf(
+   anyOf(hasOperatorName("+"), hasOperatorName("-"),
+ hasOperatorName("+="), hasOperatorName("-=")),
+   hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
+   hasRHS(HasIntegerType));
+
+return stmt(binaryOperator(anyOf(PtrAtLeft, 
PtrAtRight)).bind(PointerArithmeticTag));
+  }
+
+  const Stmt *getBaseStmt() const override { return PA; }
+
+  DeclUseList getClaimedVarUseSites() const override {
+if (const auto *DRE =
+dyn_cast(Ptr->IgnoreParenImpCasts())) {
+  return {DRE};
+}
+
+return {};
+  }
+  // FIXME: pointer adding zero should be fine
+  //FIXME: this gadge will need a fix-it
+};
 } // namespace
 
 namespace {

diff  --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
index b367bd2c781ce..476ec73c4f744 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -169,10 +169,36 @@ template T f(T t, T * pt, T a[N], T 
(&b)[N]) {
   return &t[1]; // expected-warning{{unchecked operation on raw buffer in 
expression}}
 }
 
+// Testing pointer arithmetic for pointer-to-int, qualified multi-level
+// pointer, pointer to a template type, and auto type
+T_ptr_t getPtr();
+
+template
+void testPointerArithmetic(int * p, const int **q, T * x) {
+  int a[10];
+  auto y = &a[0];
+
+  foo(p + 1, 1 + p, p - 1,  // expected-warning3{{unchecked operation on 
raw buffer in expression}}
+  *q + 1, 1 + *q, *q - 1,   // expected-warning3{{unchecked operation on 
raw buffer in expression}}
+  x + 1, 1 + x, x - 1,  // expected-warning3{{unchecked operation on 
raw buffer in expression}}
+  y + 1, 1 + y, y - 1,  // expected-warning3{{unchecked operation on 
raw buffer in expression}}
+  getPtr() + 1, 1 + getPtr(), getPtr() - 1 // expected-warning3{{unchecked 
operation on raw buffer in expression}}
+  );
+
+  p += 1;  p -= 1;  // expected-

[clang] f58b025 - Revert "[-Wunsafe-buffer-usage] Add a new `forEachDescendant` matcher that skips callable declarations"

2023-01-04 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-01-04T17:16:21-08:00
New Revision: f58b025354ee2d3bcd7ab2399a11429ec940c1e0

URL: 
https://github.com/llvm/llvm-project/commit/f58b025354ee2d3bcd7ab2399a11429ec940c1e0
DIFF: 
https://github.com/llvm/llvm-project/commit/f58b025354ee2d3bcd7ab2399a11429ec940c1e0.diff

LOG: Revert "[-Wunsafe-buffer-usage] Add a new `forEachDescendant` matcher that 
skips callable declarations"

This reverts commit b2ac5fd724c44cf662caed84bd8f84af574b981d.

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 29c8dbb45fe9..a699d27c1544 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -8,111 +8,12 @@
 
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/AST/RecursiveASTVisitor.h"
 #include "llvm/ADT/SmallVector.h"
 
 using namespace llvm;
 using namespace clang;
 using namespace ast_matchers;
 
-namespace clang::ast_matchers::internal {
-// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
-// except for those belonging to a 
diff erent callable of "n".
-class MatchDescendantVisitor
-: public RecursiveASTVisitor {
-public:
-  typedef RecursiveASTVisitor VisitorBase;
-
-  // Creates an AST visitor that matches `Matcher` on all
-  // descendants of a given node "n" except for the ones
-  // belonging to a 
diff erent callable of "n".
-  MatchDescendantVisitor(const DynTypedMatcher *Matcher, ASTMatchFinder 
*Finder,
- BoundNodesTreeBuilder *Builder,
- ASTMatchFinder::BindKind Bind)
-  : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
-Matches(false) {}
-
-  // Returns true if a match is found in a subtree of `DynNode`, which belongs
-  // to the same callable of `DynNode`.
-  bool findMatch(const DynTypedNode &DynNode) {
-Matches = false;
-if (const Stmt *StmtNode = DynNode.get()) {
-  TraverseStmt(const_cast(StmtNode));
-  *Builder = ResultBindings;
-  return Matches;
-}
-return false;
-  }
-
-  // The following are overriding methods from the base visitor class.
-  // They are public only to allow CRTP to work. They are *not *part
-  // of the public API of this class.
-
-  // For the matchers so far used in safe buffers, we only need to match
-  // `Stmt`s.  To override more as needed.
-
-  bool TraverseDecl(Decl *Node) {
-if (!Node)
-  return true;
-if (!match(*Node))
-  return false;
-// To skip callables:
-if (isa(Node))
-  return true;
-// Traverse descendants
-return VisitorBase::TraverseDecl(Node);
-  }
-
-  bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
-if (!Node)
-  return true;
-if (!match(*Node))
-  return false;
-// To skip callables:
-if (isa(Node))
-  return true;
-return VisitorBase::TraverseStmt(Node);
-  }
-
-  bool shouldVisitTemplateInstantiations() const { return true; }
-  bool shouldVisitImplicitCode() const {
-// TODO: let's ignore implicit code for now
-return false;
-  }
-
-private:
-  // Sets 'Matched' to true if 'Matcher' matches 'Node'
-  //
-  // Returns 'true' if traversal should continue after this function
-  // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
-  template  bool match(const T &Node) {
-BoundNodesTreeBuilder RecursiveBuilder(*Builder);
-
-if (Matcher->matches(DynTypedNode::create(Node), Finder,
- &RecursiveBuilder)) {
-  ResultBindings.addMatch(RecursiveBuilder);
-  Matches = true;
-  if (Bind != ASTMatchFinder::BK_All)
-return false; // Abort as soon as a match is found.
-}
-return true;
-  }
-
-  const DynTypedMatcher *const Matcher;
-  ASTMatchFinder *const Finder;
-  BoundNodesTreeBuilder *const Builder;
-  BoundNodesTreeBuilder ResultBindings;
-  const ASTMatchFinder::BindKind Bind;
-  bool Matches;
-};
-
-AST_MATCHER_P(Stmt, forEveryDescendant, Matcher, innerMatcher) {
-  MatchDescendantVisitor Visitor(new DynTypedMatcher(innerMatcher), Finder,
- Builder, ASTMatchFinder::BK_All);
-  return Visitor.findMatch(DynTypedNode::create(Node));
-}
-} // namespace clang::ast_matchers::internal
-
 namespace {
 // Because the analysis revolves around variables and their types, we'll need 
to
 // track uses of variables (aka DeclRefExprs).
@@ -497,7 +398,7 @@ static std::pair 
findGadgets(const Decl *D) {
 
   // clang-format off
   M.addMatcher(
-stmt(forEveryDescendant(
+stmt(forEachDescendant(
   stmt(anyOf(
 // Add Gadget::matcher() for every gadget in the registry.
 #define GADGET(x)  

[clang] 33f6161 - [-Wunsafe-buffer-usage] Group parameter fix-its

2023-09-21 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-09-21T12:45:30-07:00
New Revision: 33f6161d9eaaa7c5a01ee8e50dec440d3052d34a

URL: 
https://github.com/llvm/llvm-project/commit/33f6161d9eaaa7c5a01ee8e50dec440d3052d34a
DIFF: 
https://github.com/llvm/llvm-project/commit/33f6161d9eaaa7c5a01ee8e50dec440d3052d34a.diff

LOG: [-Wunsafe-buffer-usage] Group parameter fix-its

For a function `F` whose parameters need to be fixed, we group fix-its
of F's parameters together so that either all of the parameters get
fixed or none of them gets fixed.

Reviewed by: NoQ (Artem Dergachev), t-rasmud (Rashmi Mudduluru), jkorous (Jan 
Korous)

Differential revision: https://reviews.llvm.org/D153059

Added: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-multi-parm-span.cpp

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index c865b2e8bdb3794..e8a93164302a47d 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -31,7 +31,10 @@ class VariableGroupsManager {
   /// together in one step.
   ///
   /// `Var` must be a variable that needs fix (so it must be in a group).
-  virtual VarGrpRef getGroupOfVar(const VarDecl *Var) const =0;
+  /// `HasParm` is an optional argument that will be set to true if the set of
+  /// variables, where `Var` is in, contains parameters.
+  virtual VarGrpRef getGroupOfVar(const VarDecl *Var,
+  bool *HasParm = nullptr) const =0;
 };
 
 /// The interface that lets the caller handle unsafe buffer usage analysis
@@ -62,11 +65,14 @@ class UnsafeBufferUsageHandler {
  bool IsRelatedToDecl) = 0;
 
   /// Invoked when a fix is suggested against a variable. This function groups
-  /// all variables that must be fixed together (i.e their types must be 
changed to the
-  /// same target type to prevent type mismatches) into a single fixit.
+  /// all variables that must be fixed together (i.e their types must be 
changed
+  /// to the same target type to prevent type mismatches) into a single fixit.
+  ///
+  /// `D` is the declaration of the callable under analysis that owns 
`Variable`
+  /// and all of its group mates.
   virtual void handleUnsafeVariableGroup(const VarDecl *Variable,
  const VariableGroupsManager 
&VarGrpMgr,
- FixItList &&Fixes) = 0;
+ FixItList &&Fixes, const Decl *D) = 0;
 
 #ifndef NDEBUG
 public:

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0ac4df8edb242f6..9613cca63193ffd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11916,6 +11916,9 @@ def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
 def note_unsafe_buffer_variable_fixit_group : Note<
   "change type of %0 to '%select{std::span|std::array|std::span::iterator}1' 
to preserve bounds information%select{|, and change %2 to 
'%select{std::span|std::array|std::span::iterator}1' to propagate bounds 
information between them}3">;
+def note_unsafe_buffer_variable_fixit_together : Note<
+  "change type of %0 to '%select{std::span|std::array|std::span::iterator}1' 
to preserve bounds information"
+  "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">;
 def note_safe_buffer_usage_suggestions_disabled : Note<
   "pass -fsafe-buffer-usage-suggestions to receive code hardening 
suggestions">;
 #ifndef NDEBUG

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index df89ab436380857..348585ef16e967b 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1434,6 +1434,22 @@ static bool hasUnsupportedSpecifiers(const VarDecl *VD,
  AttrRangeOverlapping;
 }
 
+// Returns the `SourceRange` of `D`.  The reason why this function exists is
+// that `D->getSourceRange()` may return a range where the end location is the
+// starting location of the last token.  The end location of the source range
+// returned by this function is the last location of the last token.
+static SourceRange getSourceRangeToTokenEnd(const Decl *D,
+const SourceManager &SM,
+LangOptions Lang

[clang] 700baeb - [-Wunsafe-buffer-usage] Use `Strategy` to determine whether to fix a parameter

2023-09-21 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-09-21T15:06:22-07:00
New Revision: 700baeb765cfe8628eb68bc24319b3db0209dd84

URL: 
https://github.com/llvm/llvm-project/commit/700baeb765cfe8628eb68bc24319b3db0209dd84
DIFF: 
https://github.com/llvm/llvm-project/commit/700baeb765cfe8628eb68bc24319b3db0209dd84.diff

LOG: [-Wunsafe-buffer-usage] Use `Strategy` to determine whether to fix a 
parameter

- 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

Reviewed by: t-rasmud (Rashmi Mudduluru), NoQ (Artem Dergachev)

Differential revision: https://reviews.llvm.org/D157441

Added: 


Modified: 
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-fixits-test.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-ptr-init-fixits.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-ptr-init.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc-fixits.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index e8a93164302a47d..8a2d56668e32f92 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -35,6 +35,10 @@ class VariableGroupsManager {
   /// variables, where `Var` is in, contains parameters.
   virtual VarGrpRef getGroupOfVar(const VarDecl *Var,
   bool *HasParm = nullptr) const =0;
+
+  /// 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 =0;
 };
 
 /// The interface that lets the caller handle unsafe buffer usage analysis

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 348585ef16e967b..a4a200469f70be3 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1634,12 +1634,10 @@ PointerDereferenceGadget::getFixits(const Strategy &S) 
const {
 CharSourceRange derefRange = clang::CharSourceRange::getCharRange(
 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
 // Inserts the [0]
-std::optional 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;
   }
@@ -1978,18 +1976,9 @@ static bool hasConflictingOverload(const FunctionDecl 
*FD) {
 //   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
-createOverloadsForFixedParams(const std::vector &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))
@@ -1999,21 +1988,33 @@ createOverloadsForFixedParams(const std::vector 
&ParmsMask, const Strategy
   const LangOptions &LangOpts = Ctx.getLangOpts();
   const unsigned NumParms = FD->getNumParams();
   std::vector NewTysTexts(NumParms);
+  std::vector ParmsMask(NumParms, false);
+  bool AtLeastOneParmToFix = false;
 
   for (unsigned i = 0; i < NumParms; i++) {
-if (!ParmsMask[i])
+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:
+  return std::nullopt;
 
 std::optional PteTyQuals = std::nullopt;
 std::optional PteTy

[clang] 4b5f17e - [-Wunsafe-buffer-usage] Do not assert that function parameters have names

2023-07-19 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-07-19T14:14:28-07:00
New Revision: 4b5f17e008c684998a5ee10454d34714736eb6c5

URL: 
https://github.com/llvm/llvm-project/commit/4b5f17e008c684998a5ee10454d34714736eb6c5
DIFF: 
https://github.com/llvm/llvm-project/commit/4b5f17e008c684998a5ee10454d34714736eb6c5.diff

LOG: [-Wunsafe-buffer-usage] Do not assert that function parameters have names

It is possible that a function parameter does not have a name even in
a function definition.  This patch deals with such cases in generating
function overload fix-its for safe buffers.

Reviewed by: NoQ (Artem Dergachev)

Differential revision: https://reviews.llvm.org/D155641

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 142c56beddafea..495d171c618c99 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1885,9 +1885,12 @@ createOverloadsForFixedParams(unsigned ParmIdx, 
StringRef NewTyText,
   const ParmVarDecl *Parm = FD->getParamDecl(i);
 
   if (Parm->isImplicit())
-continue;
-  assert(Parm->getIdentifier() &&
- "A parameter of a function definition has no name");
+continue;  
+  // FIXME: If a parameter has no name, it is unused in the
+  // definition. So we could just leave it as it is.
+  if (!Parm->getIdentifier()) 
+   // If a parameter of a function definition has no name:
+return std::nullopt;
   if (i == ParmIdx)
 // This is our spanified paramter!
 SS << NewTypeText.str() << "(" << 
Parm->getIdentifier()->getName().str() << ", "

diff  --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
index cef6afd5933b3c..85210dd4d337bd 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -156,4 +156,9 @@ void macroIdentifier(int *MACRO_NAME) { // The fix-it ends 
with a macro. It will
   if (++MyName){}
 }
 
+// CHECK-NOT: fix-it:{{.*}}:
+void parmHasNoName(int *p, int *) { // cannot fix the function because there 
is one parameter has no name.
+  p[5] = 5;
+}
+
 #endif



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


[clang] cfcf76c - [-Wunsafe-buffer-usage] Ignore the FixableGadgets that will not be fixed at an earlier stage

2023-07-25 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-07-25T16:58:27-07:00
New Revision: cfcf76c6ad72581917fc65b598e3b64ca28c5ce1

URL: 
https://github.com/llvm/llvm-project/commit/cfcf76c6ad72581917fc65b598e3b64ca28c5ce1
DIFF: 
https://github.com/llvm/llvm-project/commit/cfcf76c6ad72581917fc65b598e3b64ca28c5ce1.diff

LOG: [-Wunsafe-buffer-usage] Ignore the FixableGadgets that will not be fixed 
at an earlier stage

`FixableGadget`s are not always associated with variables that are unsafe
(warned). For example, they could be associated with variables whose
unsafe operations are suppressed or that are not used in any unsafe
operation. Such `FixableGadget`s will not be fixed. Removing these
`FixableGadget` as early as possible helps improve the performance
and stability of the analysis.

Reviewed by: NoQ (Artem Dergachev), t-rasmud (Rashmi Mudduluru)

Differential revision: https://reviews.llvm.org/D155524

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 78f180447eef6f..7b1c5107a7e049 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1090,16 +1090,6 @@ findGadgets(const Decl *D, const 
UnsafeBufferUsageHandler &Handler,
   }
 
   M.match(*D->getBody(), D->getASTContext());
-
-  // Gadgets "claim" variables they're responsible for. Once this loop 
finishes,
-  // the tracker will only track DREs that weren't claimed by any gadgets,
-  // i.e. not understood by the analysis.
-  for (const auto &G : CB.FixableGadgets) {
-for (const auto *DRE : G->getClaimedVarUseSites()) {
-  CB.Tracker.claimUse(DRE);
-}
-  }
-
   return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
   std::move(CB.Tracker)};
 }
@@ -2250,6 +2240,33 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
 return;
   }
 
+  // If no `WarningGadget`s ever matched, there is no unsafe operations in the
+  //  function under the analysis. No need to fix any Fixables.
+  if (!WarningGadgets.empty()) {
+// Gadgets "claim" variables they're responsible for. Once this loop
+// finishes, the tracker will only track DREs that weren't claimed by any
+// gadgets, i.e. not understood by the analysis.
+for (const auto &G : FixableGadgets) {
+  for (const auto *DRE : G->getClaimedVarUseSites()) {
+Tracker.claimUse(DRE);
+  }
+}
+  }
+
+  // If no `WarningGadget`s ever matched, there is no unsafe operations in the
+  // function under the analysis.  Thus, it early returns here as there is
+  // nothing needs to be fixed.
+  //
+  // Note this claim is based on the assumption that there is no unsafe
+  // variable whose declaration is invisible from the analyzing function.
+  // Otherwise, we need to consider if the uses of those unsafe varuables needs
+  // fix.
+  // So far, we are not fixing any global variables or class members. And,
+  // lambdas will be analyzed along with the enclosing function. So this early
+  // return is correct for now.
+  if (WarningGadgets.empty())
+return;
+
   UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
   FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
 
@@ -2356,6 +2373,34 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
 }
   }
 
+  // Remove a `FixableGadget` if the associated variable is not in the graph
+  // computed above.  We do not want to generate fix-its for such variables,
+  // since they are neither warned nor reachable from a warned one.
+  //
+  // Note a variable is not warned if it is not directly used in any unsafe
+  // operation. A variable `v` is NOT reachable from an unsafe variable, if it
+  // does not exist another variable `u` such that `u` is warned and fixing `u`
+  // (transitively) implicates fixing `v`.
+  //
+  // For example,
+  // ```
+  // void f(int * p) {
+  //   int * a = p; *p = 0;
+  // }
+  // ```
+  // `*p = 0` is a fixable gadget associated with a variable `p` that is 
neither
+  // warned nor reachable from a warned one.  If we add `a[5] = 0` to the end 
of
+  // the function above, `p` becomes reachable from a warned variable.
+  for (auto I = FixablesForAllVars.byVar.begin();
+   I != FixablesForAllVars.byVar.end();) {
+// Note `VisitedVars` contain all the variables in the graph:
+if (VisitedVars.find((*I).first) == VisitedVars.end()) {
+  // no such var in graph:
+  I = FixablesForAllVars.byVar.erase(I);
+} else
+  ++I;
+  }
+
   Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
 
   FixItsForVariableGroup =

diff  --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
index 9301e2a2f1bd6f..47ef0b79729512 100644
--- a/clang/test/Sema

[clang] 41279e8 - [-Wunsafe-buffer-usage] Refactor and improve for parameter fix-its

2023-08-17 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-08-17T15:27:38-07:00
New Revision: 41279e870fa577f8a7f7030b8e45da250d0def9e

URL: 
https://github.com/llvm/llvm-project/commit/41279e870fa577f8a7f7030b8e45da250d0def9e
DIFF: 
https://github.com/llvm/llvm-project/commit/41279e870fa577f8a7f7030b8e45da250d0def9e.diff

LOG: [-Wunsafe-buffer-usage] Refactor and improve for parameter fix-its

- Factor out the code that will be shared by both parameter and local variable 
fix-its
- Add a check to ensure that a TypeLoc::isNull is false before using the TypeLoc
- Remove the special check for whether a fixing variable involves unnamed 
types. This check is unnecessary now.
- Move tests for cv-qualified parameters and unnamed types out of the 
"...-unsupported.cpp" test file.

Reviewed by: NoQ (Artem Dergachev)

Differential revision: https://reviews.llvm.org/D156188

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 87087686171347..41820c80da6b4b 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1380,13 +1380,35 @@ static std::optional 
getRangeText(SourceRange SR,
   return std::nullopt;
 }
 
+// Returns the begin location of the identifier of the given variable
+// declaration.
+static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) {
+  // According to the implementation of `VarDecl`, `VD->getLocation()` actually
+  // returns the begin location of the identifier of the declaration:
+  return VD->getLocation();
+}
+
+// Returns the literal text of the identifier of the given variable 
declaration.
+static std::optional
+getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+  SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
+  SourceLocation ParmIdentEndLoc =
+  Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
+
+  if (ParmIdentEndLoc.isMacroID() &&
+  !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
+return std::nullopt;
+  return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
+}
+
 // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
 // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does 
not
 // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
 // :( ), `Qualifiers` of the pointee type is returned separately through the
 // output parameter `QualifiersToAppend`.
 static std::optional
-getPointeeTypeText(const ParmVarDecl *VD, const SourceManager &SM,
+getPointeeTypeText(const VarDecl *VD, const SourceManager &SM,
const LangOptions &LangOpts,
std::optional *QualifiersToAppend) {
   QualType Ty = VD->getType();
@@ -1395,15 +1417,36 @@ getPointeeTypeText(const ParmVarDecl *VD, const 
SourceManager &SM,
   assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
  "Expecting a VarDecl of type of pointer to object type");
   PteTy = Ty->getPointeeType();
-  if (PteTy->hasUnnamedOrLocalType())
-// If the pointee type is unnamed, we can't refer to it
+
+  TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
+  TypeLoc PteTyLoc;
+
+  // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
+  // the `TypeLoc` of the pointee type:
+  switch (TyLoc.getTypeLocClass()) {
+  case TypeLoc::ConstantArray:
+  case TypeLoc::IncompleteArray:
+  case TypeLoc::VariableArray:
+  case TypeLoc::DependentSizedArray:
+  case TypeLoc::Decayed:
+assert(isa(VD) && "An array type shall not be treated as a "
+   "pointer type unless it decays.");
+PteTyLoc = TyLoc.getNextTypeLoc();
+break;
+  case TypeLoc::Pointer:
+PteTyLoc = TyLoc.castAs().getPointeeLoc();
+break;
+  default:
+return std::nullopt;
+  }
+  if (PteTyLoc.isNull())
+// Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
+// when the pointer type is `auto`.
 return std::nullopt;
 
-  TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc();
-  TypeLoc PteTyLoc = TyLoc.getUnqualifiedLoc().getNextTypeLoc();
-  SourceLocation VDNameStartLoc = VD->getLocation();
+  SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD);
 
-  if (!(VDNameStartLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
+  if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
 // We are expecting these locations to be valid. But in some cases, they 
are
 // not all valid. It is a Clang bug to me and we are not responsible for
 // fixing it.  So we will just give up for now

[clang] 8437847 - [-Wunsafe-buffer-usage][NFC] Slightly refactor and optimize the code

2023-08-17 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-08-17T16:24:47-07:00
New Revision: 843784764ab58e35f8aa2da97f07dc5e810f4bcb

URL: 
https://github.com/llvm/llvm-project/commit/843784764ab58e35f8aa2da97f07dc5e810f4bcb
DIFF: 
https://github.com/llvm/llvm-project/commit/843784764ab58e35f8aa2da97f07dc5e810f4bcb.diff

LOG: [-Wunsafe-buffer-usage][NFC] Slightly refactor and optimize the code

Slightly refactor and optimize the code in preparation for
implementing grouping parameters for a single fix-it.

Reviewed by: NoQ (Artem Dergachev), t-rasmud (Rashmi Mudduluru)

Differential revision: https://reviews.llvm.org/D156474

Added: 


Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 13f28076c6f4d7..3bb106f6ab83e1 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -20,7 +20,19 @@
 
 namespace clang {
 
-using DefMapTy = llvm::DenseMap>;
+using VarGrpTy = std::vector;
+using VarGrpRef = ArrayRef;
+
+class VariableGroupsManager {
+public:
+  VariableGroupsManager() = default;
+  virtual ~VariableGroupsManager() = default;
+  /// Returns the set of variables (including `Var`) that need to be fixed
+  /// together in one step.
+  ///
+  /// `Var` must be a variable that needs fix (so it must be in a group).
+  virtual VarGrpRef getGroupOfVar(const VarDecl *Var) const;
+};
 
 /// The interface that lets the caller handle unsafe buffer usage analysis
 /// results by overriding this class's handle... methods.
@@ -53,7 +65,7 @@ class UnsafeBufferUsageHandler {
   /// all variables that must be fixed together (i.e their types must be 
changed to the
   /// same target type to prevent type mismatches) into a single fixit.
   virtual void handleUnsafeVariableGroup(const VarDecl *Variable,
- const DefMapTy &VarGrpMap,
+ const VariableGroupsManager 
&VarGrpMgr,
  FixItList &&Fixes) = 0;
 
 #ifndef NDEBUG

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 2c36d78e60c89b..f2e0190a9221cb 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1176,7 +1176,11 @@ groupWarningGadgetsByVar(const WarningGadgetList 
&AllUnsafeOperations) {
 }
 
 struct FixableGadgetSets {
-  std::map> byVar;
+  std::map,
+   // To keep keys sorted by their locations in the map so that the
+   // order is deterministic:
+   CompareNode>
+  byVar;
 };
 
 static FixableGadgetSets
@@ -1382,7 +1386,7 @@ static std::optional getRangeText(SourceRange 
SR,
  const SourceManager &SM,
  const LangOptions &LangOpts) {
   bool Invalid = false;
-  CharSourceRange CSR = CharSourceRange::getCharRange(SR.getBegin(), 
SR.getEnd());
+  CharSourceRange CSR = CharSourceRange::getCharRange(SR);
   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
 
   if (!Invalid)
@@ -2225,7 +2229,7 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
  ASTContext &Ctx,
   /* The function decl under analysis */ const Decl *D,
   const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
-  const DefMapTy &VarGrpMap) {
+  const VariableGroupsManager &VarGrpMgr) {
   std::map FixItsForVariable;
   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
 FixItsForVariable[VD] =
@@ -2261,9 +2265,10 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
   continue;
 }
 
-const auto VarGroupForVD = VarGrpMap.find(VD);
-if (VarGroupForVD != VarGrpMap.end()) {
-  for (const VarDecl * V : VarGroupForVD->second) {
+
+   {
+  const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(VD);
+  for (const VarDecl * V : VarGroupForVD) {
 if (V == VD) {
   continue;
 }
@@ -2275,7 +2280,7 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
 
   if (ImpossibleToFix) {
 FixItsForVariable.erase(VD);
-for (const VarDecl * V : VarGroupForVD->second) {
+for (const VarDecl * V : VarGroupForVD) {
   FixItsForVariable.erase(V);
 }
 continue;
@@ -2293,30 +2298,24 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
 }
   }
 
-  for (auto VD : FixItsForVariable) {
-const auto VarGroupForVD = VarGrpMap.find(VD.first);
-const Strategy::Kind ReplacementTypeForVD = S.lookup(VD.first);
-if (VarGroupForVD != V

[clang] ac9a76d - Revert "[-Wunsafe-buffer-usage][NFC] Slightly refactor and optimize the code"

2023-08-17 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-08-17T16:42:30-07:00
New Revision: ac9a76d7487b9af1ace626eb90064194cb12c53d

URL: 
https://github.com/llvm/llvm-project/commit/ac9a76d7487b9af1ace626eb90064194cb12c53d
DIFF: 
https://github.com/llvm/llvm-project/commit/ac9a76d7487b9af1ace626eb90064194cb12c53d.diff

LOG: Revert "[-Wunsafe-buffer-usage][NFC] Slightly refactor and optimize the 
code"

This reverts commit 843784764ab58e35f8aa2da97f07dc5e810f4bcb.
There is a build failure caused by this commit.

Added: 


Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 3bb106f6ab83e1..13f28076c6f4d7 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -20,19 +20,7 @@
 
 namespace clang {
 
-using VarGrpTy = std::vector;
-using VarGrpRef = ArrayRef;
-
-class VariableGroupsManager {
-public:
-  VariableGroupsManager() = default;
-  virtual ~VariableGroupsManager() = default;
-  /// Returns the set of variables (including `Var`) that need to be fixed
-  /// together in one step.
-  ///
-  /// `Var` must be a variable that needs fix (so it must be in a group).
-  virtual VarGrpRef getGroupOfVar(const VarDecl *Var) const;
-};
+using DefMapTy = llvm::DenseMap>;
 
 /// The interface that lets the caller handle unsafe buffer usage analysis
 /// results by overriding this class's handle... methods.
@@ -65,7 +53,7 @@ class UnsafeBufferUsageHandler {
   /// all variables that must be fixed together (i.e their types must be 
changed to the
   /// same target type to prevent type mismatches) into a single fixit.
   virtual void handleUnsafeVariableGroup(const VarDecl *Variable,
- const VariableGroupsManager 
&VarGrpMgr,
+ const DefMapTy &VarGrpMap,
  FixItList &&Fixes) = 0;
 
 #ifndef NDEBUG

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index f2e0190a9221cb..2c36d78e60c89b 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1176,11 +1176,7 @@ groupWarningGadgetsByVar(const WarningGadgetList 
&AllUnsafeOperations) {
 }
 
 struct FixableGadgetSets {
-  std::map,
-   // To keep keys sorted by their locations in the map so that the
-   // order is deterministic:
-   CompareNode>
-  byVar;
+  std::map> byVar;
 };
 
 static FixableGadgetSets
@@ -1386,7 +1382,7 @@ static std::optional getRangeText(SourceRange 
SR,
  const SourceManager &SM,
  const LangOptions &LangOpts) {
   bool Invalid = false;
-  CharSourceRange CSR = CharSourceRange::getCharRange(SR);
+  CharSourceRange CSR = CharSourceRange::getCharRange(SR.getBegin(), 
SR.getEnd());
   StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
 
   if (!Invalid)
@@ -2229,7 +2225,7 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
  ASTContext &Ctx,
   /* The function decl under analysis */ const Decl *D,
   const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
-  const VariableGroupsManager &VarGrpMgr) {
+  const DefMapTy &VarGrpMap) {
   std::map FixItsForVariable;
   for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
 FixItsForVariable[VD] =
@@ -2265,10 +2261,9 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
   continue;
 }
 
-
-   {
-  const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(VD);
-  for (const VarDecl * V : VarGroupForVD) {
+const auto VarGroupForVD = VarGrpMap.find(VD);
+if (VarGroupForVD != VarGrpMap.end()) {
+  for (const VarDecl * V : VarGroupForVD->second) {
 if (V == VD) {
   continue;
 }
@@ -2280,7 +2275,7 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
 
   if (ImpossibleToFix) {
 FixItsForVariable.erase(VD);
-for (const VarDecl * V : VarGroupForVD) {
+for (const VarDecl * V : VarGroupForVD->second) {
   FixItsForVariable.erase(V);
 }
 continue;
@@ -2298,24 +2293,30 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const 
Strategy &S,
 }
   }
 
-  // The map that maps each variable `v` to fix-its for the whole group where
-  // `v` is in:
-  std::map FinalFixItsForVariable{
-  FixItsForVariable};
+  for (auto VD : FixItsForVariable) {
+const auto VarGroupForVD = VarGrpMap.find(VD.first);
+const Strategy::Kind ReplacementTypeForVD = S.l

[clang] 3a67b91 - [-Wunsafe-buffer-usage] Refactor to let local variable fix-its and parameter fix-its share common code

2023-08-21 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-08-21T14:50:04-07:00
New Revision: 3a67b912386e70073efcb2225c6354b85048b1ae

URL: 
https://github.com/llvm/llvm-project/commit/3a67b912386e70073efcb2225c6354b85048b1ae
DIFF: 
https://github.com/llvm/llvm-project/commit/3a67b912386e70073efcb2225c6354b85048b1ae.diff

LOG: [-Wunsafe-buffer-usage] Refactor to let local variable fix-its and 
parameter fix-its share common code

Refactor the code for local variable fix-its so that it reuses the
code for parameter fix-its, which is in general better. For example,
cv-qualifiers are supported.

Reviewed by: NoQ (Artem Dergachev), t-rasmud (Rashmi Mudduluru)

Differential revision: https://reviews.llvm.org/D156189

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 29543131dae230..7b8d758c60fa70 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1716,7 +1716,7 @@ std::optional 
UPCPreIncrementGadget::getFixits(const Strategy &S) con
 //   `Init` a pointer to the initializer expression
 //   `Ctx` a reference to the ASTContext
 static FixItList
-populateInitializerFixItWithSpan(const Expr *Init, ASTContext &Ctx,
+FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
  const StringRef UserFillPlaceHolder) {
   const SourceManager &SM = Ctx.getSourceManager();
   const LangOptions &LangOpts = Ctx.getLangOpts();
@@ -1827,59 +1827,67 @@ static std::optional 
createSpanTypeForVarDecl(const VarDecl *VD,
 }
 
 // For a `VarDecl` of the form `T  * var (= Init)?`, this
-// function generates a fix-it for the declaration, which re-declares `var` to
-// be of `span` type and transforms the initializer, if present, to a span
-// constructor---`span var {Init, Extent}`, where `Extent` may need the user
-// to fill in.
+// function generates fix-its that
+//  1) replace `T * var` with `std::span var`; and
+//  2) change `Init` accordingly to a span constructor, if it exists.
 //
 // FIXME: support Multi-level pointers
 //
 // Parameters:
 //   `D` a pointer the variable declaration node
 //   `Ctx` a reference to the ASTContext
+//   `UserFillPlaceHolder` the user-input placeholder text
 // Returns:
-//the generated fix-it
-static FixItList fixVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
-const StringRef UserFillPlaceHolder,
-UnsafeBufferUsageHandler &Handler) {
-  (void)Handler; // Suppress unused variable warning in release builds.
-  const QualType &SpanEltT = D->getType()->getPointeeType();
-  assert(!SpanEltT.isNull() && "Trying to fix a non-pointer type variable!");
-
+//the non-empty fix-it list, if fix-its are successfuly generated; empty
+//list otherwise.
+static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
+ const StringRef UserFillPlaceHolder,
+ UnsafeBufferUsageHandler &Handler) {
   FixItList FixIts{};
-  std::optional
-  ReplacementLastLoc; // the inclusive end location of the replacement
-  const SourceManager &SM = Ctx.getSourceManager();
+  std::optional SpanTyText = createSpanTypeForVarDecl(D, Ctx);
+
+  if (!SpanTyText) {
+DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
+return {};
+  }
 
+  // Will hold the text for `std::span Ident`:
+  std::stringstream SS;
+
+  SS << *SpanTyText;
+  // Append qualifiers to the type of `D`, if any:
+  if (D->getType().hasQualifiers())
+SS << " " << D->getType().getQualifiers().getAsString();
+
+  // The end of the range of the original source that will be replaced
+  // by `std::span ident`:
+  SourceLocation EndLocForReplacement = D->getEndLoc();
+  std::optional IdentText =
+  getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
+
+  if (!IdentText) {
+DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
+return {};
+  }
+  // Fix the initializer if it exists:
   if (const Expr *Init = D->getInit()) {
 FixItList InitFixIts =
-populateInitializerFixItWithSpan(Init, Ctx, UserFillPlaceHolder);
-
-if (InitFixIts.empty()) {
-  DEBUG_NOTE_DECL_FAIL(D, " : empty initializer");
+FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
+if (InitFixIts.empty())
   return {};
-}
-
-// The loc right before the initializer:
-Replacement

[clang] b58e528 - [-Wunsafe-buffer-usage] Stop generating incorrect fix-its for variable declarations with unsupported specifiers

2023-08-21 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-08-21T16:34:44-07:00
New Revision: b58e52889808e0e8da55ac77f651762f202aa4c5

URL: 
https://github.com/llvm/llvm-project/commit/b58e52889808e0e8da55ac77f651762f202aa4c5
DIFF: 
https://github.com/llvm/llvm-project/commit/b58e52889808e0e8da55ac77f651762f202aa4c5.diff

LOG: [-Wunsafe-buffer-usage] Stop generating incorrect fix-its for variable 
declarations with unsupported specifiers

We have to give up on fixing a variable declaration if it has
specifiers that are not supported yet.  We could support these
specifiers incrementally using the same approach as how we deal with
cv-qualifiers. If a fixing variable declaration has a storage
specifier, instead of trying to find out the source location of the
specifier or to avoid touching it, we add the keyword to a
canonicalized place in the fix-it text that replaces the whole
declaration.

Reviewed by: NoQ (Artem Dergachev), jkorous (Jan Korous)

Differential revision: https://reviews.llvm.org/D156192

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 7b8d758c60fa70..f586c0ad2caf98 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1416,6 +1416,27 @@ getVarDeclIdentifierText(const VarDecl *VD, const 
SourceManager &SM,
   return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
 }
 
+// We cannot fix a variable declaration if it has some other specifiers than 
the
+// type specifier.  Because the source ranges of those specifiers could overlap
+// with the source range that is being replaced using fix-its.  Especially when
+// we often cannot obtain accurate source ranges of cv-qualified type
+// specifiers.
+// FIXME: also deal with type attributes
+static bool hasUnsupportedSpecifiers(const VarDecl *VD,
+ const SourceManager &SM) {
+  // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
+  // source range of `VD`:
+  bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
+return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
+  VD->getBeginLoc())) &&
+   !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
+  At->getRange().getBegin()));
+  });
+  return VD->isInlineSpecified() || VD->isConstexpr() ||
+ VD->hasConstantInitialization() || !VD->hasLocalStorage() ||
+ AttrRangeOverlapping;
+}
+
 // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
 // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does 
not
 // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
@@ -1841,8 +1862,11 @@ static std::optional 
createSpanTypeForVarDecl(const VarDecl *VD,
 //the non-empty fix-it list, if fix-its are successfuly generated; empty
 //list otherwise.
 static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
- const StringRef UserFillPlaceHolder,
- UnsafeBufferUsageHandler &Handler) {
+const StringRef UserFillPlaceHolder,
+UnsafeBufferUsageHandler &Handler) {
+  if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager()))
+return {};
+
   FixItList FixIts{};
   std::optional SpanTyText = createSpanTypeForVarDecl(D, Ctx);
 
@@ -2076,6 +2100,10 @@ createOverloadsForFixedParams(unsigned ParmIdx, 
StringRef NewTyText,
 // `createOverloadsForFixedParams`).
 static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext 
&Ctx,
   UnsafeBufferUsageHandler &Handler) {
+  if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager())) {
+DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
+return {};
+  }
   if (PVD->hasDefaultArg()) {
 // FIXME: generate fix-its for default values:
 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");

diff  --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
index fcc798622918fa..b9c8bec77787de 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -58,12 +58,46 @@ void local_variable_qualifiers_specifiers() {
   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:24}:"std::span const q"
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:25-[[@LINE-2]]:25}:"{"
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  [[deprecated]] const int * x 

cfe-commits@lists.llvm.org

2023-04-05 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-04-05T14:54:03-07:00
New Revision: ca6ceeb0d6cd5b8545410d878c8d7bcce9538a3a

URL: 
https://github.com/llvm/llvm-project/commit/ca6ceeb0d6cd5b8545410d878c8d7bcce9538a3a
DIFF: 
https://github.com/llvm/llvm-project/commit/ca6ceeb0d6cd5b8545410d878c8d7bcce9538a3a.diff

LOG: Reland "[-Wunsafe-buffer-usage] Fix-Its transforming `&DRE[any]` to 
`&DRE.data()[any]`"

This commit relands 87b5807d3802b932c06d83c4287014872aa2caab, where a
test fails on a few specific targets.  Now hard-code a target
for the test.

Added: 

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/lib/Analysis/UnsafeBufferUsage.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 8957ab664ed93..f78cf2c57689c 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -43,7 +43,7 @@ class UnsafeBufferUsageHandler {
 
   /// Returns the text indicating that the user needs to provide input there:
   virtual std::string
-  getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
+  getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") const {
 std::string s = std::string("<# ");
 s += HintTextToUser;
 s += " #>";

diff  --git 
a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index a8485682c1d1f..f0c99a767071f 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -30,9 +30,10 @@ WARNING_GADGET(Decrement)
 WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
-FIXABLE_GADGET(ULCArraySubscript)
+FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
+FIXABLE_GADGET(UPCAddressofArraySubscript) // '&DRE[any]' in an Unspecified 
Pointer Context
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 4a8358af68ec5..1ef276ab90444 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include 
 #include 
+#include 
 
 using namespace llvm;
 using namespace clang;
@@ -112,6 +113,15 @@ class MatchDescendantVisitor
   bool Matches;
 };
 
+// Because we're dealing with raw pointers, let's define what we mean by that.
+static auto hasPointerType() {
+return hasType(hasCanonicalType(pointerType()));
+}
+
+static auto hasArrayType() {
+return hasType(hasCanonicalType(arrayType()));
+}
+  
 AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher, innerMatcher) 
{
   const DynTypedMatcher &DTM = static_cast(innerMatcher);
 
@@ -145,6 +155,30 @@ static auto 
isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) {
 ));
 // clang-format off
 }
+
+
+// Returns a matcher that matches any expression `e` such that `InnerMatcher`
+// matches `e` and `e` is in an Unspecified Pointer Context (UPC).
+static internal::Matcher
+isInUnspecifiedPointerContext(internal::Matcher InnerMatcher) {
+  // A UPC can be
+  // 1. an argument of a function call (except the callee has [[unsafe_...]]
+  // attribute), or
+  // 2. the operand of a cast operation; or
+  // ...
+  auto CallArgMatcher =
+  callExpr(hasAnyArgument(allOf(
+   hasPointerType() /* array also decays to pointer type*/,
+   InnerMatcher)),
+   unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage);
+  auto CastOperandMatcher =
+  explicitCastExpr(hasCastKind(CastKind::CK_PointerToIntegral),
+   castSubExpr(allOf(hasPointerType(), InnerMatcher)));
+
+ return stmt(anyOf(CallArgMatcher, CastOperandMatcher));
+  // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
+  // don't have to check that.)
+}
 } // namespace clang::ast_matchers
 
 namespace {
@@ -159,15 +193,6 @@ using FixItList = SmallVector;
 class Strategy;
 } // namespace
 
-// Because we're dealing with raw pointers, let's define what we mean by that.
-static auto hasPointerType() {
-return hasType(hasCanonicalType(pointerType()));
-}
-
-static auto hasArrayType() {
-return hasType(hasCanonicalType(arrayType()));
-}
-
 namespace {
 /// Gadget is an individual operation in the code that may be of interest to
 /// this analysis. Each (non-abstract) subclass corresponds to a 

[clang] 88f7f01 - [-Wunsafe-buffer-usage] Match unsafe pointers being casted to bool or participating in pointer subtractions

2023-04-11 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-04-11T15:09:51-07:00
New Revision: 88f7f018e23b24d3c31dd2b4f3cd68481d1739c1

URL: 
https://github.com/llvm/llvm-project/commit/88f7f018e23b24d3c31dd2b4f3cd68481d1739c1
DIFF: 
https://github.com/llvm/llvm-project/commit/88f7f018e23b24d3c31dd2b4f3cd68481d1739c1.diff

LOG: [-Wunsafe-buffer-usage] Match unsafe pointers being casted to bool or 
participating in pointer subtractions

Add two new unique cases to the Unspecified Pointer Context (UPC),
under which we match unsafe operation patterns:
- A pointer being casted to a boolean value is in a UPC;
- A pointer participating in pointer subtraction is in a UPC.

Reviewed by: NoQ (Artem Dergachev), malavikasamak (Malavika Samak)

Differential revision: https://reviews.llvm.org/D144064

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index fdc7584ad6ff1..6a597c8851002 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -163,24 +163,40 @@ static internal::Matcher
 isInUnspecifiedPointerContext(internal::Matcher InnerMatcher) {
   // A UPC can be
   // 1. an argument of a function call (except the callee has [[unsafe_...]]
-  // attribute), or
-  // 2. the operand of a cast operation; or
+  //attribute), or
+  // 2. the operand of a pointer-to-(integer or bool) cast operation; or
   // 3. the operand of a comparator operation; or
+  // 4. the operand of a pointer subtraction operation
+  //(i.e., computing the distance between two pointers); or ...
+
   auto CallArgMatcher =
   callExpr(forEachArgumentWithParam(InnerMatcher,
   hasPointerType() /* array also decays to pointer type*/),
   unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage);
 
   auto CastOperandMatcher =
-  explicitCastExpr(hasCastKind(CastKind::CK_PointerToIntegral),
-   castSubExpr(allOf(hasPointerType(), InnerMatcher)));
+  castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
+hasCastKind(CastKind::CK_PointerToBoolean)),
+  castSubExpr(allOf(hasPointerType(), InnerMatcher)));
 
   auto CompOperandMatcher =
   binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
  eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
 hasRHS(allOf(hasPointerType(), InnerMatcher;
 
-  return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher));
+  // A matcher that matches pointer subtractions:
+  auto PtrSubtractionMatcher =
+  binaryOperator(hasOperatorName("-"),
+// Note that here we need both LHS and RHS to be
+// pointer. Then the inner matcher can match any of
+// them:
+allOf(hasLHS(hasPointerType()),
+  hasRHS(hasPointerType())),
+eachOf(hasLHS(InnerMatcher),
+   hasRHS(InnerMatcher)));
+
+  return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
+   PtrSubtractionMatcher));
   // FIXME: any more cases? (UPC excludes the RHS of an assignment.  For now we
   // don't have to check that.)
 }

diff  --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
index 8fdbc4bed4f6d..474e7495e3e1d 100644
--- 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
+++ 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
@@ -13,12 +13,30 @@ void address_to_integer(int x) {
   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"&p.data()[x]"
 }
 
+void address_to_bool(int x) {
+  int * p = new int[10];
+  bool a = (bool) &p[5];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:19-[[@LINE-1]]:24}:"&p.data()[5]"
+  bool b = (bool) &p[x];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:19-[[@LINE-1]]:24}:"&p.data()[x]"
+
+  bool a1 = &p[5];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:18}:"&p.data()[5]"
+  bool b1 = &p[x];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:18}:"&p.data()[x]"
+
+  if (&p[5]) {
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:12}:"&p.data()[5]"
+return;
+  }
+}
+
 void call_argument(int x) {
   int * p = new int[10];
 
   f((unsigned long) &p[5], &p[x]);
-  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:21-[[@LINE-1]]:26}:"&p.data()[5]"
-  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:28-[[@LINE-2]]:33}:"&p.data()[x]"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:21-[[@LINE-1]]:26}:"&p.data()[5]"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:28-[[@LINE

[clang] 762af11 - [-Wunsafe-buffer-usage] Add a Fixable for pointer pre-increment

2023-04-12 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-04-12T14:51:46-07:00
New Revision: 762af11d4c5d0bd1e76f23a07087773db09ef17d

URL: 
https://github.com/llvm/llvm-project/commit/762af11d4c5d0bd1e76f23a07087773db09ef17d
DIFF: 
https://github.com/llvm/llvm-project/commit/762af11d4c5d0bd1e76f23a07087773db09ef17d.diff

LOG: [-Wunsafe-buffer-usage] Add a Fixable for pointer pre-increment

For a pointer type expression `e` of the form `++DRE`, if `e` is under
an Unspecified Pointer Context (UPC) and `DRE` is suppose to be
transformed to have std:span type, we generate fix-its that transform `e` to
`(DRE = DRE.subspan(1)).data()`.

For reference, `e` is in an UPC if `e` is
- an argument of a function call (except the callee has [[unsafe_buffer_usage]] 
attribute), or
- the operand of a cast-to-(Integer or Boolean) operation; or
- the operand of a pointer subtraction operation; or
- the operand of a pointer comparison operation;

We may extend the definition of UPC by adding more cases later.

Reviewed by: NoQ (Artem Dergachev)

Differential revision: https://reviews.llvm.org/D144304

Added: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/lib/Analysis/UnsafeBufferUsage.cpp

Removed: 




diff  --git 
a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index a52b00bf8d4ce..a112b6d105025 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -35,6 +35,7 @@ FIXABLE_GADGET(DerefSimplePtrArithFixable)
 FIXABLE_GADGET(PointerDereference)
 FIXABLE_GADGET(UPCAddressofArraySubscript) // '&DRE[any]' in an Unspecified 
Pointer Context
 FIXABLE_GADGET(UPCStandalonePointer)
+FIXABLE_GADGET(UPCPreIncrement)// '++Ptr' in an Unspecified 
Pointer Context
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 6a597c8851002..ff818140b2d89 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -139,6 +139,11 @@ AST_MATCHER_P(CastExpr, castSubExpr, 
internal::Matcher, innerMatcher) {
   return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
 }
 
+// Matches a `UnaryOperator` whose operator is pre-increment:
+AST_MATCHER(UnaryOperator, isPreInc) {
+  return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
+}  
+
 // Returns a matcher that matches any expression 'e' such that `innerMatcher`
 // matches 'e' and 'e' is in an Unspecified Lvalue Context.
 static auto isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) 
{
@@ -719,6 +724,46 @@ class Strategy {
 };
 } // namespace
 
+
+// Representing a pointer type expression of the form `++Ptr` in an Unspecified
+// Pointer Context (UPC):
+class UPCPreIncrementGadget : public FixableGadget {
+private:
+  static constexpr const char *const UPCPreIncrementTag =
+"PointerPreIncrementUnderUPC";
+  const UnaryOperator *Node; // the `++Ptr` node
+
+public:
+  UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
+: FixableGadget(Kind::UPCPreIncrement),
+  Node(Result.Nodes.getNodeAs(UPCPreIncrementTag)) {
+assert(Node != nullptr && "Expecting a non-null matching result");
+  }
+
+  static bool classof(const Gadget *G) {
+return G->getKind() == Kind::UPCPreIncrement;
+  }
+
+  static Matcher matcher() {
+// Note here we match `++Ptr` for any expression `Ptr` of pointer type.
+// Although currently we can only provide fix-its when `Ptr` is a DRE, we
+// can have the matcher be general, so long as `getClaimedVarUseSites` does
+// things right.
+return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
+   
unaryOperator(isPreInc(),
+   
  hasUnaryOperand(declRefExpr())
+   
  ).bind(UPCPreIncrementTag);
+  }
+
+  virtual std::optional getFixits(const Strategy &S) const override;
+
+  virtual const Stmt *getBaseStmt() const override { return Node; }
+
+  virtual DeclUseList getClaimedVarUseSites() const override {
+return {dyn_cast(Node->getSubExpr())};
+  }
+};
+
 // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
 // ptr)`:
 class DerefSimplePtrArithFixableGadget : public FixableGadget {
@@ -1196,6 +1241,36 @@ fixUPCAddressofArraySubscriptWithSpan(const 
UnaryOperator *Node) {
   FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
 }
 
+
+std::optional UPCPreIncrementGadget::getFixits(const Strategy &S) 
const {
+  DeclUseList DREs = getCl

[clang] 6251b04 - [-Wunsafe-buffer-usage] A follow-up fix to 762af11d4c5d0bd1e76f23a07087773db09ef17d

2023-04-13 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-04-13T11:24:46-07:00
New Revision: 6251b04fc76a45d5a9c00bfb5010b9bbbcefb9b0

URL: 
https://github.com/llvm/llvm-project/commit/6251b04fc76a45d5a9c00bfb5010b9bbbcefb9b0
DIFF: 
https://github.com/llvm/llvm-project/commit/6251b04fc76a45d5a9c00bfb5010b9bbbcefb9b0.diff

LOG: [-Wunsafe-buffer-usage] A follow-up fix to 
762af11d4c5d0bd1e76f23a07087773db09ef17d

Add -triple=arm-apple to the test 
`SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp`

Added: 


Modified: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp

Removed: 




diff  --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp
index d3d7e8fe821fb..6a7446d54c362 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -triple=arm-apple -std=c++20 -Wunsafe-buffer-usage 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
 
 void foo(int * , int *);
 



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


[clang] a29e676 - [-Wunsafe-buffer-usage] Generate fix-it for local variable declarations

2023-02-07 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-07T13:17:44-08:00
New Revision: a29e67614c3b7018287e5f68c57bba7618aa880e

URL: 
https://github.com/llvm/llvm-project/commit/a29e67614c3b7018287e5f68c57bba7618aa880e
DIFF: 
https://github.com/llvm/llvm-project/commit/a29e67614c3b7018287e5f68c57bba7618aa880e.diff

LOG: [-Wunsafe-buffer-usage] Generate fix-it for local variable declarations

Use clang fix-its to transform declarations of local variables, which are used 
for buffer access , to be of std::span type.

We placed a few limitations to keep the solution simple:
- it only transforms local variable declarations (no parameter declaration);
- it only considers single level pointers, i.e., pointers of type T * 
regardless of whether T is again a pointer;
- it only transforms to std::span types (no std::array, or std::span::iterator, 
or ...);
- it can only transform a VarDecl that belongs to a DeclStmt whose has a single 
child.

One of the purposes of keeping this patch simple enough is to first
evaluate if fix-it is an appropriate approach to do the
transformation.

Reviewed by: NoQ, jkorous

Differential revision: https://reviews.llvm.org/D139737

Added: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index e3f87cd0f3660..04c9fdea444f8 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -37,6 +37,15 @@ class UnsafeBufferUsageHandler {
   /// Invoked when a fix is suggested against a variable.
   virtual void handleFixableVariable(const VarDecl *Variable,
  FixItList &&List) = 0;
+
+  /// Returns the text indicating that the user needs to provide input there:
+  virtual std::string
+  getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
+std::string s = std::string("<# ");
+s += HintTextToUser;
+s += " #>";
+return s;
+  }
 };
 
 // This function invokes the analysis and allows the caller to react to it

diff  --git 
a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 78889da32b3b4..75657d8d9a584 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -30,6 +30,7 @@ WARNING_GADGET(Decrement)
 WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
+FIXABLE_GADGET(ULCArraySubscript)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bc70dbf4a624d..9b9ec23a2f21f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11790,6 +11790,8 @@ def warn_unsafe_buffer_operation : Warning<
   InGroup, DefaultIgnore;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
+def note_unsafe_buffer_variable_fixit : Note<
+  "change type of '%0' to '%select{std::span|std::array|std::span::iterator}1' 
to preserve bounds information">;
 def err_loongarch_builtin_requires_la32 : Error<
   "this builtin requires target: loongarch32">;
 } // end of sema component.

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 331479a80f6cc..7741d6cd4157f 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
 #include "llvm/ADT/SmallVector.h"
 #include 
 #include 
@@ -115,6 +116,19 @@ AST_MATCHER_P(Stmt, forEveryDescendant, 
internal::Matcher, innerMatcher) {
   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, 
ASTMatchFinder::BK_All);  
   return Visitor.findMatch(DynTypedNode::create(Node));
 }
+
+AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher, innerMatcher) {
+  return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
+}
+
+// Returns a matcher that matches any expression 'e' such that `innerMatcher`
+// matches 'e' and 'e' is in an Unspecified Lvalue Context.
+static internal::Matcher
+isInUnspecifiedLvalueContext(internal::Matche

[clang] 622be09 - Revert "[-Wunsafe-buffer-usage] Generate fix-it for local variable declarations"

2023-02-07 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-07T14:47:43-08:00
New Revision: 622be09c815266632e204eaf1c7a35f050220459

URL: 
https://github.com/llvm/llvm-project/commit/622be09c815266632e204eaf1c7a35f050220459
DIFF: 
https://github.com/llvm/llvm-project/commit/622be09c815266632e204eaf1c7a35f050220459.diff

LOG: Revert "[-Wunsafe-buffer-usage] Generate fix-it for local variable 
declarations"

This reverts commit a29e67614c3b7018287e5f68c57bba7618aa880e.

Added: 


Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp



diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 04c9fdea444f8..e3f87cd0f3660 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -37,15 +37,6 @@ class UnsafeBufferUsageHandler {
   /// Invoked when a fix is suggested against a variable.
   virtual void handleFixableVariable(const VarDecl *Variable,
  FixItList &&List) = 0;
-
-  /// Returns the text indicating that the user needs to provide input there:
-  virtual std::string
-  getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
-std::string s = std::string("<# ");
-s += HintTextToUser;
-s += " #>";
-return s;
-  }
 };
 
 // This function invokes the analysis and allows the caller to react to it

diff  --git 
a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 75657d8d9a584..78889da32b3b4 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -30,7 +30,6 @@ WARNING_GADGET(Decrement)
 WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
-FIXABLE_GADGET(ULCArraySubscript)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9b9ec23a2f21f..bc70dbf4a624d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11790,8 +11790,6 @@ def warn_unsafe_buffer_operation : Warning<
   InGroup, DefaultIgnore;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
-def note_unsafe_buffer_variable_fixit : Note<
-  "change type of '%0' to '%select{std::span|std::array|std::span::iterator}1' 
to preserve bounds information">;
 def err_loongarch_builtin_requires_la32 : Error<
   "this builtin requires target: loongarch32">;
 } // end of sema component.

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 7741d6cd4157f..331479a80f6cc 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,7 +9,6 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/Lex/Lexer.h"
 #include "llvm/ADT/SmallVector.h"
 #include 
 #include 
@@ -116,19 +115,6 @@ AST_MATCHER_P(Stmt, forEveryDescendant, 
internal::Matcher, innerMatcher) {
   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, 
ASTMatchFinder::BK_All);  
   return Visitor.findMatch(DynTypedNode::create(Node));
 }
-
-AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher, innerMatcher) {
-  return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
-}
-
-// Returns a matcher that matches any expression 'e' such that `innerMatcher`
-// matches 'e' and 'e' is in an Unspecified Lvalue Context.
-static internal::Matcher
-isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) {
-  return implicitCastExpr(hasCastKind(CastKind::CK_LValueToRValue),
-  castSubExpr(innerMatcher));
-  // FIXME: add assignmentTo context...
-}
 } // namespace clang::ast_matchers
 
 namespace {
@@ -296,7 +282,7 @@ class DecrementGadget : public WarningGadget {
 /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
 /// it doesn't have any bounds checks for the array.
 class ArraySubscriptGadget : public WarningGadget {
-  static constexpr const char *const ArraySubscrTag = "ArraySubscript";
+  static constexpr const char *const ArraySubscrTag = "arraySubscr";
   const ArraySubscriptExpr *ASE;
 
 public:
@@ -407,48 +393,6 @@ class

[clang] bdf4f2b - [-Wunsafe-buffer-usage] Generate fix-it for local variable declarations

2023-02-07 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-07T15:40:19-08:00
New Revision: bdf4f2bea50e87f5b9273e3bbc9a7753bca3a6bb

URL: 
https://github.com/llvm/llvm-project/commit/bdf4f2bea50e87f5b9273e3bbc9a7753bca3a6bb
DIFF: 
https://github.com/llvm/llvm-project/commit/bdf4f2bea50e87f5b9273e3bbc9a7753bca3a6bb.diff

LOG: [-Wunsafe-buffer-usage] Generate fix-it for local variable declarations

Use clang fix-its to transform declarations of local variables, which
are used for buffer access , to be of std::span type.

We placed a few limitations to keep the solution simple:
- it only transforms local variable declarations (no parameter declaration);
- it only considers single level pointers, i.e., pointers of type T * 
regardless of whether T is again a pointer;
- it only transforms to std::span types (no std::array, or std::span::iterator, 
or ...);
- it can only transform a VarDecl that belongs to a DeclStmt whose has a 
single child.

One of the purposes of keeping this patch simple enough is to first
evaluate if fix-it is an appropriate approach to do the
transformation.

This commit was reverted by 622be09c815266632e204eaf1c7a35f050220459
for a compilation warning and now it is fixed.

Reviewed by: NoQ, jkorous

Differential revision: https://reviews.llvm.org/D139737

Added: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index e3f87cd0f3660..04c9fdea444f8 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -37,6 +37,15 @@ class UnsafeBufferUsageHandler {
   /// Invoked when a fix is suggested against a variable.
   virtual void handleFixableVariable(const VarDecl *Variable,
  FixItList &&List) = 0;
+
+  /// Returns the text indicating that the user needs to provide input there:
+  virtual std::string
+  getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
+std::string s = std::string("<# ");
+s += HintTextToUser;
+s += " #>";
+return s;
+  }
 };
 
 // This function invokes the analysis and allows the caller to react to it

diff  --git 
a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 78889da32b3b4..75657d8d9a584 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -30,6 +30,7 @@ WARNING_GADGET(Decrement)
 WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
+FIXABLE_GADGET(ULCArraySubscript)
 
 #undef FIXABLE_GADGET
 #undef WARNING_GADGET

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bc70dbf4a624d..9b9ec23a2f21f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11790,6 +11790,8 @@ def warn_unsafe_buffer_operation : Warning<
   InGroup, DefaultIgnore;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
+def note_unsafe_buffer_variable_fixit : Note<
+  "change type of '%0' to '%select{std::span|std::array|std::span::iterator}1' 
to preserve bounds information">;
 def err_loongarch_builtin_requires_la32 : Error<
   "this builtin requires target: loongarch32">;
 } // end of sema component.

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 331479a80f6cc..c4f0a13a5d51e 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
 #include "llvm/ADT/SmallVector.h"
 #include 
 #include 
@@ -115,6 +116,19 @@ AST_MATCHER_P(Stmt, forEveryDescendant, 
internal::Matcher, innerMatcher) {
   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, 
ASTMatchFinder::BK_All);  
   return Visitor.findMatch(DynTypedNode::create(Node));
 }
+
+AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher, innerMatcher) {
+  return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
+}
+
+// Returns a matcher that matches any expression 'e' such that `innerMatcher`
+// matches '

[clang] 692da62 - [-Wunsafe-buffer-usage] Filter out conflicting fix-its

2023-02-07 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-07T16:15:28-08:00
New Revision: 692da6245d719fcee9d55936a021d9f9e301c557

URL: 
https://github.com/llvm/llvm-project/commit/692da6245d719fcee9d55936a021d9f9e301c557
DIFF: 
https://github.com/llvm/llvm-project/commit/692da6245d719fcee9d55936a021d9f9e301c557.diff

LOG: [-Wunsafe-buffer-usage] Filter out conflicting fix-its

Two fix-its conflict if they have overlapping source ranges. We shall
not emit conflicting fix-its.  This patch checks conflicts in fix-its
generated for one variable (including variable declaration fix-its and
variable usage fix-its). If there is any, we do NOT emit any fix-it
for that variable.

Reviewed by: NoQ

Differential revision: https://reviews.llvm.org/D141338

Added: 
clang/unittests/Analysis/UnsafeBufferUsageTest.cpp

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/unittests/Analysis/CMakeLists.txt

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 04c9fdea444f8..3ac4def0429bf 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -52,6 +52,12 @@ class UnsafeBufferUsageHandler {
 // through the handler class.
 void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler);
 
+namespace internal {
+// Tests if any two `FixItHint`s in `FixIts` conflict.  Two `FixItHint`s
+// conflict if they have overlapping source ranges.
+bool anyConflict(const llvm::SmallVectorImpl &FixIts,
+ const SourceManager &SM);
+} // namespace internal
 } // end namespace clang
 
 #endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */

diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index c4f0a13a5d51e..af648235beef0 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -694,6 +694,37 @@ groupFixablesByVar(FixableGadgetList 
&&AllFixableOperations) {
   return FixablesForUnsafeVars;
 }
 
+bool clang::internal::anyConflict(
+const SmallVectorImpl &FixIts, const SourceManager &SM) {
+  // A simple interval overlap detection algorithm.  Sorts all ranges by their
+  // begin location then finds the first overlap in one pass.
+  std::vector All; // a copy of `FixIts`
+
+  for (const FixItHint &H : FixIts)
+All.push_back(&H);
+  std::sort(All.begin(), All.end(),
+[&SM](const FixItHint *H1, const FixItHint *H2) {
+  return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
+  H2->RemoveRange.getBegin());
+});
+
+  const FixItHint *CurrHint = nullptr;
+
+  for (const FixItHint *Hint : All) {
+if (!CurrHint ||
+SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
+ Hint->RemoveRange.getBegin())) {
+  // Either to initialize `CurrHint` or `CurrHint` does not
+  // overlap with `Hint`:
+  CurrHint = Hint;
+} else
+  // In case `Hint` overlaps the `CurrHint`, we found at least one
+  // conflict:
+  return true;
+  }
+  return false;
+}
+
 std::optional
 ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
   if (const auto *DRE = 
dyn_cast(Node->getBase()->IgnoreImpCasts()))
@@ -948,8 +979,12 @@ getFixIts(FixableGadgetSets &FixablesForUnsafeVars, const 
Strategy &S,
 else
   FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
FixItsForVD.begin(), FixItsForVD.end());
-// Fix-it shall not overlap with macros or/and templates:
-if (overlapWithMacro(FixItsForVariable[VD])) {
+// We conservatively discard fix-its of a variable if
+// a fix-it overlaps with macros; or
+// a fix-it conflicts with another one
+if (overlapWithMacro(FixItsForVariable[VD]) ||
+clang::internal::anyConflict(FixItsForVariable[VD],
+ Ctx.getSourceManager())) {  
   FixItsForVariable.erase(VD);
 }
   }

diff  --git a/clang/unittests/Analysis/CMakeLists.txt 
b/clang/unittests/Analysis/CMakeLists.txt
index 619f2fc8b8581..d418a2b0f9c34 100644
--- a/clang/unittests/Analysis/CMakeLists.txt
+++ b/clang/unittests/Analysis/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_unittest(ClangAnalysisTests
   CloneDetectionTest.cpp
   ExprMutationAnalyzerTest.cpp
   MacroExpansionContextTest.cpp
+  UnsafeBufferUsageTest.cpp
   )
 
 clang_target_link_libraries(ClangAnalysisTests

diff  --git a/clang/unittests/Analysis/UnsafeBufferUsageTest.cpp 
b/clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
new file mode 100644
index 0..66af5750ef242
--- /dev/null
+++ b/clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
@@ -0,0 +1,60 @@

[clang] aef05b5 - [-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

2023-02-07 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-07T16:54:39-08:00
New Revision: aef05b5dc5c566bcaa15b66c989ccb8d2841ac71

URL: 
https://github.com/llvm/llvm-project/commit/aef05b5dc5c566bcaa15b66c989ccb8d2841ac71
DIFF: 
https://github.com/llvm/llvm-project/commit/aef05b5dc5c566bcaa15b66c989ccb8d2841ac71.diff

LOG: [-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

Add a pair of clang pragmas:
- `#pragma clang unsafe_buffer_usage begin` and
- `#pragma clang unsafe_buffer_usage end`,
which specify the start and end of an (unsafe buffer checking) opt-out
region, respectively.

Behaviors of opt-out regions conform to the following rules:

- No nested nor overlapped opt-out regions are allowed. One cannot
  start an opt-out region with `... unsafe_buffer_usage begin` but never
  close it with `... unsafe_buffer_usage end`. Mis-use of the pragmas
  will be warned.
- Warnings raised from unsafe buffer operations inside such an opt-out
  region will always be suppressed. This behavior CANNOT be changed by
  `clang diagnostic` pragmas or command-line flags.
- Warnings raised from unsafe operations outside of such opt-out
  regions may be reported on declarations inside opt-out
  regions. These warnings are NOT suppressed.
- An un-suppressed unsafe operation warning may be attached with
  notes. These notes are NOT suppressed as well regardless of whether
  they are in opt-out regions.

The implementation maintains a separate sequence of location pairs
representing opt-out regions in `Preprocessor`.  The `UnsafeBufferUsage`
analyzer reads the region sequence to check if an unsafe operation is
in an opt-out region. If it is, discard the warning raised from the
operation immediately.

Reviewed by: NoQ

Differential revision: https://reviews.llvm.org/D140179

Added: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Basic/DiagnosticLexKinds.td
clang/include/clang/Lex/Preprocessor.h
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Lex/Pragma.cpp
clang/lib/Lex/Preprocessor.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 3ac4def0429bf..d7d37cedfa98e 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -46,6 +46,9 @@ class UnsafeBufferUsageHandler {
 s += " #>";
 return s;
   }
+
+  /// Returns a reference to the `Preprocessor`:
+  virtual const Preprocessor & getPP() const;
 };
 
 // This function invokes the analysis and allows the caller to react to it

diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 0fb8d196dd6a5..fbb08a6f761be 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -945,4 +945,15 @@ def err_dep_source_scanner_unexpected_tokens_at_import : 
Error<
 
 }
 
+def err_pp_double_begin_pragma_unsafe_buffer_usage :
+Error<"already inside '#pragma unsafe_buffer_usage'">;
+
+def err_pp_unmatched_end_begin_pragma_unsafe_buffer_usage :
+Error<"not currently inside '#pragma unsafe_buffer_usage'">;
+
+def err_pp_unclosed_pragma_unsafe_buffer_usage :
+Error<"'#pragma unsafe_buffer_usage' was not ended">;
+
+def err_pp_pragma_unsafe_buffer_usage_syntax :
+Error<"Expected 'begin' or 'end'">;
 }

diff  --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index f383a2e5b5301..81deef21ca684 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2688,6 +2688,51 @@ class Preprocessor {
   void emitMacroDeprecationWarning(const Token &Identifier) const;
   void emitRestrictExpansionWarning(const Token &Identifier) const;
   void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const;
+
+  /// This boolean state keeps track if the current scanned token (by this PP)
+  /// is in an "-Wunsafe-buffer-usage" opt-out region. Assuming PP scans a
+  /// translation unit in a linear order.
+  bool InSafeBufferOptOutRegion = 0;
+
+  /// Hold the start location of the current "-Wunsafe-buffer-usage" opt-out
+  /// region if PP is currently in such a region.  Hold undefined value
+  /// otherwise.
+  SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the 
start location of an never-closed region.
+
+  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one
+  // translation unit. Each region is represented by

[clang] 9aa00c8 - Revert "[-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas"

2023-02-07 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-07T17:06:20-08:00
New Revision: 9aa00c8a306561c4e3ddb09058e66bae322a0769

URL: 
https://github.com/llvm/llvm-project/commit/9aa00c8a306561c4e3ddb09058e66bae322a0769
DIFF: 
https://github.com/llvm/llvm-project/commit/9aa00c8a306561c4e3ddb09058e66bae322a0769.diff

LOG: Revert "[-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas"

This reverts commit aef05b5dc5c566bcaa15b66c989ccb8d2841ac71.
It causes a buildbot failure: 
https://lab.llvm.org/buildbot/#/builders/216/builds/16879/steps/6/logs/stdio

Added: 


Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Basic/DiagnosticLexKinds.td
clang/include/clang/Lex/Preprocessor.h
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Lex/Pragma.cpp
clang/lib/Lex/Preprocessor.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp

Removed: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h



diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index d7d37cedfa98e..3ac4def0429bf 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -46,9 +46,6 @@ class UnsafeBufferUsageHandler {
 s += " #>";
 return s;
   }
-
-  /// Returns a reference to the `Preprocessor`:
-  virtual const Preprocessor & getPP() const;
 };
 
 // This function invokes the analysis and allows the caller to react to it

diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index fbb08a6f761be..0fb8d196dd6a5 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -945,15 +945,4 @@ def err_dep_source_scanner_unexpected_tokens_at_import : 
Error<
 
 }
 
-def err_pp_double_begin_pragma_unsafe_buffer_usage :
-Error<"already inside '#pragma unsafe_buffer_usage'">;
-
-def err_pp_unmatched_end_begin_pragma_unsafe_buffer_usage :
-Error<"not currently inside '#pragma unsafe_buffer_usage'">;
-
-def err_pp_unclosed_pragma_unsafe_buffer_usage :
-Error<"'#pragma unsafe_buffer_usage' was not ended">;
-
-def err_pp_pragma_unsafe_buffer_usage_syntax :
-Error<"Expected 'begin' or 'end'">;
 }

diff  --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index 81deef21ca684..f383a2e5b5301 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2688,51 +2688,6 @@ class Preprocessor {
   void emitMacroDeprecationWarning(const Token &Identifier) const;
   void emitRestrictExpansionWarning(const Token &Identifier) const;
   void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const;
-
-  /// This boolean state keeps track if the current scanned token (by this PP)
-  /// is in an "-Wunsafe-buffer-usage" opt-out region. Assuming PP scans a
-  /// translation unit in a linear order.
-  bool InSafeBufferOptOutRegion = 0;
-
-  /// Hold the start location of the current "-Wunsafe-buffer-usage" opt-out
-  /// region if PP is currently in such a region.  Hold undefined value
-  /// otherwise.
-  SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the 
start location of an never-closed region.
-
-  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one
-  // translation unit. Each region is represented by a pair of start and end
-  // locations.  A region is "open" if its' start and end locations are
-  // identical.
-  SmallVector, 8> 
SafeBufferOptOutMap;
-
-public:
-  /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out
-  /// region.  This `Loc` must be a source location that has been 
pre-processed.
-  bool isSafeBufferOptOut(const SourceManager&SourceMgr, const SourceLocation 
&Loc) const;
-
-  /// Alter the state of whether this PP currently is in a
-  /// "-Wunsafe-buffer-usage" opt-out region.
-  ///
-  /// \param isEnter: true if this PP is entering a region; otherwise, this PP
-  /// is exiting a region
-  /// \param Loc: the location of the entry or exit of a
-  /// region
-  /// \return true iff it is INVALID to enter or exit a region, i.e.,
-  /// attempt to enter a region before exiting a previous region, or exiting a
-  /// region that PP is not currently in.
-  bool enterOrExitSafeBufferOptOutRegion(bool isEnter,
- const SourceLocation &Loc);
-
-  /// \return true iff this PP is currently in a "-Wunsafe-buffer-usage"
-  ///  opt-out region
-  bool isPPInSafeBufferOptOutRegion();
-
-  /// \param StartLoc: output argument. It

[clang] 829bcb0 - [-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

2023-02-08 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-08T14:12:03-08:00
New Revision: 829bcb06ec43ab4b56b95ff040ec9d36feeaf06a

URL: 
https://github.com/llvm/llvm-project/commit/829bcb06ec43ab4b56b95ff040ec9d36feeaf06a
DIFF: 
https://github.com/llvm/llvm-project/commit/829bcb06ec43ab4b56b95ff040ec9d36feeaf06a.diff

LOG: [-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

Add a pair of clang pragmas:
- `#pragma clang unsafe_buffer_usage begin` and
- `#pragma clang unsafe_buffer_usage end`,
which specify the start and end of an (unsafe buffer checking) opt-out
region, respectively.

Behaviors of opt-out regions conform to the following rules:

- No nested nor overlapped opt-out regions are allowed. One cannot
  start an opt-out region with `... unsafe_buffer_usage begin` but never
  close it with `... unsafe_buffer_usage end`. Mis-use of the pragmas
  will be warned.
- Warnings raised from unsafe buffer operations inside such an opt-out
  region will always be suppressed. This behavior CANNOT be changed by
  `clang diagnostic` pragmas or command-line flags.
- Warnings raised from unsafe operations outside of such opt-out
  regions may be reported on declarations inside opt-out
  regions. These warnings are NOT suppressed.
- An un-suppressed unsafe operation warning may be attached with
  notes. These notes are NOT suppressed as well regardless of whether
  they are in opt-out regions.

The implementation maintains a separate sequence of location pairs
representing opt-out regions in `Preprocessor`.  The `UnsafeBufferUsage`
analyzer reads the region sequence to check if an unsafe operation is
in an opt-out region. If it is, discard the warning raised from the
operation immediately.

This is a re-land after I reverting it at 
9aa00c8a306561c4e3ddb09058e66bae322a0769.
The compilation error should be resolved.

Reviewed by: NoQ

Differential revision: https://reviews.llvm.org/D140179

Added: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h

Modified: 
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Basic/DiagnosticLexKinds.td
clang/include/clang/Lex/Preprocessor.h
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Lex/Pragma.cpp
clang/lib/Lex/Preprocessor.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp

Removed: 




diff  --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 3ac4def0429bf..926a4fce3ae74 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -38,6 +38,9 @@ class UnsafeBufferUsageHandler {
   virtual void handleFixableVariable(const VarDecl *Variable,
  FixItList &&List) = 0;
 
+  /// Returns a reference to the `Preprocessor`:
+  virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
+
   /// Returns the text indicating that the user needs to provide input there:
   virtual std::string
   getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {

diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 0fb8d196dd6a5..fbb08a6f761be 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -945,4 +945,15 @@ def err_dep_source_scanner_unexpected_tokens_at_import : 
Error<
 
 }
 
+def err_pp_double_begin_pragma_unsafe_buffer_usage :
+Error<"already inside '#pragma unsafe_buffer_usage'">;
+
+def err_pp_unmatched_end_begin_pragma_unsafe_buffer_usage :
+Error<"not currently inside '#pragma unsafe_buffer_usage'">;
+
+def err_pp_unclosed_pragma_unsafe_buffer_usage :
+Error<"'#pragma unsafe_buffer_usage' was not ended">;
+
+def err_pp_pragma_unsafe_buffer_usage_syntax :
+Error<"Expected 'begin' or 'end'">;
 }

diff  --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index f383a2e5b5301..81deef21ca684 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2688,6 +2688,51 @@ class Preprocessor {
   void emitMacroDeprecationWarning(const Token &Identifier) const;
   void emitRestrictExpansionWarning(const Token &Identifier) const;
   void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const;
+
+  /// This boolean state keeps track if the current scanned token (by this PP)
+  /// is in an "-Wunsafe-buffer-usage" opt-out region. Assuming PP scans a
+  /// translation unit in a linear order.
+  bool InSafeBufferOptOutRegion = 0;
+
+  /// Hold the start location of the current "-Wunsafe-buffer-usage" opt-out
+  /// r

[clang] 03cc52d - [-Wunsafe-buffer-usage] To disable a test on Windows systems.

2023-02-08 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-08T17:38:19-08:00
New Revision: 03cc52dfd1dbb4a59b479da55e87838fb93d2067

URL: 
https://github.com/llvm/llvm-project/commit/03cc52dfd1dbb4a59b479da55e87838fb93d2067
DIFF: 
https://github.com/llvm/llvm-project/commit/03cc52dfd1dbb4a59b479da55e87838fb93d2067.diff

LOG: [-Wunsafe-buffer-usage] To disable a test on Windows systems.

One of the tests in the commit
'bdf4f2bea50e87f5b9273e3bbc9a7753bca3a6bb' sometimes fails on one of
the buildbots runing on a Windows machine. For example,
"https://lab.llvm.org/buildbot/#/builders/60/builds/10615"; is a failed
build.

Now we disable it until we figure out why this could happen.

Added: 


Modified: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Removed: 




diff  --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
index 4f0e0fa31a431..3cb4f57a6efd6 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -1,3 +1,4 @@
+// REQUIRES: !system-windows
 // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
 typedef int * Int_ptr_t;
 typedef int Int_t;
@@ -97,10 +98,10 @@ void decl_without_init() {
   int tmp;
   int * p;
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span 
p"
-  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:{{^3}}
   Int_ptr_t q;
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span 
q"
-  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:{{^3}}
   tmp = p[5];
   tmp = q[5];
 }



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


[clang] f78c343 - [-Wunsafe-buffer-usage] Create Fix-Its only if they are emitted

2023-02-23 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-23T14:47:43-08:00
New Revision: f78c34346635e25919e2777b1b1cbb9627d5ad43

URL: 
https://github.com/llvm/llvm-project/commit/f78c34346635e25919e2777b1b1cbb9627d5ad43
DIFF: 
https://github.com/llvm/llvm-project/commit/f78c34346635e25919e2777b1b1cbb9627d5ad43.diff

LOG: [-Wunsafe-buffer-usage] Create Fix-Its only if they are emitted

`-Wunsafe-buffer-usage` diagnostics shall not emit fix-its if fix-its
are globally disabled.

Commit on behalf of jkorous (Jan Korous)

Reviewed by: NoQ (Artem Dergachev)

Differential revision: https://reviews.llvm.org/D143697

Added: 


Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 61c2c4e4b52ad..bfae5a6ea3351 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1017,8 +1017,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
   DeclUseTracker Tracker;
 
   {
-// FIXME: We could skip even matching Fixables' matchers if EmitFixits ==
-// false.
 auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D, 
Handler);
 UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
 FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets));

diff  --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 85dc3a7eb9507..07e17f9f71072 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2518,7 +2518,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
 UnsafeBufferUsageReporter R(S);
 checkUnsafeBufferUsage(
 D, R,
-/*EmitFixits=*/S.getLangOpts().CPlusPlus20);
+/*EmitFixits=*/S.getDiagnostics().getDiagnosticOptions().ShowFixits &&
+S.getLangOpts().CPlusPlus20);
   }
 
   // If none of the previous checks caused a CFG build, trigger one here



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


[clang] cd26529 - [-Wunsafe-buffer-usage] Fixits for assignments to array subscript expressions

2023-02-23 Thread Ziqing Luo via cfe-commits

Author: Ziqing Luo
Date: 2023-02-23T15:02:46-08:00
New Revision: cd2652963b6b97c01329419f15c336e5560afa98

URL: 
https://github.com/llvm/llvm-project/commit/cd2652963b6b97c01329419f15c336e5560afa98
DIFF: 
https://github.com/llvm/llvm-project/commit/cd2652963b6b97c01329419f15c336e5560afa98.diff

LOG: [-Wunsafe-buffer-usage] Fixits for assignments to array subscript 
expressions

Let generate fix-its to make assignments' left-hand side of the form
`dre[e]` safe if `e` is known to be non-negative.

Commit on behalf of jkorous (Jan Korous)

Reviewed by: NoQ (Artem Dergachev)

Differential revision: https://reviews.llvm.org/D142794

Added: 

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp

Modified: 
clang/lib/Analysis/UnsafeBufferUsage.cpp

Removed: 




diff  --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index bfae5a6ea3351..d9602a41af787 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -129,11 +129,19 @@ AST_MATCHER_P(CastExpr, castSubExpr, 
internal::Matcher, innerMatcher) {
 
 // Returns a matcher that matches any expression 'e' such that `innerMatcher`
 // matches 'e' and 'e' is in an Unspecified Lvalue Context.
-static internal::Matcher
-isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) {
-  return implicitCastExpr(hasCastKind(CastKind::CK_LValueToRValue),
-  castSubExpr(innerMatcher));
-  // FIXME: add assignmentTo context...
+static auto isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) 
{
+// clang-format off
+  return
+expr(anyOf(
+  implicitCastExpr(
+hasCastKind(CastKind::CK_LValueToRValue),
+castSubExpr(innerMatcher)),
+  binaryOperator(
+hasAnyOperatorName("="),
+hasLHS(innerMatcher)
+  )
+));
+// clang-format off
 }
 } // namespace clang::ast_matchers
 

diff  --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp
 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp
new file mode 100644
index 0..ba3b5bc22d543
--- /dev/null
+++ 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// TODO cases where we don't want fixits
+
+// The Fix-It for unsafe operation is trivially empty.
+// In order to test that our machinery recognizes that we can test if the 
variable declaration gets a Fix-It.
+// If the operation wasn't handled propertly the declaration won't get Fix-It.
+// By testing presence of the declaration Fix-It we indirectly test presence 
of the trivial Fix-It for its operations.
+void test() {
+  int *p = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+  p[5] = 1;
+  // CHECK-NOT: fix-it:
+}



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


[clang] [-Wunsafe-buffer-usage] Minimize fixit range for pointer variables (PR #81935)

2024-02-16 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 edited 
https://github.com/llvm/llvm-project/pull/81935
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Minimize fixit range for pointer variables (PR #81935)

2024-02-16 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 approved this pull request.

LGTM!

https://github.com/llvm/llvm-project/pull/81935
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Minimize fixit range for pointer variables (PR #81935)

2024-02-16 Thread Ziqing Luo via cfe-commits


@@ -2326,15 +2312,21 @@ static FixItList fixLocalVarDeclWithSpan(const VarDecl 
*D, ASTContext &Ctx,
   return {};
 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
   std::make_move_iterator(InitFixIts->end()));
-// If the declaration has the form `T *ident = init`, we want to replace
-// `T *ident = ` with `std::span ident`:
-EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1);
   }
-  SS << " " << IdentText->str();
+  // For declaration of the form `T * ident = init;`, we want to replace
+  // `T * ` with `std::span`.
+  // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
+  // just `T *` with `std::span`.
+  const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();

ziqingluo-90 wrote:

This looks a correct optimization to me!  I probably didn't realize the 
existence of `getTypeSpecEndLoc()` when I did this.

https://github.com/llvm/llvm-project/pull/81935
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Minimize fixit range for pointer variables (PR #81935)

2024-02-16 Thread Ziqing Luo via cfe-commits


@@ -2326,15 +2312,21 @@ static FixItList fixLocalVarDeclWithSpan(const VarDecl 
*D, ASTContext &Ctx,
   return {};
 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
   std::make_move_iterator(InitFixIts->end()));
-// If the declaration has the form `T *ident = init`, we want to replace
-// `T *ident = ` with `std::span ident`:
-EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1);
   }
-  SS << " " << IdentText->str();
+  // For declaration of the form `T * ident = init;`, we want to replace
+  // `T * ` with `std::span`.
+  // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
+  // just `T *` with `std::span`.
+  const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
   if (!EndLocForReplacement.isValid()) {
 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
 return {};
   }
+  // The only exception is that for `T *ident` we'll add a single space between
+  // "std::span" and "ident".
+  if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))

ziqingluo-90 wrote:

In case the identifier is a macro expansion, e.g., 
```
#define IDENT ident
T *IDENT;
```
, the if-condition will evaluate to false because the files of the two source 
locations are different.  
But this is rare and the fix-it is still correct.

https://github.com/llvm/llvm-project/pull/81935
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-09-04 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-09-04 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

> There needs to be a flag to opt out of this to not break everybody who is 
> currently using unsafe-buffer-usage. #105383 seems to do that, but it really 
> should be in this same PR. Can this be reverted and relanded with the flag?

ok, will do

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-09-05 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

> There needs to be a flag to opt out of this to not break everybody who is 
> currently using unsafe-buffer-usage. #105383 seems to do that, but it really 
> should be in this same PR. Can this be reverted and relanded with the flag?

@aeubanks  you should be able to suppress the new warning with 
`-Wno-unsafe-buffer-usage-in-libc-call`

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-09-06 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

> > We're seeing a crash with this patch when compiling with -Weverything.
> > ```
> > clang: ../../clang/include/clang/AST/Expr.h:3026: const clang::Expr 
> > *clang::CallExpr::getArg(unsigned int) const: Assertion `Arg < getNumArgs() 
> > && "Arg access out of range!"' failed.
> > ```
> > 
> > 
> > 
> >   
> > 
> > 
> >   
> > 
> > 
> > 
> >   
> > I'm working on extracting a reproducer.
> 
> `clang -c -Weverything bbi-98867.c` with bbi-98867.c being just
> 
> ```
> void printf() { printf(); }
> ```
> 
> (I've creduced the reproducer, I can't share the full one)

Thanks for finding the bug and making a reproducer.  It has been fixed in 
de88d7db7b77141297fbb5638ee1e18d1fba53b8.

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-09-06 Thread Ziqing Luo via cfe-commits

ziqingluo-90 wrote:

> Btw a question about the new warning: So with 
> -Wunsafe-buffer-usage-in-libc-call clang now warns on the following?
> 
> ```
> #include 
> 
> void foo(void) {
>   char q[10];
>   snprintf(q, 10, "%s", "hello");
> }
> ```
> 
> It says
> 
> ```
> foo.c:5:3: warning: function 'snprintf' is unsafe 
> [-Wunsafe-buffer-usage-in-libc-call]
> 5 |   snprintf(q, 10, "%s", "hello");
>   |   ^~
> foo.c:5:12: note:  buffer pointer and size may not match
> 5 |   snprintf(q, 10, "%s", "hello");
>   |^
> 1 warning generated.
> ```
> 
> Is that as expected? If so, how should snprintf be used to avoid the warning?

Yes, this is expected.  According to the C++ Safe Buffers programming model, 
buffer pointers should be changed to `std::span`.   Then  
`snprintf(span.data(), span.size(), ...)` is considered safe and will not be 
warned.  We may also allow the use of the form `snprintf(span.first(10).data(), 
10, ...)` later.

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Safe Buffers] Fix a small bug recently found (PR #102953)

2024-08-12 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 created 
https://github.com/llvm/llvm-project/pull/102953

`QualType::isConstantArrayType()` checks canonical type. So a following cast 
should be applied to canonical type as well:

```
if (Ty->isConstantArrayType())
  cast(Ty.getCanonicalType());  //  
cast(Ty) is incorrect
```

>From a15f22e9577154783165bdfc1021e640bbc4dcd0 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 12 Aug 2024 11:57:17 -0700
Subject: [PATCH] [-Wunsafe-buffer-usage] Fix a bug in the ASTMatcher for span
 constructors

`QualType::isConstantArrayType()` checks canonical type. So a
following cast should be applied to canonical type as well:

```
if (Ty->isConstantArrayType())
  cast(Ty.getCanonicalType());  //  
cast(Ty) is incorrect
```
---
 clang/lib/Analysis/UnsafeBufferUsage.cpp   | 2 +-
 .../warn-unsafe-buffer-usage-in-container-span-construct.cpp   | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 866222380974b..379b3ea7adf9e 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -404,7 +404,7 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
 
   if (Arg0Ty->isConstantArrayType()) {
 const APSInt ConstArrSize =
-APSInt(cast(Arg0Ty)->getSize());
+APSInt(cast(Arg0Ty.getCanonicalType())->getSize());
 
 // Check form 4:
 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
diff --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp
index a1ddc384e0d9b..f4f2a028f0b8f 100644
--- 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp
+++ 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp
@@ -79,6 +79,8 @@ namespace construct_wt_ptr_size {
 unsigned Y = 10;
 std::span S = std::span{&X, 1}; // no-warning
 int Arr[10];
+typedef int TenInts_t[10];
+TenInts_t Arr2;
 
 S = std::span{&X, 2};// expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}
 S = std::span{new int[10], 10};  // no-warning
@@ -90,6 +92,7 @@ namespace construct_wt_ptr_size {
 S = std::span{new int[10], 9};   // expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}  // not smart enough to tell 
its safe
 S = std::span{new int[10], Y};   // expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}  // not smart enough to tell 
its safe
 S = std::span{Arr, 10};  // no-warning
+S = std::span{Arr2, 10}; // no-warning
 S = std::span{Arr, Y};   // expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}  // not smart enough to tell 
its safe
 S = std::span{p, 0}; // no-warning
   }

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


[clang] Warning Libc functions (PR #101583)

2024-08-12 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/101583

>From cce5781733a7c294f10dc75f48372ff6ee331239 Mon Sep 17 00:00:00 2001
From: Ziqing Luo 
Date: Thu, 1 Aug 2024 16:36:27 -0700
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add warn on unsafe calls to libc
 functions

Warning about calls to libc functions involving buffer access.  Warned
functions are hardcoded by names.

(rdar://117182250)
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |  12 +
 .../Analyses/UnsafeBufferUsageGadgets.def |   1 +
 .../clang/Basic/DiagnosticSemaKinds.td|   7 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  | 408 +-
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  12 +
 ...-usage-libc-functions-inline-namespace.cpp |  60 +++
 ...arn-unsafe-buffer-usage-libc-functions.cpp |  86 
 ...n-unsafe-buffer-usage-test-unreachable.cpp |   4 +-
 8 files changed, 586 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 228b4ae1e3e115..3e0fae6db7562d 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
@@ -106,6 +107,17 @@ class UnsafeBufferUsageHandler {
   virtual void handleUnsafeOperation(const Stmt *Operation,
  bool IsRelatedToDecl, ASTContext &Ctx) = 
0;
 
+  /// Invoked when a call to an unsafe libc function is found.
+  /// \param PrintfInfo
+  ///  is 0 if the callee function is not a member of the printf family;
+  ///  is 1 if the callee is `sprintf`;
+  ///  is 2 if arguments of the call have `__size_by` relation but are not in a
+  ///  safe pattern;
+  ///  is 3 if string arguments do not guarantee null-termination
+  ///  is 4 if the callee takes va_list
+  virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
+ASTContext &Ctx) = 0;
+
   /// Invoked when an unsafe operation with a std container is found.
   virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
 bool IsRelatedToDecl,
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 242ad763ba62b9..ac01b285ae833b 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -38,6 +38,7 @@ WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(UnsafeBufferUsageCtorAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_GADGET(UnsafeLibcFunctionCall)
 WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 554dbaff2ce0d8..7e1e3686ce6554 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12383,6 +12383,13 @@ def warn_unsafe_buffer_operation : Warning<
   "%select{unsafe pointer operation|unsafe pointer arithmetic|"
   "unsafe buffer access|function introduces unsafe buffer manipulation|unsafe 
invocation of span::data}0">,
   InGroup, DefaultIgnore;
+def warn_unsafe_buffer_libc_call : Warning<
+  "function %0 introduces unsafe buffer access">,
+  InGroup, DefaultIgnore;
+def note_unsafe_buffer_printf_call : Note<
+  "%select{| change to 'snprintf' for explicit bounds checking | buffer 
pointer and size may not match"
+  "| use 'std::string::c_str' or string literal as string pointer to 
guarantee null-termination"
+  "| do not use va_list that cannot be checked at compile-time for 
bounds safety}0">;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
 def note_unsafe_buffer_variable_fixit_group : Note<
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 866222380974b6..751fb75f6ed602 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,20 +9,24 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclC

[clang] Warning Libc functions (PR #101583)

2024-08-12 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/101583

>From cce5781733a7c294f10dc75f48372ff6ee331239 Mon Sep 17 00:00:00 2001
From: Ziqing Luo 
Date: Thu, 1 Aug 2024 16:36:27 -0700
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Add warn on unsafe calls to libc
 functions

Warning about calls to libc functions involving buffer access.  Warned
functions are hardcoded by names.

(rdar://117182250)
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |  12 +
 .../Analyses/UnsafeBufferUsageGadgets.def |   1 +
 .../clang/Basic/DiagnosticSemaKinds.td|   7 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  | 408 +-
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  12 +
 ...-usage-libc-functions-inline-namespace.cpp |  60 +++
 ...arn-unsafe-buffer-usage-libc-functions.cpp |  86 
 ...n-unsafe-buffer-usage-test-unreachable.cpp |   4 +-
 8 files changed, 586 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 228b4ae1e3e115..3e0fae6db7562d 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
@@ -106,6 +107,17 @@ class UnsafeBufferUsageHandler {
   virtual void handleUnsafeOperation(const Stmt *Operation,
  bool IsRelatedToDecl, ASTContext &Ctx) = 
0;
 
+  /// Invoked when a call to an unsafe libc function is found.
+  /// \param PrintfInfo
+  ///  is 0 if the callee function is not a member of the printf family;
+  ///  is 1 if the callee is `sprintf`;
+  ///  is 2 if arguments of the call have `__size_by` relation but are not in a
+  ///  safe pattern;
+  ///  is 3 if string arguments do not guarantee null-termination
+  ///  is 4 if the callee takes va_list
+  virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
+ASTContext &Ctx) = 0;
+
   /// Invoked when an unsafe operation with a std container is found.
   virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
 bool IsRelatedToDecl,
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 242ad763ba62b9..ac01b285ae833b 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -38,6 +38,7 @@ WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(UnsafeBufferUsageCtorAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_GADGET(UnsafeLibcFunctionCall)
 WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 554dbaff2ce0d8..7e1e3686ce6554 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12383,6 +12383,13 @@ def warn_unsafe_buffer_operation : Warning<
   "%select{unsafe pointer operation|unsafe pointer arithmetic|"
   "unsafe buffer access|function introduces unsafe buffer manipulation|unsafe 
invocation of span::data}0">,
   InGroup, DefaultIgnore;
+def warn_unsafe_buffer_libc_call : Warning<
+  "function %0 introduces unsafe buffer access">,
+  InGroup, DefaultIgnore;
+def note_unsafe_buffer_printf_call : Note<
+  "%select{| change to 'snprintf' for explicit bounds checking | buffer 
pointer and size may not match"
+  "| use 'std::string::c_str' or string literal as string pointer to 
guarantee null-termination"
+  "| do not use va_list that cannot be checked at compile-time for 
bounds safety}0">;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
 def note_unsafe_buffer_variable_fixit_group : Note<
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 866222380974b6..751fb75f6ed602 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,20 +9,24 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclC

[clang] Warning Libc functions (PR #101583)

2024-08-12 Thread Ziqing Luo via cfe-commits


@@ -443,6 +447,314 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+AST_MATCHER(CallExpr, isUnsafeLibcFunctionCall) {
+  static const std::set PredefinedNames{
+  // numeric conversion:
+  "atof",
+  "atoi",
+  "atol",
+  "atoll",
+  "strtol",
+  "strtoll",
+  "strtoul",
+  "strtoull",
+  "strtof",
+  "strtod",
+  "strtold",
+  "strtoimax",
+  "strtoumax",
+  // "strfromf",  "strfromd", "strfroml", // C23?
+  // string manipulation:
+  "strcpy",
+  "strncpy",
+  "strlcpy",
+  "strcat",
+  "strncat",
+  "strlcat",
+  "strxfrm",
+  "strdup",
+  "strndup",
+  // string examination:
+  "strlen",
+  "strnlen",
+  "strcmp",
+  "strncmp",
+  "stricmp",
+  "strcasecmp",
+  "strcoll",
+  "strchr",
+  "strrchr",
+  "strspn",
+  "strcspn",
+  "strpbrk",
+  "strstr",
+  "strtok",
+  // "mem-" functions
+  "memchr",
+  "wmemchr",
+  "memcmp",
+  "wmemcmp",
+  "memcpy",
+  "memccpy",
+  "mempcpy",
+  "wmemcpy",
+  "memmove",
+  "wmemmove",
+  "memset",
+  "wmemset",
+  // IO:
+  "fread",
+  "fwrite",
+  "fgets",
+  "fgetws",
+  "gets",
+  "fputs",
+  "fputws",
+  "puts",
+  // others
+  "strerror_s",
+  "strerror_r",
+  "bcopy",
+  "bzero",
+  "bsearch",
+  "qsort",
+  };
+
+  // A tiny name parser for unsafe libc function names with additional
+  // checks for `printf`s:
+  struct FuncNameMatch {
+const CallExpr *const Call;
+ASTContext &Ctx;
+enum ResultKind {
+  NO,   // no match
+  YES,  // matched a name that is not a member of the printf family
+  SPRINTF,  // matched `sprintf`
+  OTHER_PRINTF, // matched a printf function that is not `sprintf`
+};
+FuncNameMatch(const CallExpr *Call, ASTContext &Ctx)
+: Call(Call), Ctx(Ctx) {}
+
+// For a name `S` in `PredefinedNames` or a member of the printf/scanf
+// family, define matching function names with `S` by the grammar below:
+//
+//  CoreName := S | S["wcs"/"str"]
+//  LibcName := CoreName | CoreName + "_s"
+//  MatchingName := "__builtin_" + LibcName  |
+//  "__builtin___" + LibcName + "_chk"   |
+//  "__asan_" + LibcName
+//
+// (Note S["wcs"/"str"] means substitute "str" with "wcs" in S.)
+ResultKind matchName(StringRef FunName, bool isBuiltin) {
+  // Try to match __builtin_:
+  if (isBuiltin && FunName.starts_with("__builtin_"))
+// Then either it is __builtin_LibcName or __builtin___LibcName_chk or
+// no match:
+return matchLibcNameOrBuiltinChk(
+FunName.drop_front(10 /* truncate "__builtin_" */));
+  // Try to match __asan_:
+  if (FunName.starts_with("__asan_"))
+return matchLibcName(FunName.drop_front(7 /* truncate of "__asan_" 
*/));
+  return matchLibcName(FunName);
+}
+
+// Parameter `Name` is the substring after stripping off the prefix
+// "__builtin_".
+ResultKind matchLibcNameOrBuiltinChk(StringRef Name) {
+  if (Name.starts_with("__") && Name.ends_with("_chk"))
+return matchLibcName(
+Name.drop_front(2).drop_back(4) /* truncate "__" and "_chk" */);
+  return matchLibcName(Name);
+}
+
+ResultKind matchLibcName(StringRef Name) {
+  if (Name.ends_with("_s"))
+return matchCoreName(Name.drop_back(2 /* truncate "_s" */));
+  return matchCoreName(Name);
+}
+
+ResultKind matchCoreName(StringRef Name) {
+  if (PredefinedNames.find(Name.str()) != PredefinedNames.end())
+return !isSafeStrlen(Name) ? YES
+   : NO; // match unless it's a safe strlen 
call
+
+  std::string NameWCS = Name.str();
+  size_t WcsPos = NameWCS.find("wcs");
+
+  while (WcsPos != std::string::npos) {
+NameWCS[WcsPos++] = 's';
+NameWCS[WcsPos++] = 't';
+NameWCS[WcsPos++] = 'r';
+WcsPos = NameWCS.find("wcs", WcsPos);
+  }
+  if (PredefinedNames.find(NameWCS) != PredefinedNames.end())
+return !isSafeStrlen(NameWCS) ? YES : NO;
+  return matchPrintfOrScanfFamily(Name);
+}
+
+ResultKind matchPrintfOrScanfFamily(StringRef Name) {
+  if (Name.ends_with("scanf"))
+return YES; // simply warn about scanf functions
+  if (!Name.ends_with("printf"))
+return NO; // neither printf nor scanf
+  if (Name.starts_with("v"))
+// cannot check args for va_list, so `vprintf`s are treated as regular
+// unsafe libc calls:
+return YES;
+
+  // Truncate "printf", focus on prefixes.  There are different possible
+  // name prefixes: "k", "f", "s", "sn", "fw", ..., "snw".  We strip off 
the
+  // 'w' and handle printfs di

[clang] [Safe Buffers] Fix a small bug recently found (PR #102953)

2024-08-13 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/102953

>From d6c860de3facc37f27b17a26a01e48bc02b4659b Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 12 Aug 2024 11:57:17 -0700
Subject: [PATCH] [-Wunsafe-buffer-usage] Fix a bug in the ASTMatcher for span
 constructors

`QualType::isConstantArrayType()` checks canonical type. So a
following cast should be applied to canonical type as well. And, a
better option is to use `ASTContext::getAsArrayType()`.

```
if (Ty->isConstantArrayType())
  cast(Ty); // this is incorrect

if (auto *CAT = Context.getAsConstantArrayType(Ty))
  ...  // this is good

```
---
 clang/lib/Analysis/UnsafeBufferUsage.cpp   | 14 +++---
 ...fe-buffer-usage-in-container-span-construct.cpp |  3 +++
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 866222380974b6..db7ec5741f91c4 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -402,9 +402,9 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
 
   QualType Arg0Ty = Arg0->IgnoreImplicit()->getType();
 
-  if (Arg0Ty->isConstantArrayType()) {
-const APSInt ConstArrSize =
-APSInt(cast(Arg0Ty)->getSize());
+  if (auto *ConstArrTy =
+  Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) {
+const APSInt ConstArrSize = APSInt(ConstArrTy->getSize());
 
 // Check form 4:
 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
@@ -2659,7 +2659,7 @@ static FixItList fixVarDeclWithArray(const VarDecl *D, 
const ASTContext &Ctx,
 
   // Note: the code below expects the declaration to not use any type sugar 
like
   // typedef.
-  if (auto CAT = dyn_cast(D->getType())) {
+  if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
 const QualType &ArrayEltT = CAT->getElementType();
 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
 // FIXME: support multi-dimensional arrays
@@ -2792,9 +2792,9 @@ fixVariable(const VarDecl *VD, FixitStrategy::Kind K,
 return {};
   }
   case FixitStrategy::Kind::Array: {
-if (VD->isLocalVarDecl() &&
-isa(VD->getType().getCanonicalType()))
-  return fixVariableWithArray(VD, Tracker, Ctx, Handler);
+if (VD->isLocalVarDecl())
+  if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
+return fixVariableWithArray(VD, Tracker, Ctx, Handler);
 
 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
 return {};
diff --git 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp
index a1ddc384e0d9b8..e97511593bbd81 100644
--- 
a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp
+++ 
b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp
@@ -79,6 +79,8 @@ namespace construct_wt_ptr_size {
 unsigned Y = 10;
 std::span S = std::span{&X, 1}; // no-warning
 int Arr[10];
+typedef int TenInts_t[10];
+TenInts_t Arr2;
 
 S = std::span{&X, 2};// expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}
 S = std::span{new int[10], 10};  // no-warning
@@ -90,6 +92,7 @@ namespace construct_wt_ptr_size {
 S = std::span{new int[10], 9};   // expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}  // not smart enough to tell 
its safe
 S = std::span{new int[10], Y};   // expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}  // not smart enough to tell 
its safe
 S = std::span{Arr, 10};  // no-warning
+S = std::span{Arr2, 10}; // no-warning
 S = std::span{Arr, Y};   // expected-warning{{the 
two-parameter std::span construction is unsafe as it can introduce mismatch 
between buffer size and the bound information}}  // not smart enough to tell 
its safe
 S = std::span{p, 0}; // no-warning
   }

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


[clang] [Safe Buffers] Fix a small bug recently found (PR #102953)

2024-08-13 Thread Ziqing Luo via cfe-commits


@@ -404,7 +404,7 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
 
   if (Arg0Ty->isConstantArrayType()) {
 const APSInt ConstArrSize =
-APSInt(cast(Arg0Ty)->getSize());
+APSInt(cast(Arg0Ty.getCanonicalType())->getSize());

ziqingluo-90 wrote:

Have changed to use `ASTContext::getAsConstantArrayType()`

https://github.com/llvm/llvm-project/pull/102953
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Fix a small bug recently found (PR #102953)

2024-08-15 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/102953
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -443,6 +448,368 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+namespace libc_fun_disjoint_inner_matchers {
+// `libc_fun_disjoint_inner_matchers` covers a set of matchers that match
+// disjoint node sets.   They all take a `CoreName`, which is the substring of 
a
+// function name after `ignoreLibcPrefixAndSuffix`.  They are suppose to be 
used
+// as an inner matcher of `ignoreLibcPrefixAndSuffix` to deal with different
+// libc function calls.
+
+// Matches a function call node such that
+//  1. It's name, after stripping off predefined prefix and suffix, is
+// `CoreName`; and
+//  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
+// is a set of libc function names.
+//
+//  Note: For predefined prefix and suffix, see `ignoreLibcPrefixAndSuffix`.
+//  And, the notation `CoreName[str/wcs]` means a new name obtained from 
replace
+//  string "wcs" with "str" in `CoreName`.
+//
+//  Also note, the set of predefined function names does not include `printf`
+//  functions, they are checked exclusively with other matchers below.
+//  Maintaining the invariant that all matchers under
+//  `libc_fun_disjoint_inner_matchers` are disjoint.
+AST_MATCHER_P(CallExpr, predefinedUnsafeLibcFunCall, StringRef, CoreName) {
+  static const std::set PredefinedNames{
+  // numeric conversion:
+  "atof",
+  "atoi",
+  "atol",
+  "atoll",
+  "strtol",
+  "strtoll",
+  "strtoul",
+  "strtoull",
+  "strtof",
+  "strtod",
+  "strtold",
+  "strtoimax",
+  "strtoumax",
+  // "strfromf",  "strfromd", "strfroml", // C23?
+  // string manipulation:
+  "strcpy",
+  "strncpy",
+  "strlcpy",
+  "strcat",
+  "strncat",
+  "strlcat",
+  "strxfrm",
+  "strdup",
+  "strndup",
+  // string examination:
+  "strlen",
+  "strnlen",
+  "strcmp",
+  "strncmp",
+  "stricmp",
+  "strcasecmp",
+  "strcoll",
+  "strchr",
+  "strrchr",
+  "strspn",
+  "strcspn",
+  "strpbrk",
+  "strstr",
+  "strtok",
+  // "mem-" functions
+  "memchr",
+  "wmemchr",
+  "memcmp",
+  "wmemcmp",
+  "memcpy",
+  "memccpy",
+  "mempcpy",
+  "wmemcpy",
+  "memmove",
+  "wmemmove",
+  "memset",
+  "wmemset",
+  // IO:
+  "fread",
+  "fwrite",
+  "fgets",
+  "fgetws",
+  "gets",
+  "fputs",
+  "fputws",
+  "puts",
+  // others
+  "strerror_s",
+  "strerror_r",
+  "bcopy",
+  "bzero",
+  "bsearch",
+  "qsort",
+  };
+  // This is safe: strlen("hello").  We don't want to be noisy on this case.
+  auto isSafeStrlen = [&Node](StringRef Name) -> bool {
+return Name == "strlen" && Node.getNumArgs() == 1 &&
+   isa(Node.getArg(0)->IgnoreParenImpCasts());
+  };
+
+  // Match predefined names:
+  if (PredefinedNames.find(CoreName.str()) != PredefinedNames.end())
+return !isSafeStrlen(CoreName);
+
+  std::string NameWCS = CoreName.str();
+  size_t WcsPos = NameWCS.find("wcs");
+
+  while (WcsPos != std::string::npos) {
+NameWCS[WcsPos++] = 's';
+NameWCS[WcsPos++] = 't';
+NameWCS[WcsPos++] = 'r';
+WcsPos = NameWCS.find("wcs", WcsPos);
+  }
+  if (PredefinedNames.find(NameWCS) != PredefinedNames.end())
+return !isSafeStrlen(NameWCS);
+  // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. 
They
+  // all should end with "scanf"):
+  return CoreName.ends_with("scanf");
+}
+
+// Match a call to one of the `-printf` functions taking `va_list`.  We cannot
+// check safety for these functions so they should be changed to their
+// non-va_list versions.
+AST_MATCHER_P(CallExpr, unsafeVaListPrintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf"))
+return false; // neither printf nor scanf
+  return Name.starts_with("v");
+}
+
+// Matches a call to one of the `-sprintf` functions (excluding the ones with
+// va_list) as they are always unsafe and should be changed to corresponding
+// `-snprintf`s.
+AST_MATCHER_P(CallExpr, unsafeSprintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf") || Name.starts_with("v"))
+return false;
+
+  StringRef Prefix = Name.drop_back(6);
+
+  if (Prefix.ends_with("w"))
+Prefix = Prefix.drop_back(1);
+  return Prefix == "s";
+}
+
+// A pointer type expression is known to be null-terminated, if it has the
+// form: E.c_str(), for any expression E of `std::string` type.
+static bool isNullTermPointer(const Expr *Ptr) {
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
+const CXXMethodDecl *MD = MCE->getMethodDecl();
+const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
+
+if (MD && RD && RD->isInStdNamespace(

[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -443,6 +448,368 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+namespace libc_fun_disjoint_inner_matchers {
+// `libc_fun_disjoint_inner_matchers` covers a set of matchers that match
+// disjoint node sets.   They all take a `CoreName`, which is the substring of 
a
+// function name after `ignoreLibcPrefixAndSuffix`.  They are suppose to be 
used
+// as an inner matcher of `ignoreLibcPrefixAndSuffix` to deal with different
+// libc function calls.
+
+// Matches a function call node such that
+//  1. It's name, after stripping off predefined prefix and suffix, is
+// `CoreName`; and
+//  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
+// is a set of libc function names.
+//
+//  Note: For predefined prefix and suffix, see `ignoreLibcPrefixAndSuffix`.
+//  And, the notation `CoreName[str/wcs]` means a new name obtained from 
replace
+//  string "wcs" with "str" in `CoreName`.
+//
+//  Also note, the set of predefined function names does not include `printf`
+//  functions, they are checked exclusively with other matchers below.
+//  Maintaining the invariant that all matchers under
+//  `libc_fun_disjoint_inner_matchers` are disjoint.
+AST_MATCHER_P(CallExpr, predefinedUnsafeLibcFunCall, StringRef, CoreName) {
+  static const std::set PredefinedNames{
+  // numeric conversion:
+  "atof",
+  "atoi",
+  "atol",
+  "atoll",
+  "strtol",
+  "strtoll",
+  "strtoul",
+  "strtoull",
+  "strtof",
+  "strtod",
+  "strtold",
+  "strtoimax",
+  "strtoumax",
+  // "strfromf",  "strfromd", "strfroml", // C23?
+  // string manipulation:
+  "strcpy",
+  "strncpy",
+  "strlcpy",
+  "strcat",
+  "strncat",
+  "strlcat",
+  "strxfrm",
+  "strdup",
+  "strndup",
+  // string examination:
+  "strlen",
+  "strnlen",
+  "strcmp",
+  "strncmp",
+  "stricmp",
+  "strcasecmp",
+  "strcoll",
+  "strchr",
+  "strrchr",
+  "strspn",
+  "strcspn",
+  "strpbrk",
+  "strstr",
+  "strtok",
+  // "mem-" functions
+  "memchr",
+  "wmemchr",
+  "memcmp",
+  "wmemcmp",
+  "memcpy",
+  "memccpy",
+  "mempcpy",
+  "wmemcpy",
+  "memmove",
+  "wmemmove",
+  "memset",
+  "wmemset",
+  // IO:
+  "fread",
+  "fwrite",
+  "fgets",
+  "fgetws",
+  "gets",
+  "fputs",
+  "fputws",
+  "puts",
+  // others
+  "strerror_s",
+  "strerror_r",
+  "bcopy",
+  "bzero",
+  "bsearch",
+  "qsort",
+  };
+  // This is safe: strlen("hello").  We don't want to be noisy on this case.
+  auto isSafeStrlen = [&Node](StringRef Name) -> bool {
+return Name == "strlen" && Node.getNumArgs() == 1 &&
+   isa(Node.getArg(0)->IgnoreParenImpCasts());
+  };
+
+  // Match predefined names:
+  if (PredefinedNames.find(CoreName.str()) != PredefinedNames.end())
+return !isSafeStrlen(CoreName);
+
+  std::string NameWCS = CoreName.str();
+  size_t WcsPos = NameWCS.find("wcs");
+
+  while (WcsPos != std::string::npos) {
+NameWCS[WcsPos++] = 's';
+NameWCS[WcsPos++] = 't';
+NameWCS[WcsPos++] = 'r';
+WcsPos = NameWCS.find("wcs", WcsPos);
+  }
+  if (PredefinedNames.find(NameWCS) != PredefinedNames.end())
+return !isSafeStrlen(NameWCS);
+  // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. 
They
+  // all should end with "scanf"):
+  return CoreName.ends_with("scanf");
+}
+
+// Match a call to one of the `-printf` functions taking `va_list`.  We cannot
+// check safety for these functions so they should be changed to their
+// non-va_list versions.
+AST_MATCHER_P(CallExpr, unsafeVaListPrintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf"))
+return false; // neither printf nor scanf
+  return Name.starts_with("v");
+}
+
+// Matches a call to one of the `-sprintf` functions (excluding the ones with
+// va_list) as they are always unsafe and should be changed to corresponding
+// `-snprintf`s.
+AST_MATCHER_P(CallExpr, unsafeSprintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf") || Name.starts_with("v"))
+return false;
+
+  StringRef Prefix = Name.drop_back(6);
+
+  if (Prefix.ends_with("w"))
+Prefix = Prefix.drop_back(1);
+  return Prefix == "s";
+}
+
+// A pointer type expression is known to be null-terminated, if it has the
+// form: E.c_str(), for any expression E of `std::string` type.
+static bool isNullTermPointer(const Expr *Ptr) {
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
+const CXXMethodDecl *MD = MCE->getMethodDecl();
+const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
+
+if (MD && RD && RD->isInStdNamespace(

[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/101583

>From cce5781733a7c294f10dc75f48372ff6ee331239 Mon Sep 17 00:00:00 2001
From: Ziqing Luo 
Date: Thu, 1 Aug 2024 16:36:27 -0700
Subject: [PATCH 1/3] [-Wunsafe-buffer-usage] Add warn on unsafe calls to libc
 functions

Warning about calls to libc functions involving buffer access.  Warned
functions are hardcoded by names.

(rdar://117182250)
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |  12 +
 .../Analyses/UnsafeBufferUsageGadgets.def |   1 +
 .../clang/Basic/DiagnosticSemaKinds.td|   7 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  | 408 +-
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  12 +
 ...-usage-libc-functions-inline-namespace.cpp |  60 +++
 ...arn-unsafe-buffer-usage-libc-functions.cpp |  86 
 ...n-unsafe-buffer-usage-test-unreachable.cpp |   4 +-
 8 files changed, 586 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 228b4ae1e3e115..3e0fae6db7562d 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
@@ -106,6 +107,17 @@ class UnsafeBufferUsageHandler {
   virtual void handleUnsafeOperation(const Stmt *Operation,
  bool IsRelatedToDecl, ASTContext &Ctx) = 
0;
 
+  /// Invoked when a call to an unsafe libc function is found.
+  /// \param PrintfInfo
+  ///  is 0 if the callee function is not a member of the printf family;
+  ///  is 1 if the callee is `sprintf`;
+  ///  is 2 if arguments of the call have `__size_by` relation but are not in a
+  ///  safe pattern;
+  ///  is 3 if string arguments do not guarantee null-termination
+  ///  is 4 if the callee takes va_list
+  virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
+ASTContext &Ctx) = 0;
+
   /// Invoked when an unsafe operation with a std container is found.
   virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
 bool IsRelatedToDecl,
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 242ad763ba62b9..ac01b285ae833b 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -38,6 +38,7 @@ WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(UnsafeBufferUsageCtorAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_GADGET(UnsafeLibcFunctionCall)
 WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 554dbaff2ce0d8..7e1e3686ce6554 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12383,6 +12383,13 @@ def warn_unsafe_buffer_operation : Warning<
   "%select{unsafe pointer operation|unsafe pointer arithmetic|"
   "unsafe buffer access|function introduces unsafe buffer manipulation|unsafe 
invocation of span::data}0">,
   InGroup, DefaultIgnore;
+def warn_unsafe_buffer_libc_call : Warning<
+  "function %0 introduces unsafe buffer access">,
+  InGroup, DefaultIgnore;
+def note_unsafe_buffer_printf_call : Note<
+  "%select{| change to 'snprintf' for explicit bounds checking | buffer 
pointer and size may not match"
+  "| use 'std::string::c_str' or string literal as string pointer to 
guarantee null-termination"
+  "| do not use va_list that cannot be checked at compile-time for 
bounds safety}0">;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
 def note_unsafe_buffer_variable_fixit_group : Note<
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 866222380974b6..751fb75f6ed602 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,20 +9,24 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclC

[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -2292,6 +2292,18 @@ class UnsafeBufferUsageReporter : public 
UnsafeBufferUsageHandler {
 }
   }
 
+  void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
+ASTContext &Ctx) override {
+// We have checked that there is a direct callee with an identifier name:
+StringRef Name = Call->getDirectCallee()->getName();
+
+S.Diag(Call->getBeginLoc(), diag::warn_unsafe_buffer_libc_call)
+<< Name << Call->getSourceRange();

ziqingluo-90 wrote:

Done

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -443,6 +448,368 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+namespace libc_fun_disjoint_inner_matchers {
+// `libc_fun_disjoint_inner_matchers` covers a set of matchers that match
+// disjoint node sets.   They all take a `CoreName`, which is the substring of 
a
+// function name after `ignoreLibcPrefixAndSuffix`.  They are suppose to be 
used
+// as an inner matcher of `ignoreLibcPrefixAndSuffix` to deal with different
+// libc function calls.
+
+// Matches a function call node such that
+//  1. It's name, after stripping off predefined prefix and suffix, is
+// `CoreName`; and
+//  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
+// is a set of libc function names.
+//
+//  Note: For predefined prefix and suffix, see `ignoreLibcPrefixAndSuffix`.
+//  And, the notation `CoreName[str/wcs]` means a new name obtained from 
replace
+//  string "wcs" with "str" in `CoreName`.
+//
+//  Also note, the set of predefined function names does not include `printf`
+//  functions, they are checked exclusively with other matchers below.
+//  Maintaining the invariant that all matchers under
+//  `libc_fun_disjoint_inner_matchers` are disjoint.
+AST_MATCHER_P(CallExpr, predefinedUnsafeLibcFunCall, StringRef, CoreName) {
+  static const std::set PredefinedNames{

ziqingluo-90 wrote:

Done

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -783,6 +783,18 @@ bool ParsePrintfString(FormatStringHandler &H,
 bool ParseFormatStringHasSArg(const char *beg, const char *end,
   const LangOptions &LO, const TargetInfo &Target);
 
+/// Parse C format string and return index (relative to `ArgIndex`) of the 
first
+/// found `s` specifier.  Return 0 if not found.
+/// \param I The start of the C format string; Updated to the first unparsed
+/// position upon return.
+/// \param E The end of the C format string;
+/// \param ArgIndex The argument index of the last found `s` specifier; Or the
+/// argument index of the formatter in initial case.
+unsigned ParseFormatStringFirstSArgIndex(const char *&I, const char *E,

ziqingluo-90 wrote:

Done

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -483,6 +483,34 @@ bool 
clang::analyze_format_string::ParseFormatStringHasSArg(const char *I,
   return false;
 }
 
+unsigned clang::analyze_format_string::ParseFormatStringFirstSArgIndex(
+const char *&I, const char *E, unsigned ArgIndex, const LangOptions &LO,
+const TargetInfo &Target) {
+  unsigned argIndex = ArgIndex;
+
+  // Keep looking for a %s format specifier until we have exhausted the string.
+  FormatStringHandler H;
+  while (I != E) {
+const PrintfSpecifierResult &FSR =
+ParsePrintfSpecifier(H, I, E, argIndex, LO, Target, false, false);
+// Did a fail-stop error of any kind occur when parsing the specifier?
+// If so, don't do any more processing.
+if (FSR.shouldStop())

ziqingluo-90 wrote:

Done

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -443,6 +448,368 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+namespace libc_fun_disjoint_inner_matchers {
+// `libc_fun_disjoint_inner_matchers` covers a set of matchers that match
+// disjoint node sets.   They all take a `CoreName`, which is the substring of 
a
+// function name after `ignoreLibcPrefixAndSuffix`.  They are suppose to be 
used
+// as an inner matcher of `ignoreLibcPrefixAndSuffix` to deal with different
+// libc function calls.
+
+// Matches a function call node such that
+//  1. It's name, after stripping off predefined prefix and suffix, is
+// `CoreName`; and
+//  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
+// is a set of libc function names.
+//
+//  Note: For predefined prefix and suffix, see `ignoreLibcPrefixAndSuffix`.
+//  And, the notation `CoreName[str/wcs]` means a new name obtained from 
replace
+//  string "wcs" with "str" in `CoreName`.
+//
+//  Also note, the set of predefined function names does not include `printf`
+//  functions, they are checked exclusively with other matchers below.
+//  Maintaining the invariant that all matchers under
+//  `libc_fun_disjoint_inner_matchers` are disjoint.
+AST_MATCHER_P(CallExpr, predefinedUnsafeLibcFunCall, StringRef, CoreName) {
+  static const std::set PredefinedNames{
+  // numeric conversion:
+  "atof",
+  "atoi",
+  "atol",
+  "atoll",
+  "strtol",
+  "strtoll",
+  "strtoul",
+  "strtoull",
+  "strtof",
+  "strtod",
+  "strtold",
+  "strtoimax",
+  "strtoumax",
+  // "strfromf",  "strfromd", "strfroml", // C23?
+  // string manipulation:
+  "strcpy",
+  "strncpy",
+  "strlcpy",
+  "strcat",
+  "strncat",
+  "strlcat",
+  "strxfrm",
+  "strdup",
+  "strndup",
+  // string examination:
+  "strlen",
+  "strnlen",
+  "strcmp",
+  "strncmp",
+  "stricmp",
+  "strcasecmp",
+  "strcoll",
+  "strchr",
+  "strrchr",
+  "strspn",
+  "strcspn",
+  "strpbrk",
+  "strstr",
+  "strtok",
+  // "mem-" functions
+  "memchr",
+  "wmemchr",
+  "memcmp",
+  "wmemcmp",
+  "memcpy",
+  "memccpy",
+  "mempcpy",
+  "wmemcpy",
+  "memmove",
+  "wmemmove",
+  "memset",
+  "wmemset",
+  // IO:
+  "fread",
+  "fwrite",
+  "fgets",
+  "fgetws",
+  "gets",
+  "fputs",
+  "fputws",
+  "puts",
+  // others
+  "strerror_s",
+  "strerror_r",
+  "bcopy",
+  "bzero",
+  "bsearch",
+  "qsort",
+  };
+  // This is safe: strlen("hello").  We don't want to be noisy on this case.
+  auto isSafeStrlen = [&Node](StringRef Name) -> bool {
+return Name == "strlen" && Node.getNumArgs() == 1 &&
+   isa(Node.getArg(0)->IgnoreParenImpCasts());
+  };
+
+  // Match predefined names:
+  if (PredefinedNames.find(CoreName.str()) != PredefinedNames.end())

ziqingluo-90 wrote:

Done

https://github.com/llvm/llvm-project/pull/101583
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -443,6 +448,368 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+namespace libc_fun_disjoint_inner_matchers {
+// `libc_fun_disjoint_inner_matchers` covers a set of matchers that match
+// disjoint node sets.   They all take a `CoreName`, which is the substring of 
a
+// function name after `ignoreLibcPrefixAndSuffix`.  They are suppose to be 
used
+// as an inner matcher of `ignoreLibcPrefixAndSuffix` to deal with different
+// libc function calls.
+
+// Matches a function call node such that
+//  1. It's name, after stripping off predefined prefix and suffix, is
+// `CoreName`; and
+//  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
+// is a set of libc function names.
+//
+//  Note: For predefined prefix and suffix, see `ignoreLibcPrefixAndSuffix`.
+//  And, the notation `CoreName[str/wcs]` means a new name obtained from 
replace
+//  string "wcs" with "str" in `CoreName`.
+//
+//  Also note, the set of predefined function names does not include `printf`
+//  functions, they are checked exclusively with other matchers below.
+//  Maintaining the invariant that all matchers under
+//  `libc_fun_disjoint_inner_matchers` are disjoint.
+AST_MATCHER_P(CallExpr, predefinedUnsafeLibcFunCall, StringRef, CoreName) {
+  static const std::set PredefinedNames{
+  // numeric conversion:
+  "atof",
+  "atoi",
+  "atol",
+  "atoll",
+  "strtol",
+  "strtoll",
+  "strtoul",
+  "strtoull",
+  "strtof",
+  "strtod",
+  "strtold",
+  "strtoimax",
+  "strtoumax",
+  // "strfromf",  "strfromd", "strfroml", // C23?
+  // string manipulation:
+  "strcpy",
+  "strncpy",
+  "strlcpy",
+  "strcat",
+  "strncat",
+  "strlcat",
+  "strxfrm",
+  "strdup",
+  "strndup",
+  // string examination:
+  "strlen",
+  "strnlen",
+  "strcmp",
+  "strncmp",
+  "stricmp",
+  "strcasecmp",
+  "strcoll",
+  "strchr",
+  "strrchr",
+  "strspn",
+  "strcspn",
+  "strpbrk",
+  "strstr",
+  "strtok",
+  // "mem-" functions
+  "memchr",
+  "wmemchr",
+  "memcmp",
+  "wmemcmp",
+  "memcpy",
+  "memccpy",
+  "mempcpy",
+  "wmemcpy",
+  "memmove",
+  "wmemmove",
+  "memset",
+  "wmemset",
+  // IO:
+  "fread",
+  "fwrite",
+  "fgets",
+  "fgetws",
+  "gets",
+  "fputs",
+  "fputws",
+  "puts",
+  // others
+  "strerror_s",
+  "strerror_r",
+  "bcopy",
+  "bzero",
+  "bsearch",
+  "qsort",
+  };
+  // This is safe: strlen("hello").  We don't want to be noisy on this case.
+  auto isSafeStrlen = [&Node](StringRef Name) -> bool {
+return Name == "strlen" && Node.getNumArgs() == 1 &&
+   isa(Node.getArg(0)->IgnoreParenImpCasts());
+  };
+
+  // Match predefined names:
+  if (PredefinedNames.find(CoreName.str()) != PredefinedNames.end())
+return !isSafeStrlen(CoreName);
+
+  std::string NameWCS = CoreName.str();
+  size_t WcsPos = NameWCS.find("wcs");
+
+  while (WcsPos != std::string::npos) {
+NameWCS[WcsPos++] = 's';
+NameWCS[WcsPos++] = 't';
+NameWCS[WcsPos++] = 'r';
+WcsPos = NameWCS.find("wcs", WcsPos);
+  }
+  if (PredefinedNames.find(NameWCS) != PredefinedNames.end())
+return !isSafeStrlen(NameWCS);
+  // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. 
They
+  // all should end with "scanf"):
+  return CoreName.ends_with("scanf");
+}
+
+// Match a call to one of the `-printf` functions taking `va_list`.  We cannot
+// check safety for these functions so they should be changed to their
+// non-va_list versions.
+AST_MATCHER_P(CallExpr, unsafeVaListPrintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf"))
+return false; // neither printf nor scanf
+  return Name.starts_with("v");
+}
+
+// Matches a call to one of the `-sprintf` functions (excluding the ones with
+// va_list) as they are always unsafe and should be changed to corresponding
+// `-snprintf`s.
+AST_MATCHER_P(CallExpr, unsafeSprintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf") || Name.starts_with("v"))
+return false;
+
+  StringRef Prefix = Name.drop_back(6);
+
+  if (Prefix.ends_with("w"))
+Prefix = Prefix.drop_back(1);
+  return Prefix == "s";
+}
+
+// A pointer type expression is known to be null-terminated, if it has the
+// form: E.c_str(), for any expression E of `std::string` type.
+static bool isNullTermPointer(const Expr *Ptr) {
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
+const CXXMethodDecl *MD = MCE->getMethodDecl();
+const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
+
+if (MD && RD && RD->isInStdNamespace(

[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits


@@ -443,6 +448,368 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
   return false;
 }
 
+namespace libc_fun_disjoint_inner_matchers {
+// `libc_fun_disjoint_inner_matchers` covers a set of matchers that match
+// disjoint node sets.   They all take a `CoreName`, which is the substring of 
a
+// function name after `ignoreLibcPrefixAndSuffix`.  They are suppose to be 
used
+// as an inner matcher of `ignoreLibcPrefixAndSuffix` to deal with different
+// libc function calls.
+
+// Matches a function call node such that
+//  1. It's name, after stripping off predefined prefix and suffix, is
+// `CoreName`; and
+//  2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
+// is a set of libc function names.
+//
+//  Note: For predefined prefix and suffix, see `ignoreLibcPrefixAndSuffix`.
+//  And, the notation `CoreName[str/wcs]` means a new name obtained from 
replace
+//  string "wcs" with "str" in `CoreName`.
+//
+//  Also note, the set of predefined function names does not include `printf`
+//  functions, they are checked exclusively with other matchers below.
+//  Maintaining the invariant that all matchers under
+//  `libc_fun_disjoint_inner_matchers` are disjoint.
+AST_MATCHER_P(CallExpr, predefinedUnsafeLibcFunCall, StringRef, CoreName) {
+  static const std::set PredefinedNames{
+  // numeric conversion:
+  "atof",
+  "atoi",
+  "atol",
+  "atoll",
+  "strtol",
+  "strtoll",
+  "strtoul",
+  "strtoull",
+  "strtof",
+  "strtod",
+  "strtold",
+  "strtoimax",
+  "strtoumax",
+  // "strfromf",  "strfromd", "strfroml", // C23?
+  // string manipulation:
+  "strcpy",
+  "strncpy",
+  "strlcpy",
+  "strcat",
+  "strncat",
+  "strlcat",
+  "strxfrm",
+  "strdup",
+  "strndup",
+  // string examination:
+  "strlen",
+  "strnlen",
+  "strcmp",
+  "strncmp",
+  "stricmp",
+  "strcasecmp",
+  "strcoll",
+  "strchr",
+  "strrchr",
+  "strspn",
+  "strcspn",
+  "strpbrk",
+  "strstr",
+  "strtok",
+  // "mem-" functions
+  "memchr",
+  "wmemchr",
+  "memcmp",
+  "wmemcmp",
+  "memcpy",
+  "memccpy",
+  "mempcpy",
+  "wmemcpy",
+  "memmove",
+  "wmemmove",
+  "memset",
+  "wmemset",
+  // IO:
+  "fread",
+  "fwrite",
+  "fgets",
+  "fgetws",
+  "gets",
+  "fputs",
+  "fputws",
+  "puts",
+  // others
+  "strerror_s",
+  "strerror_r",
+  "bcopy",
+  "bzero",
+  "bsearch",
+  "qsort",
+  };
+  // This is safe: strlen("hello").  We don't want to be noisy on this case.
+  auto isSafeStrlen = [&Node](StringRef Name) -> bool {
+return Name == "strlen" && Node.getNumArgs() == 1 &&
+   isa(Node.getArg(0)->IgnoreParenImpCasts());
+  };
+
+  // Match predefined names:
+  if (PredefinedNames.find(CoreName.str()) != PredefinedNames.end())
+return !isSafeStrlen(CoreName);
+
+  std::string NameWCS = CoreName.str();
+  size_t WcsPos = NameWCS.find("wcs");
+
+  while (WcsPos != std::string::npos) {
+NameWCS[WcsPos++] = 's';
+NameWCS[WcsPos++] = 't';
+NameWCS[WcsPos++] = 'r';
+WcsPos = NameWCS.find("wcs", WcsPos);
+  }
+  if (PredefinedNames.find(NameWCS) != PredefinedNames.end())
+return !isSafeStrlen(NameWCS);
+  // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. 
They
+  // all should end with "scanf"):
+  return CoreName.ends_with("scanf");
+}
+
+// Match a call to one of the `-printf` functions taking `va_list`.  We cannot
+// check safety for these functions so they should be changed to their
+// non-va_list versions.
+AST_MATCHER_P(CallExpr, unsafeVaListPrintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf"))
+return false; // neither printf nor scanf
+  return Name.starts_with("v");
+}
+
+// Matches a call to one of the `-sprintf` functions (excluding the ones with
+// va_list) as they are always unsafe and should be changed to corresponding
+// `-snprintf`s.
+AST_MATCHER_P(CallExpr, unsafeSprintfs, StringRef, CoreName) {
+  StringRef Name = CoreName;
+
+  if (!Name.ends_with("printf") || Name.starts_with("v"))
+return false;
+
+  StringRef Prefix = Name.drop_back(6);
+
+  if (Prefix.ends_with("w"))
+Prefix = Prefix.drop_back(1);
+  return Prefix == "s";
+}
+
+// A pointer type expression is known to be null-terminated, if it has the
+// form: E.c_str(), for any expression E of `std::string` type.
+static bool isNullTermPointer(const Expr *Ptr) {
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (isa(Ptr->IgnoreParenImpCasts()))
+return true;
+  if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
+const CXXMethodDecl *MD = MCE->getMethodDecl();
+const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
+
+if (MD && RD && RD->isInStdNamespace(

[clang] [-Wunsafe-buffer-usage] Warning Libc functions (PR #101583)

2024-08-15 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/101583

>From cce5781733a7c294f10dc75f48372ff6ee331239 Mon Sep 17 00:00:00 2001
From: Ziqing Luo 
Date: Thu, 1 Aug 2024 16:36:27 -0700
Subject: [PATCH 1/3] [-Wunsafe-buffer-usage] Add warn on unsafe calls to libc
 functions

Warning about calls to libc functions involving buffer access.  Warned
functions are hardcoded by names.

(rdar://117182250)
---
 .../Analysis/Analyses/UnsafeBufferUsage.h |  12 +
 .../Analyses/UnsafeBufferUsageGadgets.def |   1 +
 .../clang/Basic/DiagnosticSemaKinds.td|   7 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp  | 408 +-
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |  12 +
 ...-usage-libc-functions-inline-namespace.cpp |  60 +++
 ...arn-unsafe-buffer-usage-libc-functions.cpp |  86 
 ...n-unsafe-buffer-usage-test-unreachable.cpp |   4 +-
 8 files changed, 586 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 228b4ae1e3e115..3e0fae6db7562d 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/Support/Debug.h"
@@ -106,6 +107,17 @@ class UnsafeBufferUsageHandler {
   virtual void handleUnsafeOperation(const Stmt *Operation,
  bool IsRelatedToDecl, ASTContext &Ctx) = 
0;
 
+  /// Invoked when a call to an unsafe libc function is found.
+  /// \param PrintfInfo
+  ///  is 0 if the callee function is not a member of the printf family;
+  ///  is 1 if the callee is `sprintf`;
+  ///  is 2 if arguments of the call have `__size_by` relation but are not in a
+  ///  safe pattern;
+  ///  is 3 if string arguments do not guarantee null-termination
+  ///  is 4 if the callee takes va_list
+  virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
+ASTContext &Ctx) = 0;
+
   /// Invoked when an unsafe operation with a std container is found.
   virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
 bool IsRelatedToDecl,
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def 
b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 242ad763ba62b9..ac01b285ae833b 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -38,6 +38,7 @@ WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(UnsafeBufferUsageCtorAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_GADGET(UnsafeLibcFunctionCall)
 WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, 
arg1)`
 FIXABLE_GADGET(ULCArraySubscript)  // `DRE[any]` in an Unspecified 
Lvalue Context
 FIXABLE_GADGET(DerefSimplePtrArithFixable)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 554dbaff2ce0d8..7e1e3686ce6554 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12383,6 +12383,13 @@ def warn_unsafe_buffer_operation : Warning<
   "%select{unsafe pointer operation|unsafe pointer arithmetic|"
   "unsafe buffer access|function introduces unsafe buffer manipulation|unsafe 
invocation of span::data}0">,
   InGroup, DefaultIgnore;
+def warn_unsafe_buffer_libc_call : Warning<
+  "function %0 introduces unsafe buffer access">,
+  InGroup, DefaultIgnore;
+def note_unsafe_buffer_printf_call : Note<
+  "%select{| change to 'snprintf' for explicit bounds checking | buffer 
pointer and size may not match"
+  "| use 'std::string::c_str' or string literal as string pointer to 
guarantee null-termination"
+  "| do not use va_list that cannot be checked at compile-time for 
bounds safety}0">;
 def note_unsafe_buffer_operation : Note<
   "used%select{| in pointer arithmetic| in buffer access}0 here">;
 def note_unsafe_buffer_variable_fixit_group : Note<
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp 
b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 866222380974b6..751fb75f6ed602 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,20 +9,24 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclC

[clang] Add handling of the `-nostdlibinc` option to ccc-analyzer (PR #88017)

2024-04-08 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 created 
https://github.com/llvm/llvm-project/pull/88017

Compiler options recognizable to ccc-analyzer are stored in maps.  An option 
missing in the map will be dropped by ccc-analyzer.  This causes a build error 
in one of our projects that only happens in scan-build but not regular build, 
because ccc-analyzer do not recognize `-nostdlibinc`.

This commit adds the option to the map.

>From 188398778993244223638e8433efe2ce703772dd Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 8 Apr 2024 10:25:43 -0700
Subject: [PATCH] Add handling of the `-nostdlibinc` option to ccc-analyzer

Compiler options recognizable to ccc-analyzer are stored in maps.  An
option missing in the map will be dropped by ccc-analyzer.  This
causes a build error in one of our projects that only happens in
scan-build but not regular build, because ccc-analyzer do not
recognize `-nostdlibinc`.

This commit adds the option to the map.

rdar://126082053
---
 clang/tools/scan-build/libexec/ccc-analyzer | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/tools/scan-build/libexec/ccc-analyzer 
b/clang/tools/scan-build/libexec/ccc-analyzer
index 60796a543fcd06..c5588814e8f0de 100755
--- a/clang/tools/scan-build/libexec/ccc-analyzer
+++ b/clang/tools/scan-build/libexec/ccc-analyzer
@@ -361,6 +361,7 @@ sub Analyze {
 
 my %CompileOptionMap = (
   '-nostdinc' => 0,
+  '-nostdlibinc' => 0,
   '-include' => 1,
   '-idirafter' => 1,
   '-imacros' => 1,

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


[clang] Add handling of the `-nostdlibinc` option to ccc-analyzer (PR #88017)

2024-04-09 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 closed 
https://github.com/llvm/llvm-project/pull/88017
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Safe Buffers] Serialize unsafe_buffer_usage pragmas (PR #92031)

2024-06-07 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/92031

>From ac5aeb5c3a134d085320fc7fc5cf3f2c8c41a1f1 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 13 May 2024 13:31:21 -0700
Subject: [PATCH 1/5] fix safe buffer opt-out region serialization

---
 clang/include/clang/Lex/Preprocessor.h|  22 +++-
 .../include/clang/Serialization/ASTBitCodes.h |   3 +
 clang/lib/Lex/Preprocessor.cpp| 106 ++
 clang/lib/Serialization/ASTReader.cpp |  11 ++
 clang/lib/Serialization/ASTWriter.cpp |   7 ++
 clang/test/Modules/Inputs/SafeBuffers/base.h  |   9 ++
 .../SafeBuffers/safe_buffers_test.modulemap   |  10 ++
 .../Modules/Inputs/SafeBuffers/test_sub1.h|  20 
 .../Modules/Inputs/SafeBuffers/test_sub2.h|  11 ++
 clang/test/Modules/safe_buffers_optout.cpp|  39 +++
 ...unsafe-buffer-usage-pragma-pch-complex.cpp |  72 
 .../warn-unsafe-buffer-usage-pragma-pch.cpp   |  27 +
 12 files changed, 314 insertions(+), 23 deletions(-)
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/base.h
 create mode 100644 
clang/test/Modules/Inputs/SafeBuffers/safe_buffers_test.modulemap
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/test_sub1.h
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/test_sub2.h
 create mode 100644 clang/test/Modules/safe_buffers_optout.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-pch-complex.cpp
 create mode 100644 clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-pch.cpp

diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index e89b4a2c5230e..8d6884ebe7597 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2883,11 +2883,15 @@ class Preprocessor {
   /// otherwise.
   SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the 
start location of an never-closed region.
 
-  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one
-  // translation unit. Each region is represented by a pair of start and end
-  // locations.  A region is "open" if its' start and end locations are
+  using SafeBufferOptOutMapTy =
+  SmallVector, 16>;
+  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in this
+  // translation unit. Each region is represented by a pair of start and
+  // end locations.  A region is "open" if its' start and end locations are
   // identical.
-  SmallVector, 8> 
SafeBufferOptOutMap;
+  SafeBufferOptOutMapTy SafeBufferOptOutMap;
+  // `SafeBufferOptOutMap`s of loaded files:
+  llvm::DenseMap LoadedSafeBufferOptOutMap;
 
 public:
   /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out
@@ -2918,6 +2922,16 @@ class Preprocessor {
   ///  opt-out region
   bool isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc);
 
+  /// \return a sequence of SourceLocations representing ordered opt-out 
regions
+  /// specified by
+  /// `\#pragma clang unsafe_buffer_usage begin/end`s of this translation unit.
+  SmallVector serializeSafeBufferOptOutMap() const;
+
+  /// \param SrcLocSeqs a sequence of SourceLocations deserialized from a
+  /// record of code `PP_UNSAFE_BUFFER_USAGE`.
+  void setDeserializedSafeBufferOptOutMap(
+  const SmallVectorImpl &SrcLocSeqs);
+
 private:
   /// Helper functions to forward lexing to the actual lexer. They all share 
the
   /// same signature.
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index dcfa4ac0c1967..d1a0eba943039 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -775,6 +775,9 @@ enum ASTRecordTypes {
   /// Record code for lexical and visible block for delayed namespace in
   /// reduced BMI.
   DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD = 68,
+
+  /// Record code for \#pragma clang unsafe_buffer_usage begin/end
+  PP_UNSAFE_BUFFER_USAGE = 69,
 };
 
 /// Record types used within a source manager block.
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 0b70192743a39..6a41e3d4138aa 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -58,6 +58,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/Capacity.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -1483,26 +1484,41 @@ void Preprocessor::emitFinalMacroWarning(const Token 
&Identifier,
 }
 
 bool Preprocessor::isSafeBufferOptOut(const SourceManager &SourceMgr,
-   const SourceLocation &Loc) const {
-  // Try to find a region in `SafeBufferOptOutMap` where `Loc` is in:
-  auto FirstRegionEndingAfterLoc = llvm::partition_point(
-  SafeBufferOptOutMap,
-  [&SourceMgr,
-

[clang] [Safe Buffers] Serialize unsafe_buffer_usage pragmas (PR #92031)

2024-06-07 Thread Ziqing Luo via cfe-commits


@@ -0,0 +1,41 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module 
-fmodule-name=safe_buffers_test_base -x c++ 
%S/Inputs/SafeBuffers/safe_buffers_test.modulemap -std=c++20\
+// RUN: -o %t/safe_buffers_test_base.pcm -Wunsafe-buffer-usage
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module 
-fmodule-name=safe_buffers_test_module -x c++ 
%S/Inputs/SafeBuffers/safe_buffers_test.modulemap -std=c++20\
+// RUN: -o %t/safe_buffers_test_module.pcm
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module 
-fmodule-name=safe_buffers_test_optout -x c++ 
%S/Inputs/SafeBuffers/safe_buffers_test.modulemap -std=c++20\
+// RUN: -o %t/safe_buffers_test_optout.pcm 
-fmodule-file=%t/safe_buffers_test_base.pcm 
-fmodule-file=%t/safe_buffers_test_module.pcm -Wunsafe-buffer-usage
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -verify 
-fmodule-file=%t/safe_buffers_test_optout.pcm -I %S/Inputs/SafeBuffers %s\
+// RUN: -std=c++20 -Wunsafe-buffer-usage
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -verify 
-fmodules-cache-path=%t 
-fmodule-map-file=%S/Inputs/SafeBuffers/safe_buffers_test.modulemap -I 
%S/Inputs/SafeBuffers %s\
+// RUN: -x c++ -std=c++20 -Wunsafe-buffer-usage
+
+#include "test_sub1.h"

ziqingluo-90 wrote:

Thank you for the comment!   `split-file` is really neat!!

https://github.com/llvm/llvm-project/pull/92031
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Safe Buffers] Serialize unsafe_buffer_usage pragmas (PR #92031)

2024-06-13 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/92031

>From ac5aeb5c3a134d085320fc7fc5cf3f2c8c41a1f1 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 13 May 2024 13:31:21 -0700
Subject: [PATCH 1/6] fix safe buffer opt-out region serialization

---
 clang/include/clang/Lex/Preprocessor.h|  22 +++-
 .../include/clang/Serialization/ASTBitCodes.h |   3 +
 clang/lib/Lex/Preprocessor.cpp| 106 ++
 clang/lib/Serialization/ASTReader.cpp |  11 ++
 clang/lib/Serialization/ASTWriter.cpp |   7 ++
 clang/test/Modules/Inputs/SafeBuffers/base.h  |   9 ++
 .../SafeBuffers/safe_buffers_test.modulemap   |  10 ++
 .../Modules/Inputs/SafeBuffers/test_sub1.h|  20 
 .../Modules/Inputs/SafeBuffers/test_sub2.h|  11 ++
 clang/test/Modules/safe_buffers_optout.cpp|  39 +++
 ...unsafe-buffer-usage-pragma-pch-complex.cpp |  72 
 .../warn-unsafe-buffer-usage-pragma-pch.cpp   |  27 +
 12 files changed, 314 insertions(+), 23 deletions(-)
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/base.h
 create mode 100644 
clang/test/Modules/Inputs/SafeBuffers/safe_buffers_test.modulemap
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/test_sub1.h
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/test_sub2.h
 create mode 100644 clang/test/Modules/safe_buffers_optout.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-pch-complex.cpp
 create mode 100644 clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-pch.cpp

diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index e89b4a2c5230e..8d6884ebe7597 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2883,11 +2883,15 @@ class Preprocessor {
   /// otherwise.
   SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the 
start location of an never-closed region.
 
-  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one
-  // translation unit. Each region is represented by a pair of start and end
-  // locations.  A region is "open" if its' start and end locations are
+  using SafeBufferOptOutMapTy =
+  SmallVector, 16>;
+  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in this
+  // translation unit. Each region is represented by a pair of start and
+  // end locations.  A region is "open" if its' start and end locations are
   // identical.
-  SmallVector, 8> 
SafeBufferOptOutMap;
+  SafeBufferOptOutMapTy SafeBufferOptOutMap;
+  // `SafeBufferOptOutMap`s of loaded files:
+  llvm::DenseMap LoadedSafeBufferOptOutMap;
 
 public:
   /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out
@@ -2918,6 +2922,16 @@ class Preprocessor {
   ///  opt-out region
   bool isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc);
 
+  /// \return a sequence of SourceLocations representing ordered opt-out 
regions
+  /// specified by
+  /// `\#pragma clang unsafe_buffer_usage begin/end`s of this translation unit.
+  SmallVector serializeSafeBufferOptOutMap() const;
+
+  /// \param SrcLocSeqs a sequence of SourceLocations deserialized from a
+  /// record of code `PP_UNSAFE_BUFFER_USAGE`.
+  void setDeserializedSafeBufferOptOutMap(
+  const SmallVectorImpl &SrcLocSeqs);
+
 private:
   /// Helper functions to forward lexing to the actual lexer. They all share 
the
   /// same signature.
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index dcfa4ac0c1967..d1a0eba943039 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -775,6 +775,9 @@ enum ASTRecordTypes {
   /// Record code for lexical and visible block for delayed namespace in
   /// reduced BMI.
   DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD = 68,
+
+  /// Record code for \#pragma clang unsafe_buffer_usage begin/end
+  PP_UNSAFE_BUFFER_USAGE = 69,
 };
 
 /// Record types used within a source manager block.
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 0b70192743a39..6a41e3d4138aa 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -58,6 +58,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/Capacity.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -1483,26 +1484,41 @@ void Preprocessor::emitFinalMacroWarning(const Token 
&Identifier,
 }
 
 bool Preprocessor::isSafeBufferOptOut(const SourceManager &SourceMgr,
-   const SourceLocation &Loc) const {
-  // Try to find a region in `SafeBufferOptOutMap` where `Loc` is in:
-  auto FirstRegionEndingAfterLoc = llvm::partition_point(
-  SafeBufferOptOutMap,
-  [&SourceMgr,
-

[clang] [Safe Buffers] Serialize unsafe_buffer_usage pragmas (PR #92031)

2024-06-13 Thread Ziqing Luo via cfe-commits

https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/92031

>From ac5aeb5c3a134d085320fc7fc5cf3f2c8c41a1f1 Mon Sep 17 00:00:00 2001
From: ziqingluo-90 
Date: Mon, 13 May 2024 13:31:21 -0700
Subject: [PATCH 1/7] fix safe buffer opt-out region serialization

---
 clang/include/clang/Lex/Preprocessor.h|  22 +++-
 .../include/clang/Serialization/ASTBitCodes.h |   3 +
 clang/lib/Lex/Preprocessor.cpp| 106 ++
 clang/lib/Serialization/ASTReader.cpp |  11 ++
 clang/lib/Serialization/ASTWriter.cpp |   7 ++
 clang/test/Modules/Inputs/SafeBuffers/base.h  |   9 ++
 .../SafeBuffers/safe_buffers_test.modulemap   |  10 ++
 .../Modules/Inputs/SafeBuffers/test_sub1.h|  20 
 .../Modules/Inputs/SafeBuffers/test_sub2.h|  11 ++
 clang/test/Modules/safe_buffers_optout.cpp|  39 +++
 ...unsafe-buffer-usage-pragma-pch-complex.cpp |  72 
 .../warn-unsafe-buffer-usage-pragma-pch.cpp   |  27 +
 12 files changed, 314 insertions(+), 23 deletions(-)
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/base.h
 create mode 100644 
clang/test/Modules/Inputs/SafeBuffers/safe_buffers_test.modulemap
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/test_sub1.h
 create mode 100644 clang/test/Modules/Inputs/SafeBuffers/test_sub2.h
 create mode 100644 clang/test/Modules/safe_buffers_optout.cpp
 create mode 100644 
clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-pch-complex.cpp
 create mode 100644 clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-pch.cpp

diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index e89b4a2c5230e..8d6884ebe7597 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2883,11 +2883,15 @@ class Preprocessor {
   /// otherwise.
   SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the 
start location of an never-closed region.
 
-  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one
-  // translation unit. Each region is represented by a pair of start and end
-  // locations.  A region is "open" if its' start and end locations are
+  using SafeBufferOptOutMapTy =
+  SmallVector, 16>;
+  // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in this
+  // translation unit. Each region is represented by a pair of start and
+  // end locations.  A region is "open" if its' start and end locations are
   // identical.
-  SmallVector, 8> 
SafeBufferOptOutMap;
+  SafeBufferOptOutMapTy SafeBufferOptOutMap;
+  // `SafeBufferOptOutMap`s of loaded files:
+  llvm::DenseMap LoadedSafeBufferOptOutMap;
 
 public:
   /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out
@@ -2918,6 +2922,16 @@ class Preprocessor {
   ///  opt-out region
   bool isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc);
 
+  /// \return a sequence of SourceLocations representing ordered opt-out 
regions
+  /// specified by
+  /// `\#pragma clang unsafe_buffer_usage begin/end`s of this translation unit.
+  SmallVector serializeSafeBufferOptOutMap() const;
+
+  /// \param SrcLocSeqs a sequence of SourceLocations deserialized from a
+  /// record of code `PP_UNSAFE_BUFFER_USAGE`.
+  void setDeserializedSafeBufferOptOutMap(
+  const SmallVectorImpl &SrcLocSeqs);
+
 private:
   /// Helper functions to forward lexing to the actual lexer. They all share 
the
   /// same signature.
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index dcfa4ac0c1967..d1a0eba943039 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -775,6 +775,9 @@ enum ASTRecordTypes {
   /// Record code for lexical and visible block for delayed namespace in
   /// reduced BMI.
   DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD = 68,
+
+  /// Record code for \#pragma clang unsafe_buffer_usage begin/end
+  PP_UNSAFE_BUFFER_USAGE = 69,
 };
 
 /// Record types used within a source manager block.
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 0b70192743a39..6a41e3d4138aa 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -58,6 +58,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/Capacity.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -1483,26 +1484,41 @@ void Preprocessor::emitFinalMacroWarning(const Token 
&Identifier,
 }
 
 bool Preprocessor::isSafeBufferOptOut(const SourceManager &SourceMgr,
-   const SourceLocation &Loc) const {
-  // Try to find a region in `SafeBufferOptOutMap` where `Loc` is in:
-  auto FirstRegionEndingAfterLoc = llvm::partition_point(
-  SafeBufferOptOutMap,
-  [&SourceMgr,
-

  1   2   3   >