[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-08-29 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rG7df0f0b41067: [clang-tidy] Fixing a bug in 
`InfiniteLoopCheck` that raises false alarms on… (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D128401?vs=439903&id=456422#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -166,6 +169,110 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const Decl *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const Decl *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const Stmt *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool ContainsFunc = false, Overlap = false;
+
+  for (const CallGraphNode *GNode : SCC) {
+con

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-07-01 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 441816.
ziqingluo-90 added a comment.

I saw @aaron.ballman 's comment "//We typically don't add AST matchers until we 
have a need for them to be used in-tree (ASTMatchers.h is already really 
expensive to parse; adding matchers for everything possible with AST nodes 
would be prohibitively expensive).//" in this patch 
.   So I think the matchers for testing 
`noreturn` attributes is too specific to be in the `ASTMatcher.h`.  Moving them 
to where they are used.   While the `objcMessageCallee` is sort of the Obj-C 
counterpart of the `callee` matcher, so I think it is general enough to stay.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/lib/ASTMatchers/Dynamic/Registry.cpp

Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -501,6 +501,7 @@
   REGISTER_MATCHER(objcIvarDecl);
   REGISTER_MATCHER(objcIvarRefExpr);
   REGISTER_MATCHER(objcMessageExpr);
+  REGISTER_MATCHER(objcMessageCallee);
   REGISTER_MATCHER(objcMethodDecl);
   REGISTER_MATCHER(objcObjectPointerType);
   REGISTER_MATCHER(objcPropertyDecl);
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,6 +3838,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {
+  const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+  return (msgDecl != nullptr &&
+  InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -8867,6 +8867,20 @@
 
 
 
+MatcherObjCMessageExpr>objcMessageCalleeMatcherObjCMethodDecl> InnerMatcher
+matches if ObjCMessageExpr's callee declaration matches
+
+Given
+  @interface I: NSObject
+  +(void)foo;
+  @end
+  ...
+  [I foo]
+The example above matches [I foo] with
+objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+
+
+
 MatcherObjCMethodDecl>hasAnyParameterMatcherParmVarDecl> InnerMatcher
 Matches any parameter of a function or an ObjC method declaration or a
 block.
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fexceptions
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc -fexceptions
+
+@interface I
++ (void)foo;
++ (void)bar;
++ (void)baz __attribute__((noreturn));
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  int a[10];
+
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i + j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone-infinite-loop]
+[I foo];
+  }
+  while (i + j < 10) {
+[I foo];
+[I baz]; // no warning, class method baz has noreturn attribute
+  }
+
+  void (^block)() = ^{
+  };
+  void __attribute__((noreturn)) (^block_nr)(void) = ^void __attribute__((noreturn)) (void) { throw "err"; };
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is inf

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-07-01 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 441832.
ziqingluo-90 added a comment.

adding a unit test for the ASTMatcher `objcMessageCallee` added in ASTMatcher.h


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/lib/ASTMatchers/Dynamic/Registry.cpp
  clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -2352,6 +2352,37 @@
argumentCountIs(0;
 }
 
+TEST(ASTMatchersTestObjC, ObjCMessageCallee) {
+  //  Will NOT match function callee:
+  EXPECT_TRUE(notMatchesObjC("void f() {"
+ "  f(); "
+ "}",
+ objcMessageExpr(objcMessageCallee(hasName("f");
+
+  StringRef Objc1String = "@interface I "
+  " - (void)instanceMethod;"
+  " + (void)classMethod;"
+  " + (void)uncalledMethod;"
+  "@end\n"
+  "int main(void) {\n"
+  "  [I classMethod];"
+  "  I *i = [[I alloc] init];"
+  "  [i instanceMethod];"
+  "}";
+  // Should find the two method declarations through the message expressions:
+  EXPECT_TRUE(
+  matchesObjC(Objc1String, objcMessageExpr(objcMessageCallee(
+   objcMethodDecl(hasName("classMethod"));
+  EXPECT_TRUE(matchesObjC(Objc1String,
+  objcMessageExpr(objcMessageCallee(
+  objcMethodDecl(hasName("instanceMethod"));
+  // Will NOT match a method declaration through `objcMessageCallee` if there is
+  // no such message expression:
+  EXPECT_FALSE(matchesObjC(Objc1String,
+   objcMessageExpr(objcMessageCallee(
+   objcMethodDecl(hasName("uncalledMethod"));
+}
+
 TEST(ASTMatchersTestObjC, ObjCStringLiteral) {
 
   StringRef Objc1String = "@interface NSObject "
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -501,6 +501,7 @@
   REGISTER_MATCHER(objcIvarDecl);
   REGISTER_MATCHER(objcIvarRefExpr);
   REGISTER_MATCHER(objcMessageExpr);
+  REGISTER_MATCHER(objcMessageCallee);
   REGISTER_MATCHER(objcMethodDecl);
   REGISTER_MATCHER(objcObjectPointerType);
   REGISTER_MATCHER(objcPropertyDecl);
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,6 +3838,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {
+  const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+  return (msgDecl != nullptr &&
+  InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -8867,6 +8867,20 @@
 
 
 
+MatcherObjCMessageExpr>objcMessageCalleeMatcherObjCMethodDecl> InnerMatcher
+matches if ObjCMessageExpr's callee declaration matches
+
+Given
+  @interface I: NSObject
+  +(void)foo;
+  @end
+  ...
+  [I foo]
+The example above matches [I foo] with
+objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+
+
+
 MatcherObjCMethodDecl>hasAnyParameterMatcherParmVarDecl> InnerMatcher
 Matches any parameter of a function or an Obj

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-07-01 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

In D128314#3605588 , @NoQ wrote:

> Nice!
>
> There's usually some bureaucracy when creating new matchers, i.e. there 
> should be documentation and unittests covering them.

I have added a unit test and auto-generated document for it.




Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:5099
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {
+  return Node.getNoReturnAttr();

NoQ wrote:
> Can we make a polymorphic matcher `hasNoReturnAttr` that can accept either 
> decl or type?
I've moved these two matchers, which seems are specific to the infinite loop 
checker, to `InfiniteLoopCheck.cpp`.  Maybe they no longer need to be 
generalized by polymorphism. 


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128314

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


[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-07-05 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

Hi @LegalizeAdulthood, @NoQ, @gribozavr2, @Eugene.Zelenko,

Is there any further issue I should address for this patch?


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

https://reviews.llvm.org/D128401

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


[PATCH] D129398: Adding a new ASTMatcher for callee declarations of Obj-C message expressions

2022-07-08 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, njames93, aaron.ballman, klimek, t-rasmud, 
usama54321, LegalizeAdulthood.
ziqingluo-90 added a project: clang.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a subscriber: cfe-commits.

For an Obj-C message expression `[o  m]`, the adding matcher will match the 
declaration of the method `m`.  This matcher is the Obj-C counterpart of the 
existing `callee` ASTMatcher which matches function/method declarations for 
C/C++ calls. So I think this matcher is general enough to be added to the 
`ASTMatcher.h`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129398

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/lib/ASTMatchers/Dynamic/Registry.cpp
  clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -2352,6 +2352,37 @@
argumentCountIs(0;
 }
 
+TEST(ASTMatchersTestObjC, ObjCMessageCallee) {
+  //  Will NOT match function callee:
+  EXPECT_TRUE(notMatchesObjC("void f() {"
+ "  f(); "
+ "}",
+ objcMessageExpr(objcMessageCallee(hasName("f");
+
+  StringRef Objc1String = "@interface I "
+  " - (void)instanceMethod;"
+  " + (void)classMethod;"
+  " + (void)uncalledMethod;"
+  "@end\n"
+  "int main(void) {\n"
+  "  [I classMethod];"
+  "  I *i = [[I alloc] init];"
+  "  [i instanceMethod];"
+  "}";
+  // Should find the two method declarations through the message expressions:
+  EXPECT_TRUE(
+  matchesObjC(Objc1String, objcMessageExpr(objcMessageCallee(
+   objcMethodDecl(hasName("classMethod"));
+  EXPECT_TRUE(matchesObjC(Objc1String,
+  objcMessageExpr(objcMessageCallee(
+  objcMethodDecl(hasName("instanceMethod"));
+  // Will NOT match a method declaration through `objcMessageCallee` if there is
+  // no such message expression:
+  EXPECT_FALSE(matchesObjC(Objc1String,
+   objcMessageExpr(objcMessageCallee(
+   objcMethodDecl(hasName("uncalledMethod"));
+}
+
 TEST(ASTMatchersTestObjC, ObjCStringLiteral) {
 
   StringRef Objc1String = "@interface NSObject "
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -501,6 +501,7 @@
   REGISTER_MATCHER(objcIvarDecl);
   REGISTER_MATCHER(objcIvarRefExpr);
   REGISTER_MATCHER(objcMessageExpr);
+  REGISTER_MATCHER(objcMessageCallee);
   REGISTER_MATCHER(objcMethodDecl);
   REGISTER_MATCHER(objcObjectPointerType);
   REGISTER_MATCHER(objcPropertyDecl);
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,6 +3838,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {
+  const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+  return (msgDecl != nullptr &&
+  InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -8867,6 +8867,20 @@
 
 
 
+MatcherObjCMessageExpr>objcMessageCalleeMatcherObjCMethodDecl> InnerMatcher
+matches if ObjCMessageExpr's callee declaration matches
+
+Given
+  @interface I: NSObject
+  +(void)foo;
+  @end
+  ...
+  [I foo]
+The example above matches [I foo] with
+objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-07-08 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

In D128314#3635861 , @njames93 wrote:

> Sorry to do this again, but could this be split up again, one patch for the 
> new matcher and the tests associated with it, then another for the actual bug 
> fix.
> Also cc @klimek as he is the code owner of ASTMatchers

Makes sense.  I have created a new review  request 
 for the ASTMatcher.   This patch now depends 
on it.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128314

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


[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-07-26 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 447854.
ziqingluo-90 added a comment.

The `callee` ASTMatcher overloading patch has landed in LLVM repo.  I update 
this patch to use `callee` for matching objective-C message callee methods.


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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fexceptions
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc -fexceptions
+
+@interface I
++ (void)foo;
++ (void)bar;
++ (void)baz __attribute__((noreturn));
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  int a[10];
+
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i + j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone-infinite-loop]
+[I foo];
+  }
+  while (i + j < 10) {
+[I foo];
+[I baz]; // no warning, class method baz has noreturn attribute
+  }
+
+  void (^block)() = ^{
+  };
+  void __attribute__((noreturn)) (^block_nr)(void) = ^void __attribute__((noreturn)) (void) { throw "err"; };
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+block();
+  }
+  while (i < 10) {
+// no warning, the block has "noreturn" arribute
+block_nr();
+  }
+}
+
+@implementation I
++ (void)bar {
+}
+
++ (void)foo {
+  static int i = 0;
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I bar];
+  }
+}
+@end
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -16,15 +16,35 @@
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
 
 namespace clang {
+namespace ast_matchers {
+/// matches a Decl if it has a  "no return" attribute of any kind
+AST_MATCHER(Decl, declHasNoReturnAttr) {
+  return Node.hasAttr() || Node.hasAttr() ||
+ Node.hasAttr();
+}
+
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {
+  return Node.getNoReturnAttr();
+}
+} // namespace ast_matchers
 namespace tidy {
 namespace bugprone {
 
 static internal::Matcher
 loopEndingStmt(internal::Matcher Internal) {
-  // FIXME: Cover noreturn ObjC methods (and blocks?).
+  internal::Matcher isNoReturnFunType =
+  ignoringParens(functionType(typeHasNoReturnAttr()));
+  internal::Matcher isNoReturnDecl =
+  anyOf(declHasNoReturnAttr(), functionDecl(hasType(isNoReturnFunType)),
+varDecl(hasType(blockPointerType(pointee(isNoReturnFunType);
+
   return stmt(anyOf(
   mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
-  callExpr(Internal, callee(functionDecl(isNoReturn());
+  callExpr(Internal,
+   callee(mapAnyOf(functionDecl, /* block callee */ varDecl)
+  .with(isNoReturnDecl))),
+  objcMessageExpr(Internal, callee(isNoReturnDecl;
 }
 
 /// Return whether `Var` was changed in `LoopStmt`.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131009: [analyzer] Fixing a bug raising false positives of stack block object leaking under ARC

2022-08-02 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, t-rasmud, usama54321, steakhal, vsavchenko, 
martong, Szelethus.
Herald added subscribers: manas, ASDenysPetrov, dkrupp, donat.nagy, 
mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

When ARC is enabled, (objective-c) block objects are automatically retained and 
released thus they do not leak.  
Without ARC, they still can leak from an expiring stack frame like other stack 
variables.

This patch simply adds the ARC checking condition onto the changes made in 
https://reviews.llvm.org/D107078 .   The 
checker already checks the same condition for global variables.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131009

Files:
  clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
  clang/test/Analysis/stack-capture-leak-arc.mm
  clang/test/Analysis/stack-capture-leak-no-arc.mm

Index: clang/test/Analysis/stack-capture-leak-no-arc.mm
===
--- clang/test/Analysis/stack-capture-leak-no-arc.mm
+++ clang/test/Analysis/stack-capture-leak-no-arc.mm
@@ -4,6 +4,7 @@
 typedef void (^dispatch_block_t)(void);
 void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 extern dispatch_queue_t queue;
+void f(int);
 
 void test_block_inside_block_async_no_leak() {
   int x = 123;
@@ -35,3 +36,46 @@
   return outer; // expected-warning-re{{Address of stack-allocated block declared on line {{.+}} is captured by a returned block}}
 }
 
+// The block literal defined in this function could leak once being
+// called.
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+}
+
+// The block literal captures nothing thus is treated as a constant.
+void output_constant_block(dispatch_block_t * blk) {
+  *blk = ^{ };
+}
+
+// A block can leak if it captures at least one variable and is not
+// under ARC when its' stack frame expires.
+void test_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+  f(x);
+};
+  };
+
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// A block captures nothing is a constant thus never leaks.
+void test_constant_block_no_leak() {
+  __block dispatch_block_t blk;
+  dispatch_block_t p = ^{
+blk = ^{
+  f(0);
+};
+  };
+  
+  p();
+  blk();
+  output_constant_block(&blk);
+  blk();
+}
Index: clang/test/Analysis/stack-capture-leak-arc.mm
===
--- clang/test/Analysis/stack-capture-leak-arc.mm
+++ clang/test/Analysis/stack-capture-leak-arc.mm
@@ -8,6 +8,7 @@
 typedef long dispatch_time_t;
 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
 void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
+void f(int);
 
 extern dispatch_queue_t queue;
 extern dispatch_once_t *predicate;
@@ -187,3 +188,40 @@
   }
   dispatch_barrier_sync(queue, ^{});
 }
+
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); };
+}
+
+// Block objects themselves can never leak under ARC.
+void test_no_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+blk = ^{
+  f(x);
+};
+  };
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// Block objects do not leak under ARC but stack variables of
+// non-object kind indirectly referred by a block can leak.
+dispatch_block_t test_block_referencing_variable_leak() {
+  int x = 0;
+  __block int * p = &x;
+  __block int * q = &x;
+  
+  dispatch_async(queue, ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by an asynchronously-executed block \
+[alpha.core.StackAddressAsyncEscape]}}
+  ++(*p);
+});
+  return (dispatch_block_t) ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by a returned block \
+[core.StackAddressEscape]}}
+++(*q);
+  };
+}
Index: clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -310,6 +310,9 @@
 /// referred by an other stack variable from different st

[PATCH] D129398: [ASTMatchers] Add a new matcher for callee declarations of Obj-C message expressions

2022-07-11 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3853
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {

aaron.ballman wrote:
> Is there a reason why we want a separate matcher here instead of overloading 
> `callee()`?
This is a good question!  

`callee` has already been overloaded to accept both `Matcher` and 
`Matcher` as the parameter.   In my case, I will need `callee` to be 
polymorphic in returning both `Matcher` and 
`Matcher` types.   So that will end up in 4 definitions of 
`callee`, one of which has different return type and parameter type from one of 
the others.   

Is this achievable?  I know I can overload parameters or make return types 
polymorphic, but can I mix them together? 


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129398

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


[PATCH] D129398: [ASTMatchers] Add a new matcher for callee declarations of Obj-C message expressions

2022-07-11 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3853
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {

ziqingluo-90 wrote:
> aaron.ballman wrote:
> > Is there a reason why we want a separate matcher here instead of 
> > overloading `callee()`?
> This is a good question!  
> 
> `callee` has already been overloaded to accept both `Matcher` and 
> `Matcher` as the parameter.   In my case, I will need `callee` to be 
> polymorphic in returning both `Matcher` and 
> `Matcher` types.   So that will end up in 4 definitions of 
> `callee`, one of which has different return type and parameter type from one 
> of the others.   
> 
> Is this achievable?  I know I can overload parameters or make return types 
> polymorphic, but can I mix them together? 
I figured it out.  I will soon update this patch to simply overload `callee`


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129398

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


[PATCH] D129398: [ASTMatchers] Add a new matcher for callee declarations of Obj-C message expressions

2022-07-12 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 444032.
ziqingluo-90 added a comment.

Taking @aaron.ballman 's advice to overload `callee` instead of creating a new 
matcher.  Avoid to bloat `ASTMatchers.h`.

Sorry to the reviewers that have to review this patch again.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129398

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -2307,6 +2307,45 @@
   hasName("cc"), hasInitializer(integerLiteral(equals(1));
 }
 
+TEST(ASTMatchersTestObjC, ObjCMessageCalees) {
+  StatementMatcher MessagingFoo =
+  objcMessageExpr(callee(objcMethodDecl(hasName("foo";
+
+  EXPECT_TRUE(matchesObjC("@interface I"
+  "+ (void)foo;"
+  "@end\n"
+  "int main() {"
+  "  [I foo];"
+  "}",
+  MessagingFoo));
+  EXPECT_TRUE(notMatchesObjC("@interface I"
+ "+ (void)foo;"
+ "+ (void)bar;"
+ "@end\n"
+ "int main() {"
+ "  [I bar];"
+ "}",
+ MessagingFoo));
+  EXPECT_TRUE(matchesObjC("@interface I"
+  "- (void)foo;"
+  "- (void)bar;"
+  "@end\n"
+  "int main() {"
+  "  I *i;"
+  "  [i foo];"
+  "}",
+  MessagingFoo));
+  EXPECT_TRUE(notMatchesObjC("@interface I"
+ "- (void)foo;"
+ "- (void)bar;"
+ "@end\n"
+ "int main() {"
+ "  I *i;"
+ "  [i bar];"
+ "}",
+ MessagingFoo));
+}
+
 TEST(ASTMatchersTestObjC, ObjCMessageExpr) {
   // Don't find ObjCMessageExpr where none are present.
   EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything(;
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,8 +3838,9 @@
   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 @@
 ///   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))
+.matches(Node, Finder, Builder);
+  else {
+// The dynamic cast below is guaranteed to succeed as there are only 2
+// supported return types.
+const auto *MsgNode = dyn_cast(&Node);
+const Decl *DeclNode = MsgNode->getMethodDecl();
+return (DeclNode != nullptr &&
+InnerMatcher.matches(*DeclNode, Finder, Builder));
+  }
 }
 
 /// Matches if the expression's or declaration's type matches a type
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -7255,14 +7255,24 @@
 
 
 
-MatcherCallExpr>calleeMatcherDecl> InnerMatcher
-Matches if the call expression's callee's declaration matches the
-given matcher.
+Ma

[PATCH] D129398: [ASTMatchers] Add a new matcher for callee declarations of Obj-C message expressions

2022-07-13 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 49.
ziqingluo-90 added a comment.

Added a test to `unittests/ASTMatchers/Dynamic/RegistryTest.cpp` and confirms 
that the overloaded `callee` still works with dynamic matchers


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

https://reviews.llvm.org/D129398

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

Index: clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
===
--- clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -198,13 +198,32 @@
constructMatcher("hasName", StringRef("x")
   .getTypedMatcher();
 
+  Matcher ObjCMsgExpr =
+  constructMatcher(
+  "objcMessageExpr",
+  constructMatcher(
+  "callee",
+  constructMatcher("objcMethodDecl",
+   constructMatcher("hasName", StringRef("x")
+  .getTypedMatcher();
+
   std::string Code = "class Y { public: void x(); }; void z() { Y y; y.x(); }";
   EXPECT_FALSE(matches(Code, CallExpr0));
   EXPECT_TRUE(matches(Code, CallExpr1));
+  EXPECT_FALSE(matches(Code, ObjCMsgExpr));
 
   Code = "class Z { public: void z() { this->z(); } };";
   EXPECT_TRUE(matches(Code, CallExpr0));
   EXPECT_FALSE(matches(Code, CallExpr1));
+  EXPECT_FALSE(matches(Code, ObjCMsgExpr));
+
+  Code = "@interface I "
+ "+ (void)x; "
+ "@end\n"
+ "int main() { [I x]; }";
+  EXPECT_FALSE(matchesObjC(Code, CallExpr0));
+  EXPECT_FALSE(matchesObjC(Code, CallExpr1));
+  EXPECT_TRUE(matchesObjC(Code, ObjCMsgExpr));
 
   Matcher DeclDecl = declaratorDecl(hasTypeLoc(
   constructMatcher(
Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -2307,6 +2307,45 @@
   hasName("cc"), hasInitializer(integerLiteral(equals(1));
 }
 
+TEST(ASTMatchersTestObjC, ObjCMessageCalees) {
+  StatementMatcher MessagingFoo =
+  objcMessageExpr(callee(objcMethodDecl(hasName("foo";
+
+  EXPECT_TRUE(matchesObjC("@interface I"
+  "+ (void)foo;"
+  "@end\n"
+  "int main() {"
+  "  [I foo];"
+  "}",
+  MessagingFoo));
+  EXPECT_TRUE(notMatchesObjC("@interface I"
+ "+ (void)foo;"
+ "+ (void)bar;"
+ "@end\n"
+ "int main() {"
+ "  [I bar];"
+ "}",
+ MessagingFoo));
+  EXPECT_TRUE(matchesObjC("@interface I"
+  "- (void)foo;"
+  "- (void)bar;"
+  "@end\n"
+  "int main() {"
+  "  I *i;"
+  "  [i foo];"
+  "}",
+  MessagingFoo));
+  EXPECT_TRUE(notMatchesObjC("@interface I"
+ "- (void)foo;"
+ "- (void)bar;"
+ "@end\n"
+ "int main() {"
+ "  I *i;"
+ "  [i bar];"
+ "}",
+ MessagingFoo));
+}
+
 TEST(ASTMatchersTestObjC, ObjCMessageExpr) {
   // Don't find ObjCMessageExpr where none are present.
   EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything(;
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,8 +3838,9 @@
   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 @@
 ///   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 fo

[PATCH] D129398: [ASTMatchers] Add a new matcher for callee declarations of Obj-C message expressions

2022-07-15 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

Thanks @aaron.ballman ,  I plan to commit this patch on Monday (18th July 2022).


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

https://reviews.llvm.org/D129398

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


[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-07-15 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

In D128401#3656559 , @NoQ wrote:

> Looks great, thanks!
>
> Yes, I think refactoring can be done in a follow-up patch.

Thank you, @NoQ!

Let me politely ping the rest of the commenters: @LegalizeAdulthood, 
@gribozavr2, @Eugene.Zelenko.  Is this patch looking good to you?


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

https://reviews.llvm.org/D128401

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


[PATCH] D129398: [ASTMatchers] Add a new matcher for callee declarations of Obj-C message expressions

2022-07-21 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb17baa1db613: [ASTMatchers] Adding a new matcher for callee 
declarations of Obj-C (authored by ziqingluo-90).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D129398

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

Index: clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
===
--- clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -198,13 +198,32 @@
constructMatcher("hasName", StringRef("x")
   .getTypedMatcher();
 
+  Matcher ObjCMsgExpr =
+  constructMatcher(
+  "objcMessageExpr",
+  constructMatcher(
+  "callee",
+  constructMatcher("objcMethodDecl",
+   constructMatcher("hasName", StringRef("x")
+  .getTypedMatcher();
+
   std::string Code = "class Y { public: void x(); }; void z() { Y y; y.x(); }";
   EXPECT_FALSE(matches(Code, CallExpr0));
   EXPECT_TRUE(matches(Code, CallExpr1));
+  EXPECT_FALSE(matches(Code, ObjCMsgExpr));
 
   Code = "class Z { public: void z() { this->z(); } };";
   EXPECT_TRUE(matches(Code, CallExpr0));
   EXPECT_FALSE(matches(Code, CallExpr1));
+  EXPECT_FALSE(matches(Code, ObjCMsgExpr));
+
+  Code = "@interface I "
+ "+ (void)x; "
+ "@end\n"
+ "int main() { [I x]; }";
+  EXPECT_FALSE(matchesObjC(Code, CallExpr0));
+  EXPECT_FALSE(matchesObjC(Code, CallExpr1));
+  EXPECT_TRUE(matchesObjC(Code, ObjCMsgExpr));
 
   Matcher DeclDecl = declaratorDecl(hasTypeLoc(
   constructMatcher(
Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -2307,6 +2307,45 @@
   hasName("cc"), hasInitializer(integerLiteral(equals(1));
 }
 
+TEST(ASTMatchersTestObjC, ObjCMessageCalees) {
+  StatementMatcher MessagingFoo =
+  objcMessageExpr(callee(objcMethodDecl(hasName("foo";
+
+  EXPECT_TRUE(matchesObjC("@interface I"
+  "+ (void)foo;"
+  "@end\n"
+  "int main() {"
+  "  [I foo];"
+  "}",
+  MessagingFoo));
+  EXPECT_TRUE(notMatchesObjC("@interface I"
+ "+ (void)foo;"
+ "+ (void)bar;"
+ "@end\n"
+ "int main() {"
+ "  [I bar];"
+ "}",
+ MessagingFoo));
+  EXPECT_TRUE(matchesObjC("@interface I"
+  "- (void)foo;"
+  "- (void)bar;"
+  "@end\n"
+  "int main() {"
+  "  I *i;"
+  "  [i foo];"
+  "}",
+  MessagingFoo));
+  EXPECT_TRUE(notMatchesObjC("@interface I"
+ "- (void)foo;"
+ "- (void)bar;"
+ "@end\n"
+ "int main() {"
+ "  I *i;"
+ "  [i bar];"
+ "}",
+ MessagingFoo));
+}
+
 TEST(ASTMatchersTestObjC, ObjCMessageExpr) {
   // Don't find ObjCMessageExpr where none are present.
   EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything(;
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3838,8 +3838,9 @@
   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 @@
 ///   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);
+///
+/// Exampl

[PATCH] D158561: [-Wunsafe-buffer-usage] Add AST info to the unclaimed DRE debug notes for analysis

2023-10-20 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa4323586fcbb: [-Wunsafe-buffer-usage] Add AST info to the 
unclaimed DRE debug notes for… (authored by ziqingluo-90).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D158561

Files:
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
  
clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
  clang/utils/analyze_safe_buffer_debug_notes.py

Index: clang/utils/analyze_safe_buffer_debug_notes.py
===
--- /dev/null
+++ clang/utils/analyze_safe_buffer_debug_notes.py
@@ -0,0 +1,39 @@
+import sys
+from collections import OrderedDict
+
+class Trie:
+def __init__(self, name):
+self.name = name
+self.children = OrderedDict()
+self.count = 1
+
+def add(self, name):
+if name in self.children:
+self.children[name].count += 1
+else:
+self.children[name] = Trie(name)
+return self.children[name]
+
+def print(self, depth):
+if depth > 0:
+print('|', end="")
+for i in range(depth):
+print('-', end="")
+if depth > 0:
+print(end=" ")
+print(self.name, '#', self.count)
+for key, child in self.children.items():
+child.print(depth + 1)
+
+
+Root = Trie("Root")
+
+if __name__ == "__main__":
+for line in sys.stdin:
+words = line.split('==>')
+words = [word.strip() for word in words]
+MyTrie = Root;
+for word in words:
+MyTrie = MyTrie.add(word)
+
+Root.print(0)
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \
+// RUN:-mllvm -debug-only=SafeBuffers \
+// RUN:-std=c++20 -verify=expected %s
+
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \
+// RUN:-mllvm -debug-only=SafeBuffers \
+// RUN:-std=c++20 %s  \
+// RUN:2>&1 | grep 'The unclaimed DRE trace:' \
+// RUN: | sed 's/^The unclaimed DRE trace://' \
+// RUN: | %analyze_safe_buffer_debug_notes \
+// RUN: | FileCheck %s
+
+// This debugging facility is only available in debug builds.
+//
+// REQUIRES: asserts
+// REQUIRES: shell
+
+void test_unclaimed_use(int *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  p++;   //  expected-note{{used in pointer arithmetic here}} \
+ expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+ The unclaimed DRE trace: DeclRefExpr, UnaryOperator(++), CompoundStmt}}
+  *((p + 1) + 1); // expected-warning{{unsafe pointer arithmetic}}  \
+ expected-note{{used in pointer arithmetic here}}			\
+		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, ImplicitCastExpr(LValueToRValue), BinaryOperator(+), ParenExpr, BinaryOperator(+), ParenExpr, UnaryOperator(*), CompoundStmt}}
+  p -= 1; // expected-note{{used in pointer arithmetic here}} \
+		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, BinaryOperator(-=), CompoundStmt}}
+  p--;// expected-note{{used in pointer arithmetic here}} \
+ 		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, UnaryOperator(--), CompoundStmt}}
+  p[5] = 5;   // expected-note{{used in buffer access here}}
+}
+
+// CHECK: Root # 1
+// CHECK: |- DeclRefExpr # 4
+// CHECK: |-- UnaryOperator(++) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- ImplicitCastExpr(LValueToRValue) # 1
+// CHECK: |--- BinaryOperator(+) # 1
+// CHECK: | ParenExpr # 1
+// CHECK: |- BinaryOperator(+) # 1
+// CHECK: |-- ParenExpr # 1
+// CHECK: |--- UnaryOperator(*) # 1
+// CHECK: | CompoundStmt # 1
+// CHECK: |-- BinaryOperator(-=) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- UnaryOperator(--) # 1
+// CHECK: |--- CompoundStmt # 1
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
===
--- /dev/null
+++ clang/test/SemaCXX/wa

[PATCH] D131009: [analyzer] Fixing a bug raising false positives of stack block object leaking under ARC

2022-08-16 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp:313-315
+  // Under ARC, blocks are retained and released automatically:
+  if (isArcManagedBlock(Referred, Ctx))
+return false;

NoQ wrote:
> Aha ok, it sounds like we can no longer be sure that the block is on the 
> stack at this point, did I get it right?
> 
> In this case I think it's more productive to have the block's memory space be 
> `UnknownSpaceRegion` from the start, so that it fell through the memory space 
> check, both here and at other call sites of `isArcManagedBlock()` (so it can 
> be removed), and in any other code that relies on memory spaces (so this 
> mistake is never made again).
//" ..., did I get it right?"//  Yes.

This suggestion makes sense to me.  To my understanding, I need to modify the 
symbolic execution engine to address it.  So shall I do it in a new patch?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131009

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


[PATCH] D131009: [analyzer] Fixing a bug raising false positives of stack block object leaking under ARC

2022-08-16 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 453144.
ziqingluo-90 added a comment.

Addressing @NoQ 's comment.  Let the symbolic execution simulator put a block 
object in an `UnknownSpaceRegion` from the start if ARC is enabled.  Because in 
such cases whether the block is on stack or in heap depends on the 
implementation.  And, those blocks will be moved to the heap when necessary.   
So they will never trigger a stack address leak issue.

Removing those ARC checking in `StackAddrEscapeChecker` since they are no 
longer needed.


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

https://reviews.llvm.org/D131009

Files:
  clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
  clang/lib/StaticAnalyzer/Core/MemRegion.cpp
  clang/test/Analysis/stack-capture-leak-arc.mm
  clang/test/Analysis/stack-capture-leak-no-arc.mm

Index: clang/test/Analysis/stack-capture-leak-no-arc.mm
===
--- clang/test/Analysis/stack-capture-leak-no-arc.mm
+++ clang/test/Analysis/stack-capture-leak-no-arc.mm
@@ -4,6 +4,7 @@
 typedef void (^dispatch_block_t)(void);
 void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 extern dispatch_queue_t queue;
+void f(int);
 
 void test_block_inside_block_async_no_leak() {
   int x = 123;
@@ -35,3 +36,46 @@
   return outer; // expected-warning-re{{Address of stack-allocated block declared on line {{.+}} is captured by a returned block}}
 }
 
+// The block literal defined in this function could leak once being
+// called.
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+}
+
+// The block literal captures nothing thus is treated as a constant.
+void output_constant_block(dispatch_block_t * blk) {
+  *blk = ^{ };
+}
+
+// A block can leak if it captures at least one variable and is not
+// under ARC when its' stack frame expires.
+void test_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+  f(x);
+};
+  };
+
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// A block captures nothing is a constant thus never leaks.
+void test_constant_block_no_leak() {
+  __block dispatch_block_t blk;
+  dispatch_block_t p = ^{
+blk = ^{
+  f(0);
+};
+  };
+  
+  p();
+  blk();
+  output_constant_block(&blk);
+  blk();
+}
Index: clang/test/Analysis/stack-capture-leak-arc.mm
===
--- clang/test/Analysis/stack-capture-leak-arc.mm
+++ clang/test/Analysis/stack-capture-leak-arc.mm
@@ -8,6 +8,7 @@
 typedef long dispatch_time_t;
 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
 void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
+void f(int);
 
 extern dispatch_queue_t queue;
 extern dispatch_once_t *predicate;
@@ -187,3 +188,40 @@
   }
   dispatch_barrier_sync(queue, ^{});
 }
+
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); };
+}
+
+// Block objects themselves can never leak under ARC.
+void test_no_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+blk = ^{
+  f(x);
+};
+  };
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// Block objects do not leak under ARC but stack variables of
+// non-object kind indirectly referred by a block can leak.
+dispatch_block_t test_block_referencing_variable_leak() {
+  int x = 0;
+  __block int * p = &x;
+  __block int * q = &x;
+  
+  dispatch_async(queue, ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by an asynchronously-executed block \
+[alpha.core.StackAddressAsyncEscape]}}
+  ++(*p);
+});
+  return (dispatch_block_t) ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by a returned block \
+[core.StackAddressEscape]}}
+++(*q);
+  };
+}
Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp
===
--- clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1076,14 +1076,18 @@
 sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
   }
   else {
-if (LC) {
+bool IsArcManagedBlock = BD->getASTContext().getLangOpts().ObjCAutoRefCount;
+
+// ARC managed blocks can be initialized on stack or directly in heap
+// depending on the implementations.  So we initialize them with
+// UnknownRegion

[PATCH] D131009: [analyzer] Fixing a bug raising false positives of stack block object leaking under ARC

2022-08-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

In D131009#3744416 , @NoQ wrote:

> Aha perfect, now the entire static analyzer knows how to work with these 
> regions!
>
> I have one tiny remark and I think we can commit.

Thank you!  I will make the change then directly commit.


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

https://reviews.llvm.org/D131009

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


[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-08-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp:194
+  }
+  return ContainsFunc && Overlap;
+}

njames93 wrote:
> This will always be false as if it's true, the loop would return.
Thanks for pointing this out.  I will make the change before commit!


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

https://reviews.llvm.org/D128401

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


[PATCH] D131009: [analyzer] Fixing a bug raising false positives of stack block object leaking under ARC

2022-08-26 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa5e354ec4da1: [analyzer] Fixing a bug raising false 
positives of stack block object (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D131009?vs=453144&id=455992#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131009

Files:
  clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
  clang/lib/StaticAnalyzer/Core/MemRegion.cpp
  clang/test/Analysis/stack-capture-leak-arc.mm
  clang/test/Analysis/stack-capture-leak-no-arc.mm

Index: clang/test/Analysis/stack-capture-leak-no-arc.mm
===
--- clang/test/Analysis/stack-capture-leak-no-arc.mm
+++ clang/test/Analysis/stack-capture-leak-no-arc.mm
@@ -4,6 +4,7 @@
 typedef void (^dispatch_block_t)(void);
 void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 extern dispatch_queue_t queue;
+void f(int);
 
 void test_block_inside_block_async_no_leak() {
   int x = 123;
@@ -35,3 +36,46 @@
   return outer; // expected-warning-re{{Address of stack-allocated block declared on line {{.+}} is captured by a returned block}}
 }
 
+// The block literal defined in this function could leak once being
+// called.
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+}
+
+// The block literal captures nothing thus is treated as a constant.
+void output_constant_block(dispatch_block_t * blk) {
+  *blk = ^{ };
+}
+
+// A block can leak if it captures at least one variable and is not
+// under ARC when its' stack frame expires.
+void test_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+  f(x);
+};
+  };
+
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// A block captures nothing is a constant thus never leaks.
+void test_constant_block_no_leak() {
+  __block dispatch_block_t blk;
+  dispatch_block_t p = ^{
+blk = ^{
+  f(0);
+};
+  };
+  
+  p();
+  blk();
+  output_constant_block(&blk);
+  blk();
+}
Index: clang/test/Analysis/stack-capture-leak-arc.mm
===
--- clang/test/Analysis/stack-capture-leak-arc.mm
+++ clang/test/Analysis/stack-capture-leak-arc.mm
@@ -8,6 +8,7 @@
 typedef long dispatch_time_t;
 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
 void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
+void f(int);
 
 extern dispatch_queue_t queue;
 extern dispatch_once_t *predicate;
@@ -187,3 +188,40 @@
   }
   dispatch_barrier_sync(queue, ^{});
 }
+
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); };
+}
+
+// Block objects themselves can never leak under ARC.
+void test_no_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+blk = ^{
+  f(x);
+};
+  };
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// Block objects do not leak under ARC but stack variables of
+// non-object kind indirectly referred by a block can leak.
+dispatch_block_t test_block_referencing_variable_leak() {
+  int x = 0;
+  __block int * p = &x;
+  __block int * q = &x;
+  
+  dispatch_async(queue, ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by an asynchronously-executed block \
+[alpha.core.StackAddressAsyncEscape]}}
+  ++(*p);
+});
+  return (dispatch_block_t) ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by a returned block \
+[core.StackAddressEscape]}}
+++(*q);
+  };
+}
Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp
===
--- clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1076,14 +1076,18 @@
 sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
   }
   else {
-if (LC) {
+bool IsArcManagedBlock = Ctx.getLangOpts().ObjCAutoRefCount;
+
+// ARC managed blocks can be initialized on stack or directly in heap
+// depending on the implementations.  So we initialize them with
+// UnknownRegion.
+if (!IsArcManagedBlock && LC) {
   // FIXME: Once we implement scope handling, we want the parent region
   // to be the scope.
   const StackFrameContext *STC = LC->getStackFrame();
   assert(STC);
 

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-08-26 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rG9343ec861a2e: [clang-tidy] Adding the missing handling of 
"noreturn" attributes for Obj-C… (authored by ziqingluo-90).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fexceptions
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc -fexceptions
+
+@interface I
++ (void)foo;
++ (void)bar;
++ (void)baz __attribute__((noreturn));
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  int a[10];
+
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i + j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone-infinite-loop]
+[I foo];
+  }
+  while (i + j < 10) {
+[I foo];
+[I baz]; // no warning, class method baz has noreturn attribute
+  }
+
+  void (^block)() = ^{
+  };
+  void __attribute__((noreturn)) (^block_nr)(void) = ^void __attribute__((noreturn)) (void) { throw "err"; };
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+block();
+  }
+  while (i < 10) {
+// no warning, the block has "noreturn" arribute
+block_nr();
+  }
+}
+
+@implementation I
++ (void)bar {
+}
+
++ (void)foo {
+  static int i = 0;
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I bar];
+  }
+}
+@end
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -16,15 +16,35 @@
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
 
 namespace clang {
+namespace ast_matchers {
+/// matches a Decl if it has a  "no return" attribute of any kind
+AST_MATCHER(Decl, declHasNoReturnAttr) {
+  return Node.hasAttr() || Node.hasAttr() ||
+ Node.hasAttr();
+}
+
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {
+  return Node.getNoReturnAttr();
+}
+} // namespace ast_matchers
 namespace tidy {
 namespace bugprone {
 
 static internal::Matcher
 loopEndingStmt(internal::Matcher Internal) {
-  // FIXME: Cover noreturn ObjC methods (and blocks?).
+  internal::Matcher isNoReturnFunType =
+  ignoringParens(functionType(typeHasNoReturnAttr()));
+  internal::Matcher isNoReturnDecl =
+  anyOf(declHasNoReturnAttr(), functionDecl(hasType(isNoReturnFunType)),
+varDecl(hasType(blockPointerType(pointee(isNoReturnFunType);
+
   return stmt(anyOf(
   mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
-  callExpr(Internal, callee(functionDecl(isNoReturn());
+  callExpr(Internal,
+   callee(mapAnyOf(functionDecl, /* block callee */ varDecl)
+  .with(isNoReturnDecl))),
+  objcMessageExpr(Internal, callee(isNoReturnDecl;
 }
 
 /// Return whether `Var` was changed in `LoopStmt`.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D135690: [ASTMatchers] Add matcher for functions that are effectively inline

2022-10-11 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added reviewers: aaron.ballman, njames93, t-rasmud.
ziqingluo-90 added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchersMacros.h:96
   namespace internal { 
\
-  class matcher_##DefineMatcher##Matcher   
\
+  class matcher_##DefineMatcher##Matcher final 
\
   : public ::clang::ast_matchers::internal::MatcherInterface {   
\

I was wondering if this change is necessary.  This definition is so general 
that it could affect a massive matchers.  So any change to it should be very 
careful and unnecessary changes may be avoided.

Other than that, this patch looks good to me.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D135690

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


[PATCH] D138329: [-Wunsafe-buffer-usage] Add a new recursive matcher to replace `forEachDescendant` in unsafe buffer check

2022-12-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 484136.
ziqingluo-90 added a comment.

Did a rebase and addressed comments.


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

https://reviews.llvm.org/D138329

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -include %s -verify %s
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fblocks -include %s -verify %s
 #ifndef INCLUDED
 #define INCLUDED
 #pragma clang system_header
@@ -141,15 +141,15 @@
 
 int garray[10];
 int * gp = garray;
-int gvar = gp[1];  // TODO: this is not warned
+int gvar = gp[1];  // FIXME: file scope unsafe buffer access is not warned
 
 void testLambdaCaptureAndGlobal(int * p) {
   int a[10];
 
   auto Lam = [p, a]() {
-return p[1] // expected-warning2{{unchecked operation on raw buffer in expression}}
+return p[1] // expected-warning{{unchecked operation on raw buffer in expression}}
   + a[1] + garray[1]
-  + gp[1];  // expected-warning2{{unchecked operation on raw buffer in expression}}
+  + gp[1];  // expected-warning{{unchecked operation on raw buffer in expression}}
   };
 }
 
@@ -187,4 +187,66 @@
   foo(S.*p,
   (S.*q)[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
 }
+
+// test that nested callable definitions are scanned only once
+void testNestedCallableDefinition(int * p) {
+  class A {
+void inner(int * p) {
+  p++; // expected-warning{{unchecked operation on raw buffer in expression}}
+}
+
+static void innerStatic(int * p) {
+  p++; // expected-warning{{unchecked operation on raw buffer in expression}}
+}
+
+void innerInner(int * p) {
+  auto Lam = [p]() {
+int * q = p;
+q++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+return *q;
+  };
+}
+  };
+
+  auto Lam = [p]() {
+int * q = p;
+q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+return *q;
+  };
+
+  auto LamLam = [p]() {
+auto Lam = [p]() {
+  int * q = p;
+  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  return *q;
+};
+  };
+
+  void (^Blk)(int*) = ^(int *p) {
+p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+  };
+
+  void (^BlkBlk)(int*) = ^(int *p) {
+void (^Blk)(int*) = ^(int *p) {
+  p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+};
+Blk(p);
+  };
+
+  // lambda and block as call arguments...
+  foo( [p]() { int * q = p;
+  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  return *q;
+   },
+   ^(int *p) { p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+   }
+ );
+}
+
+void testVariableDecls(int * p) {
+  int * q = p++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  int a[p[1]];// expected-warning{{unchecked operation on raw buffer in expression}}
+  int b = p[1];   // expected-warning{{unchecked operation on raw buffer in expression}}
+}
+
 #endif
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ 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 different 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 different 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(cons

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

2022-12-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 484138.
ziqingluo-90 added a comment.

Did a rebase and addressed the comment.


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

https://reviews.llvm.org/D139233

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -169,10 +169,36 @@
   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-warning2{{unchecked operation on raw buffer in expression}}
+  *q += 1; *q -= 1; // expected-warning2{{unchecked operation on raw buffer in expression}}
+  y += 1; y -= 1;   // expected-warning2{{unchecked operation on raw buffer in expression}}
+  x += 1; x -= 1;   // expected-warning2{{unchecked operation on raw buffer in expression}}
+}
+
 void testTemplate(int * p) {
   int *a[10];
   foo(f(p, &p, a, a)[1]); // expected-warning{{unchecked operation on raw buffer in expression}}, \
  expected-note{{in instantiation of function template specialization 'f' requested here}}
+
+  const int **q = const_cast(&p);
+
+  testPointerArithmetic(p, q, p); //expected-note{{in instantiation of function template specialization 'testPointerArithmetic' requested here}}
 }
 
 void testPointerToMember() {
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -333,6 +333,55 @@
 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 {
Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
===
--- clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -29,6 +29,7 @@
 UNSAFE_GADGET(Decrement)
 UNSAFE_GADGET(ArraySubscript)
 UNSAFE_GADGET(UnsafeBufferUsageAttr)
+UNSAFE_GADGET(PointerArithmetic)
 
 #undef SA

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2022-12-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 484156.
ziqingluo-90 added a comment.

did a rebase


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -67,21 +67,21 @@
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
 
-  auto ap1 = a;
+  auto ap1 = a; // expected-warning{{'ap1' participates in unchecked buffer operations}}
 
-  foo(ap1[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap1[1]);
 
-  auto ap2 = p;
+  auto ap2 = p; // expected-warning{{'ap2' participates in unchecked buffer operations}}
 
-  foo(ap2[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap2[1]);
 
-  auto ap3 = pp;
+  auto ap3 = pp; // expected-warning{{'ap3' participates in unchecked buffer operations}}
 
-  foo(ap3[1][1]); // expected-warning2{{unchecked operation on raw buffer in expression}}
+  foo(ap3[1][1]); // expected-warning{{unchecked operation on raw buffer in expression}}
 
-  auto ap4 = *pp;
+  auto ap4 = *pp; // expected-warning{{'ap4' participates in unchecked buffer operations}}
 
-  foo(ap4[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap4[1]);
 }
 
 void testUnevaluatedContext(int * p) {
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+
+void foo(...);
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+// CHECK: std::span p{new int [10], 10};
+// CHECK: p[5] = 5;
+  int *p = new int[10]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  p[5] = 5;
+
+// CHECK: std::span q{new int [10], 10};
+// CHECK: std::span x{new int [10], 10};
+// CHECK: std::span y{new int, 1};
+// CHECK: std::span z{new int [10], 10};
+// CHECK: std::span w{new Int_t [10], 10};
+// CHECK: foo(q[5], x[5], y[5], z[5], w[5]);
+  const int *q = new int[10]; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  Int_ptr_t x = new int[10];  // expected-warning{{variable 'x' participates in unchecked buffer operations}}
+  Int_ptr_t y = new int;  // expected-warning{{variable 'y' participates in unchecked buffer operations}}
+  Int_t * z = new int[10];// expected-warning{{variable 'z' participates in unchecked buffer operations}}
+  Int_t * w = new Int_t[10];  // expected-warning{{variable 'w' participates in unchecked buffer operations}}
+  foo(q[5], x[5], y[5], z[5], w[5]);
+  // y[5] will crash after being span
+}
+
+void local_array_subscript_auto() {
+// CHECK: std::span p{new int [10], 10};
+// CHECK: p[5] = 5;
+  auto p = new int[10]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  p[5] = 5;
+}
+
+void local_array_subscript_variable_extent() {
+  int n = 10;
+// CHECK: std::span p{new int [n], n};
+// CHECK: std::span q{new int [n++], ...};
+// CHECK: foo(p[5], q[5]);
+  int *p = new int[n]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  int *q = new int[n++]; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  foo(p[5], q[5]);
+}
+
+
+void local_ptr_to_array() {
+  int n = 10;
+  int a[10];
+  int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+// CHECK: std::span p{a, 10};
+// CHECK: std::span q{b, ...};
+// CHECK: foo(p[5], q[5]);
+  int *p = a; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  int *q = b; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  foo(p[5], q[5]);
+}
+
+void local_ptr_addrof_init() {
+  int a[10];
+  int var;
+// CHECK: std::span p{&a, 1};
+// CHECK: std::span q{&var, 1};
+// CHECK: foo(p[5], q[5]);
+  int (*p)[10] = &a; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  int * q = &var; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  // These two expressions involve unsafe buffer accesses, which will
+  // crash at runtime after applying the fix-it,

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2022-12-20 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 484412.
ziqingluo-90 added a comment.

To follow LLVM's convention that global variables better have types that do NOT 
require construction, I change the type of the global variable from 
`std::string` to `constexpr const char * const`.


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -67,21 +67,21 @@
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
 
-  auto ap1 = a;
+  auto ap1 = a; // expected-warning{{'ap1' participates in unchecked buffer operations}}
 
-  foo(ap1[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap1[1]);
 
-  auto ap2 = p;
+  auto ap2 = p; // expected-warning{{'ap2' participates in unchecked buffer operations}}
 
-  foo(ap2[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap2[1]);
 
-  auto ap3 = pp;
+  auto ap3 = pp; // expected-warning{{'ap3' participates in unchecked buffer operations}}
 
-  foo(ap3[1][1]); // expected-warning2{{unchecked operation on raw buffer in expression}}
+  foo(ap3[1][1]); // expected-warning{{unchecked operation on raw buffer in expression}}
 
-  auto ap4 = *pp;
+  auto ap4 = *pp; // expected-warning{{'ap4' participates in unchecked buffer operations}}
 
-  foo(ap4[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap4[1]);
 }
 
 void testUnevaluatedContext(int * p) {
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+
+void foo(...);
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+// CHECK: std::span p{new int [10], 10};
+// CHECK: p[5] = 5;
+  int *p = new int[10]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  p[5] = 5;
+
+// CHECK: std::span q{new int [10], 10};
+// CHECK: std::span x{new int [10], 10};
+// CHECK: std::span y{new int, 1};
+// CHECK: std::span z{new int [10], 10};
+// CHECK: std::span w{new Int_t [10], 10};
+// CHECK: foo(q[5], x[5], y[5], z[5], w[5]);
+  const int *q = new int[10]; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  Int_ptr_t x = new int[10];  // expected-warning{{variable 'x' participates in unchecked buffer operations}}
+  Int_ptr_t y = new int;  // expected-warning{{variable 'y' participates in unchecked buffer operations}}
+  Int_t * z = new int[10];// expected-warning{{variable 'z' participates in unchecked buffer operations}}
+  Int_t * w = new Int_t[10];  // expected-warning{{variable 'w' participates in unchecked buffer operations}}
+  foo(q[5], x[5], y[5], z[5], w[5]);
+  // y[5] will crash after being span
+}
+
+void local_array_subscript_auto() {
+// CHECK: std::span p{new int [10], 10};
+// CHECK: p[5] = 5;
+  auto p = new int[10]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  p[5] = 5;
+}
+
+void local_array_subscript_variable_extent() {
+  int n = 10;
+// CHECK: std::span p{new int [n], n};
+// CHECK: std::span q{new int [n++], ...};
+// CHECK: foo(p[5], q[5]);
+  int *p = new int[n]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  int *q = new int[n++]; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  foo(p[5], q[5]);
+}
+
+
+void local_ptr_to_array() {
+  int n = 10;
+  int a[10];
+  int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+// CHECK: std::span p{a, 10};
+// CHECK: std::span q{b, ...};
+// CHECK: foo(p[5], q[5]);
+  int *p = a; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  int *q = b; // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  foo(p[5], q[5]);
+}
+
+void local_ptr_addrof_init() {
+  int a[10];
+  int var;
+// CHECK: std::span p{&a, 1};
+// CHECK: std::span q{&var, 1};
+// CHECK: foo(p[5], q[5]);
+  int (*p)[10] = &a; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  int * q = &var; // expected-wa

[PATCH] D138329: [-Wunsafe-buffer-usage] Add a new recursive matcher to replace `forEachDescendant` in unsafe buffer check

2023-01-04 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb2ac5fd724c4: [-Wunsafe-buffer-usage] Add a new 
`forEachDescendant` matcher that skips… (authored by ziqingluo-90).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D138329

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -include %s -verify %s
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fblocks -include %s -verify %s
 #ifndef INCLUDED
 #define INCLUDED
 #pragma clang system_header
@@ -141,15 +141,15 @@
 
 int garray[10];
 int * gp = garray;
-int gvar = gp[1];  // TODO: this is not warned
+int gvar = gp[1];  // FIXME: file scope unsafe buffer access is not warned
 
 void testLambdaCaptureAndGlobal(int * p) {
   int a[10];
 
   auto Lam = [p, a]() {
-return p[1] // expected-warning2{{unchecked operation on raw buffer in expression}}
+return p[1] // expected-warning{{unchecked operation on raw buffer in expression}}
   + a[1] + garray[1]
-  + gp[1];  // expected-warning2{{unchecked operation on raw buffer in expression}}
+  + gp[1];  // expected-warning{{unchecked operation on raw buffer in expression}}
   };
 }
 
@@ -187,4 +187,66 @@
   foo(S.*p,
   (S.*q)[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
 }
+
+// test that nested callable definitions are scanned only once
+void testNestedCallableDefinition(int * p) {
+  class A {
+void inner(int * p) {
+  p++; // expected-warning{{unchecked operation on raw buffer in expression}}
+}
+
+static void innerStatic(int * p) {
+  p++; // expected-warning{{unchecked operation on raw buffer in expression}}
+}
+
+void innerInner(int * p) {
+  auto Lam = [p]() {
+int * q = p;
+q++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+return *q;
+  };
+}
+  };
+
+  auto Lam = [p]() {
+int * q = p;
+q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+return *q;
+  };
+
+  auto LamLam = [p]() {
+auto Lam = [p]() {
+  int * q = p;
+  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  return *q;
+};
+  };
+
+  void (^Blk)(int*) = ^(int *p) {
+p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+  };
+
+  void (^BlkBlk)(int*) = ^(int *p) {
+void (^Blk)(int*) = ^(int *p) {
+  p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+};
+Blk(p);
+  };
+
+  // lambda and block as call arguments...
+  foo( [p]() { int * q = p;
+  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  return *q;
+   },
+   ^(int *p) { p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+   }
+ );
+}
+
+void testVariableDecls(int * p) {
+  int * q = p++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  int a[p[1]];// expected-warning{{unchecked operation on raw buffer in expression}}
+  int b = p[1];   // expected-warning{{unchecked operation on raw buffer in expression}}
+}
+
 #endif
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ 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 different 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 different 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

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

2023-01-04 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGf84f17c489f7: [-Wunsafe-buffer-usage] Add an unsafe gadget 
for pointer-arithmetic operations (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D139233?vs=484138&id=486435#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D139233

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -169,10 +169,36 @@
   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-warning2{{unchecked operation on raw buffer in expression}}
+  *q += 1; *q -= 1; // expected-warning2{{unchecked operation on raw buffer in expression}}
+  y += 1; y -= 1;   // expected-warning2{{unchecked operation on raw buffer in expression}}
+  x += 1; x -= 1;   // expected-warning2{{unchecked operation on raw buffer in expression}}
+}
+
 void testTemplate(int * p) {
   int *a[10];
   foo(f(p, &p, a, a)[1]); // expected-warning{{unchecked operation on raw buffer in expression}}, \
  expected-note{{in instantiation of function template specialization 'f' requested here}}
+
+  const int **q = const_cast(&p);
+
+  testPointerArithmetic(p, q, p); //expected-note{{in instantiation of function template specialization 'testPointerArithmetic' requested here}}
 }
 
 void testPointerToMember() {
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -304,6 +304,55 @@
 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 {
Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
===
--- clang/include/clang/Analy

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-09 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 487527.
ziqingluo-90 added a comment.

Rebase the patch with respect to the re-architecture: 
https://reviews.llvm.org/D140062.

Since we do not have any `FixableGadget` to trigger fix-its at this point,  I 
let fix-its of local variable declarations always be emitted for the purpose of 
testing.


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -67,21 +67,21 @@
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
 
-  auto ap1 = a;
+  auto ap1 = a; // expected-warning{{'ap1' participates in unchecked buffer operations}}
 
   foo(ap1[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
 
-  auto ap2 = p;
+  auto ap2 = p; // expected-warning{{'ap2' participates in unchecked buffer operations}}
 
   foo(ap2[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
 
-  auto ap3 = pp;
+  auto ap3 = pp; // expected-warning{{'ap3' participates in unchecked buffer operations}}
 
   foo(ap3[1][1]); // expected-warning2{{unchecked operation on raw buffer in expression}}
 
-  auto ap4 = *pp;
+  auto ap4 = *pp; // expected-warning{{'ap4' participates in unchecked buffer operations}}
 
-  foo(ap4[1]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo(ap4[1]);   // expected-warning{{unchecked operation on raw buffer in expression}}
 }
 
 void testUnevaluatedContext(int * p) {
@@ -176,7 +176,7 @@
 template
 void testPointerArithmetic(int * p, const int **q, T * x) {
   int a[10];
-  auto y = &a[0];
+  auto y = &a[0];   // expected-warning{{variable 'y' participates in unchecked buffer operations}}
 
   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}}
@@ -227,23 +227,23 @@
 
 void innerInner(int * p) {
   auto Lam = [p]() {
-int * q = p;
-q++;   // expected-warning{{unchecked operation on raw buffer in expression}}
+int * q = p;   // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+q++;   // expected-warning{{unchecked operation on raw buffer in expression}}
 return *q;
   };
 }
   };
 
   auto Lam = [p]() {
-int * q = p;
-q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+int * q = p;  // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
 return *q;
   };
 
   auto LamLam = [p]() {
 auto Lam = [p]() {
-  int * q = p;
-  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  int * q = p;  // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
   return *q;
 };
   };
@@ -260,8 +260,8 @@
   };
 
   // lambda and block as call arguments...
-  foo( [p]() { int * q = p;
-  q++;  // expected-warning{{unchecked operation on raw buffer in expression}}
+  foo( [p]() { int * q = p;  // expected-warning{{variable 'q' participates in unchecked buffer operations}}
+  q++;   // expected-warning{{unchecked operation on raw buffer in expression}}
   return *q;
},
^(int *p) { p++;   // expected-warning{{unchecked operation on raw buffer in expression}}
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+
+void foo(...);
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+// CHECK: std::span p{new int [10], 10};
+// CHECK: p[5] = 5;
+  int *p = new int[10]; // expected-warning{{variable 'p' participates in unchecked buffer operations}}
+  p[5] = 5; // expected-warning{{unchecked operation on raw buffer in expression}}
+
+// CHECK: std::span q{new int [10], 10};
+// CHECK: std::span x{new int [10], 10};
+// CHECK: std::span y{new int, 1};
+// CHECK: std::span z{new int [10], 1

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

2023-01-09 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a subscriber: mgrang.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Two fix-its conflict if they have overlapping source ranges.  We shall not emit 
conflicting fix-its.
This patch adds a class to find all conflicting fix-its (as well as their 
associated gadgets or declarations).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D141338

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits.cpp
@@ -71,3 +71,22 @@
   // crash at runtime after applying the fix-it,
   foo(p[5], q[5]);  // expected-warning2{{unchecked operation on raw buffer in expression}}
 }
+
+void fixits_conflict() {
+  // no fix-its will be emitted since fixits of `a` and `b` conflict in source locations:
+  // CHECK: int * a = new int[10],
+  // CHECK:   * b = new int[10];
+  // CHECK: foo(a[5], b[5]);
+  int * a = new int[10], // expected-warning{{variable 'a' participates in unchecked buffer operations}} \
+			 expected-warning{{variable 'b' participates in unchecked buffer operations}}
+* b = new int[10];
+
+  foo(a[5], b[5]); // expected-warning2{{unchecked operation on raw buffer in expression}}
+
+
+  // CHECK: std::span c{new int [10], 10};
+  // CHECK: foo(c[5]);
+  int * c = new int[10]; // expected-warning{{variable 'c' participates in unchecked buffer operations}}
+
+  foo(c[5]); // expected-warning{{unchecked operation on raw buffer in expression}}
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -109,8 +109,8 @@
 
 AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher, innerMatcher) {
   const DynTypedMatcher &DTM = static_cast(innerMatcher);
-  
-  MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All);  
+
+  MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All);
   return Visitor.findMatch(DynTypedNode::create(Node));
 }
 } // namespace clang::ast_matchers
@@ -123,6 +123,16 @@
 // Convenience typedef.
 using FixItList = SmallVector;
 
+// To bind each `FixItList` with the `FixableGadget` which it will fix:
+// FIXME: for now declarations are not Gadgets so a FixItList is associated to
+// either a Gadget or a VarDecl (only one of them is significant).
+using GadgetFix =
+std::tuple;
+// naming component indexes of `GadgetFix`:
+constexpr const int GFFixItList = 0;
+// constexpr const int GFGadget = 1;  //unused for now
+constexpr const int GFVarDecl = 2;
+
 // Defined below.
 class Strategy;
 } // namespace
@@ -360,6 +370,115 @@
 } // namespace
 
 namespace {
+// This class represents all fixes generated in one function declaration, and
+// allows to iterate over the non-conflicting subset as well as its'
+// complementary subset (i.e., conflicting subset).  Any fix in the
+// non-conflicting subset does not conflict with any other generated fix.  Two
+// fixes conflict if their source ranges overlap.
+class NonConflictingFixes {
+private:
+  // All Fix-its:
+  const GadgetFix * const FixIts;
+
+  // Number of Fix-its;
+  const int NumFixIts;
+
+  // Indices of Fix-its in `FixIts` that shall not be emitted:
+  BitVector ConflictingFixIts;
+
+public:
+  /// \return the range of the non-conflicting `GadgetFixIt`s
+  auto nonConflictingFixIts() const {
+const GadgetFix *Begin = FixIts;
+const GadgetFix *End = FixIts + NumFixIts;
+const BitVector &BV = ConflictingFixIts;
+
+return llvm::make_filter_range(llvm::make_range(Begin, End),
+   [&BV, Begin](const GadgetFix &Elt) {
+ return !BV.test(&Elt - Begin);
+   });
+  }
+
+  /// \return the range of the conflicting `GadgetFixIt`s
+  auto conflictingFixIts() const {
+const GadgetFix *Begin = FixIts;
+const GadgetFix *End = FixIts + NumFixIts;
+std::vector Result;
+const BitVector &BV = ConflictingFixIts;
+
+return llvm::make_filter_range(
+llvm::make_range(Begin, End),
+[&BV, Begin](const GadgetFix &Elt) { return BV.test(&Elt - Begin); });
+  }
+
+  // The constructor. Populating `ConflictingFixIts` and `ConflictingGroups`:
+  NonConflictingFixes(const SourceManager &SM,
+   const std::vector &&FixIts)
+  : FixIts(FixIts.data()), NumFixIts(FixIts.size()) {
+// To find out all conflicting hints (i.e., `FixItHint`s), we sorts the
+// source ranges of a

[PATCH] D137379: -Wunsafe-buffer-usage: adding warnings for unsafe buffer accesses by array subscript operations

2022-11-03 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, t-rasmud, malavikasamak, jkorous, xazax.hun, 
aaron.ballman, gribozavr2.
Herald added a subscriber: rnkovacs.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Adding an unsafe `Gadget` to represent and search  (via matchers)  for unsafe 
buffer accesses through array subscript operations.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D137379

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -6,3 +6,57 @@
   --p; // expected-warning{{unchecked operation on raw buffer in expression}}
   p--; // expected-warning{{unchecked operation on raw buffer in expression}}
 }
+
+void foo(...);
+
+void * bar(void);
+char * baz(void);
+
+void testArraySubscripts(int *p, int **pp) {
+  foo(p[0], // expected-warning{{unchecked operation on raw buffer in expression}}
+  pp[0][0], // expected-warning2{{unchecked operation on raw buffer in expression}}
+  0[0[pp]], // expected-warning2{{unchecked operation on raw buffer in expression}}
+  0[pp][0]  // expected-warning2{{unchecked operation on raw buffer in expression}}
+  );
+
+  if (p[3]) {   // expected-warning{{unchecked operation on raw buffer in expression}}
+void * q = p;
+
+foo(((int*)q)[10]); // expected-warning{{unchecked operation on raw buffer in expression}}
+  }
+
+  foo(((int*)bar())[3], // expected-warning{{unchecked operation on raw buffer in expression}}
+  3[(int*)bar()],   // expected-warning{{unchecked operation on raw buffer in expression}}
+  baz()[3], // expected-warning{{unchecked operation on raw buffer in expression}}
+  3[baz()]  // expected-warning{{unchecked operation on raw buffer in expression}}
+  );
+
+  int a[10], b[10][10];
+
+  // not to warn subscripts on arrays
+  foo(a[0], a[1],
+  0[a], 1[a],
+  b[3][4],
+  4[b][3],
+  4[3[b]]);
+}
+
+void testArraySubscriptsWithAuto(int *p, int **pp) {
+  int a[10];
+
+  auto ap1 = a;
+
+  foo(ap1[0]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+
+  auto ap2 = p;
+
+  foo(ap2[0]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+
+  auto ap3 = pp;
+
+  foo(pp[0][0]); // expected-warning2{{unchecked operation on raw buffer in expression}}
+
+  auto ap4 = *pp;
+
+  foo(ap4[0]);  // expected-warning{{unchecked operation on raw buffer in expression}}
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -149,6 +149,31 @@
 
   const Stmt *getBaseStmt() const override { return Op; }
 };
+
+/// 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 UnsafeGadget {
+  const ArraySubscriptExpr *ASE;
+
+public:
+  ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
+  : UnsafeGadget(Kind::ArraySubscript),
+ASE(Result.Nodes.getNodeAs("arraySubscr")) {}
+
+  static bool classof(const Gadget *G) {
+return G->getKind() == Kind::ArraySubscript;
+  }
+
+  static Matcher matcher() {
+// FIXME: What if the index is integer literal 0? Should this be
+// a safe gadget in this case?
+return stmt(
+arraySubscriptExpr(hasBase(ignoringParenImpCasts(hasPointerType(
+.bind("arraySubscr"));
+  }
+
+  const Stmt *getBaseStmt() const override { return ASE; }
+};
 } // namespace
 
 // Scan the function and return a list of gadgets found with provided kits.
Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
===
--- clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -20,6 +20,7 @@
 
 UNSAFE_GADGET(Increment)
 UNSAFE_GADGET(Decrement)
+UNSAFE_GADGET(ArraySubscript)
 
 #undef SAFE_GADGET
 #undef UNSAFE_GADGET
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 491917.
ziqingluo-90 added a comment.

Addressed the minor comments


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,96 @@
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+  int tmp;
+// CHECK: std::span p {new int[10], 10};
+// CHECK: std::span q {new int[10], 10};
+// CHECK: tmp = p[5];
+// CHECK: tmp = q[5];
+  int *p = new int[10];
+  const int *q = new int[10];
+  tmp = p[5];
+  tmp = q[5];
+
+// CHECK: std::span x {new int[10], 10};
+// CHECK: std::span y {new int, 1};
+// CHECK: std::span z {new int[10], 10};
+// CHECK: std::span w {new Int_t[10], 10};
+
+  Int_ptr_t x = new int[10];
+  Int_ptr_t y = new int;
+  Int_t * z = new int[10];
+  Int_t * w = new Int_t[10];
+
+  // CHECK: tmp = x[5];
+  tmp = x[5];
+  // CHECK: tmp = y[5];
+  tmp = y[5]; // y[5] will crash after being span
+  // CHECK: tmp = z[5];
+  tmp = z[5];
+  // CHECK: tmp = w[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  int tmp;
+// CHECK: std::span p {new int[10], 10};
+// CHECK: tmp = p[5];
+  auto p = new int[10];
+  tmp = p[5];
+}
+
+void local_array_subscript_variable_extent() {
+  int n = 10;
+  int tmp;
+
+  // CHECK: std::span p {new int[n], n};
+  // CHECK: std::span q {new int[n++], <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = new int[n];
+  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  int *q = new int[n++];
+  tmp = p[5];
+  tmp = q[5];
+}
+
+
+void local_ptr_to_array() {
+  int tmp;
+  int n = 10;
+  int a[10];
+  int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  // CHECK: std::span p {a, 10};
+  // CHECK: std::span q {b, <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = a;
+  int *q = b;
+  tmp = p[5];
+  tmp = q[5];
+}
+
+void local_ptr_addrof_init() {
+  int var;
+// CHECK: std::span q {&var, 1};
+// CHECK: var = q[5];
+  int * q = &var;
+  // This expression involves unsafe buffer accesses, which will crash
+  // at runtime after applying the fix-it,
+  var = q[5];
+}
+
+void decl_without_init() {
+  int tmp;
+  // CHECK: std::span p;
+  int * p;
+  // CHECK: std::span q;
+  Int_ptr_t q;
+
+  tmp = p[5];
+  tmp = q[5];
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ 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,21 @@
   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 auto isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) {
+  auto isLvalueToRvalueCast = [](internal::Matcher M) {
+return implicitCastExpr(hasCastKind(CastKind::CK_LValueToRValue),
+castSubExpr(M));
+  };
+  //FIXME: add assignmentTo context...
+  return isLvalueToRvalueCast(innerMatcher);
+}
 } // namespace clang::ast_matchers
 
 namespace {
@@ -282,7 +298,7 @@
 /// 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 = "arraySubscr";
+  static constexpr const char *const ArraySubscrTag = "ArraySubscript";
   const ArraySubscriptExpr *ASE;
 
 public:
@@ -366,6 +382,47 @@
   // FIXME: pointer adding zero should be fine
   //FIXME: this gadge will need a fix-it
 };
+
+// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
+// Context (see `isInUnspecifiedLvalueContext`).
+// Note here `[]` is the built-in s

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

2023-01-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 491960.
ziqingluo-90 retitled this revision from "[WIP][-Wunsafe-buffer-usage] Add 
unsafe buffer checking opt-out pragmas" to "[-Wunsafe-buffer-usage] Add unsafe 
buffer checking opt-out pragmas".
ziqingluo-90 added reviewers: aaron.ballman, xazax.hun, gribozavr, ymandel, 
sgatev.
ziqingluo-90 added a comment.
Herald added a subscriber: rnkovacs.

Put the implementation of the opt-out pragma completely in Preprocessor.
Add an opt-out pragma test for fix-its.


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

https://reviews.llvm.org/D140179

Files:
  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
  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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h
@@ -0,0 +1,14 @@
+#ifdef _INCLUDE_NO_WARN
+// the snippet will be included in an opt-out region
+p1++;
+
+#undef _INCLUDE_NO_WARN
+
+#elif defined(_INCLUDE_WARN)
+// the snippet will be included in a location where warnings are expected
+p2++; // expected-note{{used in pointer arithmetic here}}
+#undef _INCLUDE_WARN
+
+#else
+
+#endif
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
@@ -0,0 +1,100 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -Wno-unused-value -verify %s
+
+void basic(int * x) {// expected-warning{{'x' is an unsafe pointer used for buffer access}}
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+
+#define _INCLUDE_NO_WARN
+#include "warn-unsafe-buffer-usage-pragma.h" // increment p1 in header
+
+  int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage end
+  p2[5]; //expected-note{{used in buffer access here}}
+  p3[5]; //expected-note{{used in buffer access here}}
+  x++;   //expected-note{{used in pointer arithmetic here}}
+#define _INCLUDE_WARN
+#include "warn-unsafe-buffer-usage-pragma.h" // increment p2 in header
+}
+
+
+void withDiagnosticWarning() {
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+
+  // diagnostics in opt-out region
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic warning "-Weverything"
+  p1[5];  // not to warn expected-warning{{expression result unused}}
+  p2[5];  // not to warn expected-warning{{expression result unused}}
+#pragma clang diagnostic pop
+#pragma clang unsafe_buffer_usage end
+
+  // opt-out region under diagnostic warning
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+#pragma clang diagnostic pop
+
+  p2[5]; // expected-note{{used in buffer access here}}
+}
+
+
+void withDiagnosticIgnore() {
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+  int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic ignored "-Weverything"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic pop
+#pragma clang unsafe_buffer_usage end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+#pragma clang diagnostic pop
+
+  p2[5]; // expected-note{{used in buffer access here}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+#pr

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-27 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 marked 8 inline comments as done.
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:637
 
-static Strategy
-getNaiveStrategy(const llvm::SmallVectorImpl &UnsafeVars) {

NoQ wrote:
> Hmm, did this need to be moved? I don't think you're calling this function 
> from the new code.
it does look like I moved it.  Will change it back.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:780-781
+  } else {
+// In cases `Init` is of the form `&Var` after stripping of implicit
+// casts, where `&` is the built-in operator, the extent is 1.
+if (auto AddrOfExpr = dyn_cast(Init->IgnoreImpCasts()))

NoQ wrote:
> ```lang=c
> int x = 1;
> char *ptr = &x; // std::span ptr { &x, 4 };
> ```
> This is valid code. I suspect we want to check types as well, to see that 
> type sizes match.
> 
> Most of the time code like this violates strict aliasing, but `char` is 
> exceptional, and even if it did violate strict aliasing, people can compile 
> with `-fno-strict-aliasing` to define away the UB, so we have to respect that.
This code is not valid in C++.  An explicit cast is needed in front of `&x`.  I 
will add a test to show that 

```
int x = 1;
char * ptr = (char *)&x;
```
will have a place holder for the span size.


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

https://reviews.llvm.org/D139737

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


[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-27 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 492882.
ziqingluo-90 added a comment.

Addressing comments


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,149 @@
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+  int tmp;
+// CHECK: std::span p {new int[10], 10};
+// CHECK: std::span q {new int[10], 10};
+// CHECK: tmp = p[5];
+// CHECK: tmp = q[5];
+  int *p = new int[10];
+  const int *q = new int[10];
+  tmp = p[5];
+  tmp = q[5];
+
+// CHECK: std::span x {new int[10], 10};
+// CHECK: std::span y {new int, 1};
+// CHECK: std::span z {new int[10], 10};
+// CHECK: std::span w {new Int_t[10], 10};
+
+  Int_ptr_t x = new int[10];
+  Int_ptr_t y = new int;
+  Int_t * z = new int[10];
+  Int_t * w = new Int_t[10];
+
+  // CHECK: tmp = x[5];
+  tmp = x[5];
+  // CHECK: tmp = y[5];
+  tmp = y[5]; // y[5] will crash after being span
+  // CHECK: tmp = z[5];
+  tmp = z[5];
+  // CHECK: tmp = w[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  int tmp;
+// CHECK: std::span p {new int[10], 10};
+// CHECK: tmp = p[5];
+  auto p = new int[10];
+  tmp = p[5];
+}
+
+void local_array_subscript_variable_extent() {
+  int n = 10;
+  int tmp;
+
+  // CHECK: std::span p {new int[n], n};
+  // CHECK: std::span q {new int[n++], <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = new int[n];
+  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  int *q = new int[n++];
+  tmp = p[5];
+  tmp = q[5];
+}
+
+
+void local_ptr_to_array() {
+  int tmp;
+  int n = 10;
+  int a[10];
+  int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  // CHECK: std::span p {a, 10};
+  // CHECK: std::span q {b, <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = a;
+  int *q = b;
+  tmp = p[5];
+  tmp = q[5];
+}
+
+void local_ptr_addrof_init() {
+  int var;
+// CHECK: std::span q {&var, 1};
+// CHECK: var = q[5];
+  int * q = &var;
+  // This expression involves unsafe buffer accesses, which will crash
+  // at runtime after applying the fix-it,
+  var = q[5];
+}
+
+void decl_without_init() {
+  int tmp;
+  // CHECK: std::span p;
+  int * p;
+  // CHECK: std::span q;
+  Int_ptr_t q;
+
+  tmp = p[5];
+  tmp = q[5];
+}
+
+// Explicit casts are required in the following cases. No way to
+// figure out span extent for them automatically.
+void explict_cast() {
+  int tmp;
+  // CHECK: std::span p {(int*) new int[10][10], <# placeholder #>};
+  int * p = (int*) new int[10][10];
+  tmp = p[5];
+
+  int a;
+  // CHECK: std::span q {(char *)&a, <# placeholder #>};
+  char * q = (char *)&a;
+  tmp = (int) q[5];
+
+  void * r = &a;
+  // CHECK: std::span s {(char *) r, <# placeholder #>};
+  char * s = (char *) r;
+  tmp = (int) s[5];
+}
+
+void unsupported_multi_decl(int * x) {
+  // CHECK: int * p = x, * q = new int[10];
+  int * p = x, * q = new int[10];
+  *p = q[5];
+}
+
+void unsupported_fixit_overlapping_macro(int * x) {
+  int tmp;
+  // In the case below, a tentative fix-it replaces `MY_INT * p =` with `std::span p `.
+  // It overlaps with the use of the macro `MY_INT`.  The fix-it is
+  // discarded then.
+#define MY_INT int
+  // CHECK: MY_INT * p = new int[10];
+  MY_INT * p = new int[10];
+  tmp = p[5];
+
+#define MY_VAR(name) int * name
+  // CHECK: MY_VAR(q) = new int[10];
+  MY_VAR(q) = new int[10];
+  tmp = q[5];
+
+  // In cases where fix-its do not change the original code where
+  // macros are used, those fix-its will be emitted.  For example,
+  // fixits are inserted before and after `new MY_INT[MY_TEN]` below.
+#define MY_TEN 10
+  // CHECK: std::span g {new MY_INT[MY_TEN], <# placeholder #>};
+  int * g = new MY_INT[MY_TEN];
+  tmp = g[5];
+
+#undef MY_INT
+#undef MY_VAR
+#undef MY_TEN
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ 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

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

2023-01-27 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 492914.
ziqingluo-90 added a comment.

Rebase the change


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

https://reviews.llvm.org/D141338

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

Index: clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
@@ -0,0 +1,60 @@
+#include "gtest/gtest.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+
+using namespace clang;
+
+namespace {
+// The test fixture.
+class UnsafeBufferUsageTest : public ::testing::Test {
+protected:
+  UnsafeBufferUsageTest()
+  : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
+Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+SourceMgr(Diags, FileMgr) {}
+
+  FileSystemOptions FileMgrOpts;
+  FileManager FileMgr;
+  IntrusiveRefCntPtr DiagID;
+  DiagnosticsEngine Diags;
+  SourceManager SourceMgr;
+};
+} // namespace
+
+TEST_F(UnsafeBufferUsageTest, FixItHintsConflict) {
+  const FileEntry *DummyFile = FileMgr.getVirtualFile("", 100, 0);
+  FileID DummyFileID = SourceMgr.getOrCreateFileID(DummyFile, SrcMgr::C_User);
+  SourceLocation StartLoc = SourceMgr.getLocForStartOfFile(DummyFileID);
+
+#define MkDummyHint(Begin, End)\
+  FixItHint::CreateReplacement(SourceRange(StartLoc.getLocWithOffset((Begin)), \
+   StartLoc.getLocWithOffset((End))),  \
+   "dummy")
+
+  FixItHint H1 = MkDummyHint(0, 5);
+  FixItHint H2 = MkDummyHint(6, 10);
+  FixItHint H3 = MkDummyHint(20, 25);
+  llvm::SmallVector Fixes;
+
+  // Test non-overlapping fix-its:
+  Fixes = {H1, H2, H3};
+  EXPECT_FALSE(internal::anyConflict(Fixes, SourceMgr));
+  Fixes = {H3, H2, H1}; // re-order
+  EXPECT_FALSE(internal::anyConflict(Fixes, SourceMgr));
+
+  // Test overlapping fix-its:
+  Fixes = {H1, H2, H3, MkDummyHint(0, 4) /* included in H1 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+
+  Fixes = {H1, H2, H3, MkDummyHint(10, 15) /* overlaps H2 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+
+  Fixes = {H1, H2, H3, MkDummyHint(7, 23) /* overlaps H2, H3 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+
+  Fixes = {H1, H2, H3, MkDummyHint(2, 23) /* overlaps H1, H2, and H3 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+}
\ No newline at end of file
Index: clang/unittests/Analysis/CMakeLists.txt
===
--- clang/unittests/Analysis/CMakeLists.txt
+++ clang/unittests/Analysis/CMakeLists.txt
@@ -9,6 +9,7 @@
   CloneDetectionTest.cpp
   ExprMutationAnalyzerTest.cpp
   MacroExpansionContextTest.cpp
+  UnsafeBufferUsageTest.cpp
   )
 
 clang_target_link_libraries(ClangAnalysisTests
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -674,6 +674,37 @@
   return FixablesForUnsafeVars;
 }
 
+bool clang::internal::anyConflict(
+const SmallVectorImpl &FixIts, const SourceManager &SM) {
+  // A simple interval overlap detecting algorithm.  Sorts all ranges by their
+  // begin location then find any 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;
+}
+
 Optional
 ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
   if (const auto *DRE = dyn_cast(Node->getBase()->IgnoreImpCasts()))
@@ -925,8 +956,12 @@
 else
   FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
FixItsForVD.begin(), FixItsForVD.end());
-// Fix-it shall not overlap with m

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

2023-01-27 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 492918.

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

https://reviews.llvm.org/D141338

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

Index: clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
@@ -0,0 +1,60 @@
+#include "gtest/gtest.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+
+using namespace clang;
+
+namespace {
+// The test fixture.
+class UnsafeBufferUsageTest : public ::testing::Test {
+protected:
+  UnsafeBufferUsageTest()
+  : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
+Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+SourceMgr(Diags, FileMgr) {}
+
+  FileSystemOptions FileMgrOpts;
+  FileManager FileMgr;
+  IntrusiveRefCntPtr DiagID;
+  DiagnosticsEngine Diags;
+  SourceManager SourceMgr;
+};
+} // namespace
+
+TEST_F(UnsafeBufferUsageTest, FixItHintsConflict) {
+  const FileEntry *DummyFile = FileMgr.getVirtualFile("", 100, 0);
+  FileID DummyFileID = SourceMgr.getOrCreateFileID(DummyFile, SrcMgr::C_User);
+  SourceLocation StartLoc = SourceMgr.getLocForStartOfFile(DummyFileID);
+
+#define MkDummyHint(Begin, End)\
+  FixItHint::CreateReplacement(SourceRange(StartLoc.getLocWithOffset((Begin)), \
+   StartLoc.getLocWithOffset((End))),  \
+   "dummy")
+
+  FixItHint H1 = MkDummyHint(0, 5);
+  FixItHint H2 = MkDummyHint(6, 10);
+  FixItHint H3 = MkDummyHint(20, 25);
+  llvm::SmallVector Fixes;
+
+  // Test non-overlapping fix-its:
+  Fixes = {H1, H2, H3};
+  EXPECT_FALSE(internal::anyConflict(Fixes, SourceMgr));
+  Fixes = {H3, H2, H1}; // re-order
+  EXPECT_FALSE(internal::anyConflict(Fixes, SourceMgr));
+
+  // Test overlapping fix-its:
+  Fixes = {H1, H2, H3, MkDummyHint(0, 4) /* included in H1 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+
+  Fixes = {H1, H2, H3, MkDummyHint(10, 15) /* overlaps H2 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+
+  Fixes = {H1, H2, H3, MkDummyHint(7, 23) /* overlaps H2, H3 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+
+  Fixes = {H1, H2, H3, MkDummyHint(2, 23) /* overlaps H1, H2, and H3 */};
+  EXPECT_TRUE(internal::anyConflict(Fixes, SourceMgr));
+}
\ No newline at end of file
Index: clang/unittests/Analysis/CMakeLists.txt
===
--- clang/unittests/Analysis/CMakeLists.txt
+++ clang/unittests/Analysis/CMakeLists.txt
@@ -9,6 +9,7 @@
   CloneDetectionTest.cpp
   ExprMutationAnalyzerTest.cpp
   MacroExpansionContextTest.cpp
+  UnsafeBufferUsageTest.cpp
   )
 
 clang_target_link_libraries(ClangAnalysisTests
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -674,6 +674,37 @@
   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;
+}
+
 Optional
 ULCArraySubscriptGadget::getFixits(const Strategy &S) const {
   if (const auto *DRE = dyn_cast(Node->getBase()->IgnoreImpCasts()))
@@ -925,8 +956,12 @@
 else
   FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
FixItsForVD.begin(), FixItsForVD.end());
-// Fix-it shall not overlap with macros or/and templates:
-if (overlapWit

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-02-01 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 494053.
ziqingluo-90 marked 2 inline comments as done.
ziqingluo-90 added a comment.

To attach fix-its to notes instead of warnings.
Fix the ending '\0' issue raised by @jkorous


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

https://reviews.llvm.org/D139737

Files:
  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-fixits-local-var-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,20 +80,24 @@
 
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
-  foo(ap1[1]);// expected-note{{used in buffer access here}}
+  foo(ap1[1]);// expected-note{{used in buffer access here}} 
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}}
+  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
 
   foo(ap2[1]);// expected-note{{used in buffer access here}}
 
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}}
+  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
 
   foo(ap3[1][1]); // expected-note{{used in buffer access here}}
   // expected-warning@-1{{unsafe buffer access}}
 
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}}
+  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
 
   foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
@@ -355,7 +359,8 @@
   auto
 
 
-  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+ 	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
   foo(ap1[1]);  // expected-note{{used in buffer access here}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,168 @@
+// 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;
+
+void local_array_subscript_simple() {
+  int tmp;
+  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}"
+  const int *q = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::span q"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:29}:", 10}"
+  tmp = p[5];
+  tmp = q[5];
+
+  Int_ptr_t x = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span x"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+  Int_ptr_t y = new int;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span y"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 1}"
+  Int_t * z = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span z"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  Int_t * w = new Int_t[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span w"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+
+  tmp = x[5];
+  tmp = y[5]; // y[5] will crash after being span
+  tmp = z[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  i

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-02-01 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 494075.
ziqingluo-90 added a comment.

rebase


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

https://reviews.llvm.org/D139737

Files:
  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-fixits-local-var-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,20 +80,24 @@
 
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
-  foo(ap1[1]);// expected-note{{used in buffer access here}}
+  foo(ap1[1]);// expected-note{{used in buffer access here}} 
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}}
+  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
 
   foo(ap2[1]);// expected-note{{used in buffer access here}}
 
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}}
+  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
 
   foo(ap3[1][1]); // expected-note{{used in buffer access here}}
   // expected-warning@-1{{unsafe buffer access}}
 
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}}
+  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
 
   foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
@@ -355,7 +359,8 @@
   auto
 
 
-  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+ 	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
   foo(ap1[1]);  // expected-note{{used in buffer access here}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,168 @@
+// 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;
+
+void local_array_subscript_simple() {
+  int tmp;
+  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}"
+  const int *q = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::span q"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:29}:", 10}"
+  tmp = p[5];
+  tmp = q[5];
+
+  Int_ptr_t x = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span x"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+  Int_ptr_t y = new int;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span y"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 1}"
+  Int_t * z = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span z"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  Int_t * w = new Int_t[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span w"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+
+  tmp = x[5];
+  tmp = y[5]; // y[5] will crash after being span
+  tmp = z[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  int tmp;
+  auto p = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK: fix-it:"{{.*}

[PATCH] D143128: [-Wunsafe-buffer-usage][WIP] Fix-Its transforming `&DRE[*]` to `DRE.data() + *`

2023-02-01 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: t-rasmud, NoQ, jkorous, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Work in progress.  So far, there is some glitches, e.g., it does not ignore 
calls to functions with unsafe attributes.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D143128

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+int f(unsigned long, void *);
+
+[[clang::unsafe_buffer_usage]]
+int unsafe_f(unsigned long, void *);
+
+void address_to_integer(int x) {
+  int * p = new int[10];  
+  unsigned long n = (unsigned long) &p[5];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"p.data() + 5"
+  unsigned long m = (unsigned long) &p[x];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"p.data() + x"  
+}
+
+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"
+}
+
+void ignore_unsafe_calls(int x) {
+  int * p = new int[10];
+
+  unsafe_f((unsigned long) &p[5],
+	   &p[x]);
+}
+
+
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -9,7 +9,9 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include 
 #include 
@@ -110,6 +112,15 @@
   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);
   
@@ -129,6 +140,29 @@
   castSubExpr(innerMatcher));
   // FIXME: add assignmentTo context...
 }
+
+// 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 {
@@ -143,15 +177,6 @@
 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 specific
@@ -449,6 +474,50 @@
 return {};
   }
 };
+
+// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
+// Context (see `isInUnspecifiedPointerContext`).
+// Note here `[]` is the built-in subscript operator.
+class UPCAddressofArraySubscriptGadget : public FixableGadget {
+private:
+  static constexpr const char *const UPCAddressofArraySubscriptTag =
+  "AddressofArraySubscriptUnderUPC";
+  const UnaryOperator *Node; // the `&DR

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-02-03 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 494689.
ziqingluo-90 added a comment.

address comments


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

https://reviews.llvm.org/D139737

Files:
  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-fixits-local-var-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,20 +80,24 @@
 
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
-  foo(ap1[1]);// expected-note{{used in buffer access here}}
+  foo(ap1[1]);// expected-note{{used in buffer access here}} 
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}}
+  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
 
   foo(ap2[1]);// expected-note{{used in buffer access here}}
 
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}}
+  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
 
   foo(ap3[1][1]); // expected-note{{used in buffer access here}}
   // expected-warning@-1{{unsafe buffer access}}
 
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}}
+  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
 
   foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
@@ -355,7 +359,8 @@
   auto
 
 
-  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+ 	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
   foo(ap1[1]);  // expected-note{{used in buffer access here}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,175 @@
+// 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;
+
+void local_array_subscript_simple() {
+  int tmp;
+  int *p = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+  const int *q = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::span q"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:29}:", 10}"
+  tmp = p[5];
+  tmp = q[5];
+
+  Int_ptr_t x = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span x"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+  Int_ptr_t y = new int;
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span y"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 1}"
+  Int_t * z = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span z"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  Int_t * w = new Int_t[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span w"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+
+  tmp = x[5];
+  tmp = y[5]; // y[5] will crash after being span
+  tmp = z[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  int tmp;
+  auto p = new int[10];
+  // CHECK-DAG: f

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

2023-02-03 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 494693.
ziqingluo-90 added a comment.

Change the fix-it test style


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

https://reviews.llvm.org/D140179

Files:
  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
  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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h
@@ -0,0 +1,14 @@
+#ifdef _INCLUDE_NO_WARN
+// the snippet will be included in an opt-out region
+p1++;
+
+#undef _INCLUDE_NO_WARN
+
+#elif defined(_INCLUDE_WARN)
+// the snippet will be included in a location where warnings are expected
+p2++; // expected-note{{used in pointer arithmetic here}}
+#undef _INCLUDE_WARN
+
+#else
+
+#endif
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
@@ -0,0 +1,100 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -Wno-unused-value -verify %s
+
+void basic(int * x) {// expected-warning{{'x' is an unsafe pointer used for buffer access}}
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+
+#define _INCLUDE_NO_WARN
+#include "warn-unsafe-buffer-usage-pragma.h" // increment p1 in header
+
+  int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage end
+  p2[5]; //expected-note{{used in buffer access here}}
+  p3[5]; //expected-note{{used in buffer access here}}
+  x++;   //expected-note{{used in pointer arithmetic here}}
+#define _INCLUDE_WARN
+#include "warn-unsafe-buffer-usage-pragma.h" // increment p2 in header
+}
+
+
+void withDiagnosticWarning() {
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+
+  // diagnostics in opt-out region
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic warning "-Weverything"
+  p1[5];  // not to warn expected-warning{{expression result unused}}
+  p2[5];  // not to warn expected-warning{{expression result unused}}
+#pragma clang diagnostic pop
+#pragma clang unsafe_buffer_usage end
+
+  // opt-out region under diagnostic warning
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+#pragma clang diagnostic pop
+
+  p2[5]; // expected-note{{used in buffer access here}}
+}
+
+
+void withDiagnosticIgnore() {
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+  int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic ignored "-Weverything"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic pop
+#pragma clang unsafe_buffer_usage end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+#pragma clang diagnostic pop
+
+  p2[5]; // expected-note{{used in buffer access here}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+  p3[5];  // expected-note{{used in buffer access here}}
+#pragma clang diagnostic pop
+}
+
+void noteGoesWithVarDeclWarning() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+  int *p = new int[10]; // not to warn
+#pragma 

[PATCH] D143128: [-Wunsafe-buffer-usage][WIP] Fix-Its transforming `&DRE[any]` to `DRE.data() + any`

2023-02-03 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 494711.
ziqingluo-90 retitled this revision from "[-Wunsafe-buffer-usage][WIP] Fix-Its 
transforming `&DRE[*]` to `DRE.data() + *`" to "[-Wunsafe-buffer-usage][WIP] 
Fix-Its transforming `&DRE[any]` to `DRE.data() + any`".
ziqingluo-90 edited the summary of this revision.

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

https://reviews.llvm.org/D143128

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+int f(unsigned long, void *);
+
+[[clang::unsafe_buffer_usage]]
+int unsafe_f(unsigned long, void *);
+
+void address_to_integer(int x) {
+  int * p = new int[10];
+  unsigned long n = (unsigned long) &p[5];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"(p.data() + 5)"
+  unsigned long m = (unsigned long) &p[x];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"(p.data() + x)"
+}
+
+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)"
+}
+
+void ignore_unsafe_calls(int x) {
+  // Cannot fix `&p[x]` for now as it is an argument of an unsafe
+  // call. So no fix for variable `p`.
+  int * p = new int[10];
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+  unsafe_f((unsigned long) &p[5],
+	   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+	   &p[x]);
+
+  int * q = new int[10];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span q"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
+  unsafe_f((unsigned long) &q[5],
+	   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:28-[[@LINE-1]]:33}:"(q.data() + 5)"
+	   (void*)0);
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -20,6 +20,15 @@
 using namespace ast_matchers;
 
 namespace clang::ast_matchers {
+// 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()));
+}
+
 // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
 // except for those belonging to a different callable of "n".
 class MatchDescendantVisitor
@@ -136,6 +145,29 @@
   castSubExpr(innerMatcher));
   // FIXME: add assignmentTo context...
 }
+
+// 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 {
@@ -150,15 +182,6 @@
 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 specific
@@ -456,6 +479,50 @@
 return {};
   }
 };
+
+// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
+// Context (see `isInUnspecifiedPointerContext`).
+// Note he

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-02-06 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 495250.
ziqingluo-90 added a comment.

Fixed a bug in manipulating source locations:

Using `SourceLocation::getLocWithOffset` to get a new source location `L` with 
a relative offset is convenient. But it requires extra care to make sure that 
`L` has the same file ID as the location where `L` is originated.  Otherwise, 
`L` may be associated with a wrong file ID (or malloc ID).  Feeding such an `L` 
to a `Lexer` could cause failures.


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

https://reviews.llvm.org/D139737

Files:
  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-fixits-local-var-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,20 +80,24 @@
 
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
   foo(ap1[1]);// expected-note{{used in buffer access here}}
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}}
+  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
 
   foo(ap2[1]);// expected-note{{used in buffer access here}}
 
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}}
+  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
 
   foo(ap3[1][1]); // expected-note{{used in buffer access here}}
   // expected-warning@-1{{unsafe buffer access}}
 
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}}
+  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
 
   foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
@@ -355,7 +359,8 @@
   auto
 
 
-  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+ 	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
   foo(ap1[1]);  // expected-note{{used in buffer access here}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,175 @@
+// 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;
+
+void local_array_subscript_simple() {
+  int tmp;
+  int *p = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+  const int *q = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::span q"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:29}:", 10}"
+  tmp = p[5];
+  tmp = q[5];
+
+  Int_ptr_t x = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span x"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+  Int_ptr_t y = new int;
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span y"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 1}"
+  Int_t * z = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span z"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  Int_t * w = new Int_t[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span w"
+  // CHECK-DAG: fix

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-02-06 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 495263.
ziqingluo-90 added a comment.

To avoid emitting incorrect fix-its for array subscripts on `span` objects:

- For unsafe operations of the form `s[e]` where `s` is being transformed to be 
a `span`,  we only emit fix-its for `s[e]` when `e` is a non-negative integer 
literal or `e` has an unsigned integer type.


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

https://reviews.llvm.org/D139737

Files:
  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-fixits-local-var-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,20 +80,24 @@
 
 void testArraySubscriptsWithAuto(int *p, int **pp) {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
-  foo(ap1[1]);// expected-note{{used in buffer access here}}
+  foo(ap1[1]);// expected-note{{used in buffer access here}} 
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}}
+  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
 
   foo(ap2[1]);// expected-note{{used in buffer access here}}
 
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}}
+  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
+		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
 
   foo(ap3[1][1]); // expected-note{{used in buffer access here}}
   // expected-warning@-1{{unsafe buffer access}}
 
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}}
+  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
+  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
 
   foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
@@ -355,7 +359,8 @@
   auto
 
 
-  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
+  ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
+ 	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
 
   foo(ap1[1]);  // expected-note{{used in buffer access here}}
 }
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,192 @@
+// 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;
+
+void local_array_subscript_simple() {
+  int tmp;
+  int *p = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+  const int *q = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:17}:"std::span q"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:29}:", 10}"
+  tmp = p[5];
+  tmp = q[5];
+
+  Int_ptr_t x = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span x"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
+  Int_ptr_t y = new int;
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span y"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:17-[[@LINE-2]]:17}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 1}"
+  Int_t * z = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span z"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  Int_t * w = new Int_t[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span w"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
+  

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-02-06 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:703
+  case Strategy::Kind::Span:
+return FixItList{};
+  case Strategy::Kind::Wontfix:

jkorous wrote:
> jkorous wrote:
> > jkorous wrote:
> > > I am afraid I might have found one more problem :(
> > > I believe that for `span` strategy we have to make sure the index is > 0. 
> > > Otherwise 
> > > That means either an unsigned integer or signed or unsigned literal that 
> > > is greater than 0.
> > > For the literal you can take inspiration here:
> > > https://reviews.llvm.org/D142795
> > > 
> > @ziqingluo-90 Sorry, looks like I wasn't clear here.
> > One case (that you've already addressed) is `ptr[-5]` - for that we can't 
> > use `std::span::operator[]` as it would immediately trap.
> > But there's the other case of:
> > ```
> > uint8_t foo(uint8_t *ptr, int idx) {
> >   return ptr[idx]
> > }
> > ```
> > If anyone uses a value that's both signed and not a compile-time constant 
> > then our compile-time analysis can not prove that the index is always >= 0 
> > and consequently we can't use `std::span::operator[]` as a replacement.
> > That's why I think we really need to make sure that the index is ether a) 
> > positive literal or b) unsigned.
> > WDYT?
> > 
> > 
> And yes ... I was wrong - literal `0` is totally fine. Thanks for spotting 
> that!
I think you are right.  Fixed it.


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

https://reviews.llvm.org/D139737

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


[PATCH] D143128: [-Wunsafe-buffer-usage][WIP] Fix-Its transforming `&DRE[any]` to `DRE.data() + any`

2023-02-06 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:508
+  hasUnaryOperand(arraySubscriptExpr(
+  hasBase(ignoringParenImpCasts(declRefExpr())
+.bind(UPCAddressofArraySubscriptTag);

jkorous wrote:
> I am wondering what will happen in the weird corner-case of `&5[ptr]` - I 
> feel the Fix-It we produce would be incorrect.
> 
> Here's a suggestion - we could use `hasLHS` instead of `hasBase` here and add 
> a FIXME that when we find the time we should also properly support the 
> corner-case. That would be a pretty low-priority though - we definitely have 
> more important patterns to support first.
> 
> WDYT?
> 
I'm not sure if I understand your concern.  For `&5[ptr]`, we will generate a 
fix-it `ptr.data() + 5` in cases `ptr` is assigned a `span` strategy.   It is 
same as the case of `&ptr[5]`.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:810
+  case Strategy::Kind::Span:
+return fixUPCAddressofArraySubscriptWithSpan(Node);
+  case Strategy::Kind::Wontfix:

jkorous wrote:
> Since we use `std::nullopt` in `getFixits` to signal errors - we should 
> either use the same strategy in `fixUPCAddressofArraySubscriptWithSpan` or 
> translate the empty return value from it to `nullopt` here.
> (FWIWI I am leaning towards the former.)
> Forwarding the empty Fix-It would be incorrect.
> 
> 
Oh, that's a bug I made!  Thank you for finding it for me.


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

https://reviews.llvm.org/D143128

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


[PATCH] D141338: [WIP][-Wunsafe-buffer-usage] Filter out conflicting fix-its

2023-01-11 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 488429.

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

https://reviews.llvm.org/D141338

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

Index: clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/UnsafeBufferUsageTest.cpp
@@ -0,0 +1,60 @@
+#include "gtest/gtest.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+
+using namespace clang;
+
+namespace {
+// The test fixture.
+class UnsafeBufferUsageTest : public ::testing::Test {
+protected:
+  UnsafeBufferUsageTest()
+  : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
+Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+SourceMgr(Diags, FileMgr) {}
+
+  FileSystemOptions FileMgrOpts;
+  FileManager FileMgr;
+  IntrusiveRefCntPtr DiagID;
+  DiagnosticsEngine Diags;
+  SourceManager SourceMgr;
+};
+} // namespace
+
+TEST_F(UnsafeBufferUsageTest, FixItHintsConflict) {
+  const FileEntry *DummyFile = FileMgr.getVirtualFile("", 100, 0);
+  FileID DummyFileID = SourceMgr.getOrCreateFileID(DummyFile, SrcMgr::C_User);
+  SourceLocation StartLoc = SourceMgr.getLocForStartOfFile(DummyFileID);
+
+#define MkDummyHint(Begin, End)\
+  FixItHint::CreateReplacement(SourceRange(StartLoc.getLocWithOffset((Begin)), \
+   StartLoc.getLocWithOffset((End))),  \
+   "dummy")
+
+  FixItHint H1 = MkDummyHint(0, 5);
+  FixItHint H2 = MkDummyHint(6, 10);
+  FixItHint H3 = MkDummyHint(20, 25);
+  llvm::SmallVector Fixes;
+
+  // Test non-overlapping fix-its:
+  Fixes = {H1, H2, H3};
+  EXPECT_FALSE(anyConflict(SourceMgr, Fixes));
+  Fixes = {H3, H2, H1}; // re-order
+  EXPECT_FALSE(anyConflict(SourceMgr, Fixes));
+
+  // Test overlapping fix-its:
+  Fixes = {H1, H2, H3, MkDummyHint(0, 4) /* included in H1 */};
+  EXPECT_TRUE(anyConflict(SourceMgr, Fixes));
+
+  Fixes = {H1, H2, H3, MkDummyHint(10, 15) /* overlaps H2 */};
+  EXPECT_TRUE(anyConflict(SourceMgr, Fixes));
+
+  Fixes = {H1, H2, H3, MkDummyHint(7, 23) /* overlaps H2, H3 */};
+  EXPECT_TRUE(anyConflict(SourceMgr, Fixes));
+
+  Fixes = {H1, H2, H3, MkDummyHint(2, 23) /* overlaps H1, H2, and H3 */};
+  EXPECT_TRUE(anyConflict(SourceMgr, Fixes));
+}
\ No newline at end of file
Index: clang/unittests/Analysis/CMakeLists.txt
===
--- clang/unittests/Analysis/CMakeLists.txt
+++ clang/unittests/Analysis/CMakeLists.txt
@@ -9,6 +9,7 @@
   CloneDetectionTest.cpp
   ExprMutationAnalyzerTest.cpp
   MacroExpansionContextTest.cpp
+  UnsafeBufferUsageTest.cpp
   )
 
 clang_target_link_libraries(ClangAnalysisTests
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -109,7 +109,7 @@
 
 AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher, innerMatcher) {
   const DynTypedMatcher &DTM = static_cast(innerMatcher);
-  
+
   MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All);  
   return Visitor.findMatch(DynTypedNode::create(Node));
 }
@@ -690,6 +690,37 @@
   }
 }
 
+bool clang::anyConflict(const SourceManager &SM,
+const llvm::SmallVectorImpl &FixIts) {
+  // A simple interval overlap detecting algorithm.  Sorts all ranges by their
+  // begin location then find any 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;
+}
+
 void clang::checkUnsafeBufferUsage(const Decl *D,
UnsafeBufferUsageHandler &Handler) {
   assert(D && D->getBody());
@@ -777,9 +808,7 

[PATCH] D141338: [WIP][-Wunsafe-buffer-usage] Filter out conflicting fix-its

2023-01-11 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

In D141338#4042050 , @NoQ wrote:

> Looks awesome!
>
> I'm worried that the test is going to rapidly become stale as you develop 
> fixits for `DeclStmt` further. It might make sense to write some 
> //unittests// for this class directly, to put into 
> `clang/unittest/Analysis/...`. Then you can create some fixits defined by 
> hand, without invoking UnsafeBufferUsage analysis, and feed them to this 
> class directly and verify the results.
>
> Also let's explain motivation a bit more. Normally compiler warnings don't 
> need such machine because the fixits they come with are very simple and 
> hand-crafted. However, unsafe buffer usage analysis is a large emergent 
> behavior machine that may fix different parts of user code, and different 
> parts of the machine that can produce parts of such fixits aren't necessarily 
> aware of each other. In some cases no progress can be made without making 
> sure these parts of the machine talk to each other. However, every time the 
> machine does emit individual fixits, they're actually correct; conflicts 
> between such fixits are entirely a representation problem (we're unable to 
> present overlapping fixits to the user because this requires us to resolve 
> the merge conflict), not an underlying correctness problem. So @ziqingluo-90 
> is implementing a bailout for the situation when the fixits were perfectly 
> correct in isolation but can't be properly displayed to the user due to merge 
> conflicts between them. This makes it possible for such "merge conflicts" 
> detection to be purely SourceRange-based, so it doesn't need to take the 
> details of the underlying AST into account.

Thanks for adding the motivation here!

I updated the revision by getting rid of things we don't need for now.   
At this point, we just need to tell if there is any conflict in the 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.

At some point later, we may want to know the exact conflicting subsets.  That 
will be another patch.


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

https://reviews.llvm.org/D141338

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


[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-11 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h:46
+/// The text indicating that the user needs to provide input there:
+constexpr static const char *const UserFillPlaceHolder = "...";
 } // end namespace clang

jkorous wrote:
> jkorous wrote:
> > Should we rather pick something that is syntactically incorrect in C++ in 
> > order to prevent accidental silent corruption of the sources?
> > FWIW Xcode uses `<#placeholder#>` syntax.
> Correction - it is not Xcode, it is clang itself.
> https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/CodeCompleteConsumer.cpp#L323
It looks like we could put different messages in between `<#` and `#>` to hint 
users in different situations.   I'll make this member a function then.


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

https://reviews.llvm.org/D139737

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


[PATCH] D140179: [WIP][-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

2023-01-13 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 489136.
ziqingluo-90 added a comment.

Rebase the patch.

Move the check of whether a node is in an opt-out region to an earlier 
stage---the AST matching stage.


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

https://reviews.llvm.org/D140179

Files:
  clang/include/clang/Basic/Diagnostic.h
  clang/include/clang/Basic/DiagnosticLexKinds.td
  clang/include/clang/Lex/Preprocessor.h
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/lib/Basic/Diagnostic.cpp
  clang/lib/Lex/PPLexerChange.cpp
  clang/lib/Lex/Pragma.cpp
  clang/lib/Lex/Preprocessor.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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.h
@@ -0,0 +1,14 @@
+#ifdef _INCLUDE_NO_WARN
+// the snippet will be included in an opt-out region
+p1++;
+
+#undef _INCLUDE_NO_WARN
+
+#elif defined(_INCLUDE_WARN)
+// the snippet will be included in a location where warnings are expected
+p2++; // expected-note{{used in pointer arithmetic here}}
+#undef _INCLUDE_WARN
+
+#else
+
+#endif
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp
@@ -0,0 +1,100 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -Wno-unused-value -verify %s
+
+void basic(int * x) {// expected-warning{{'x' is an unsafe pointer used for buffer access}}
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+
+#define _INCLUDE_NO_WARN
+#include "warn-unsafe-buffer-usage-pragma.h" // increment p1 in header
+
+  int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage end
+  p2[5]; //expected-note{{used in buffer access here}}
+  p3[5]; //expected-note{{used in buffer access here}}
+  x++;   //expected-note{{used in pointer arithmetic here}}
+#define _INCLUDE_WARN
+#include "warn-unsafe-buffer-usage-pragma.h" // increment p2 in header
+}
+
+
+void withDiagnosticWarning() {
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+
+  // diagnostics in opt-out region
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic warning "-Weverything"
+  p1[5];  // not to warn expected-warning{{expression result unused}}
+  p2[5];  // not to warn expected-warning{{expression result unused}}
+#pragma clang diagnostic pop
+#pragma clang unsafe_buffer_usage end
+
+  // opt-out region under diagnostic warning
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+#pragma clang diagnostic pop
+
+  p2[5]; // expected-note{{used in buffer access here}}
+}
+
+
+void withDiagnosticIgnore() {
+  int *p1 = new int[10]; // not to warn
+  int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
+  int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
+
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic ignored "-Weverything"
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang diagnostic pop
+#pragma clang unsafe_buffer_usage end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+#pragma clang diagnostic pop
+
+  p2[5]; // expected-note{{used in buffer access here}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+#pragma clang unsafe_buffer_usage begin
+  p1[5];  // not to warn
+  p2[5];  // not to warn
+#pragma clang unsafe_buffer_usage end
+  p3[5];  // expected-note{{used in buffer access here}}
+#pragma clang diagnostic pop
+}
+
+void noteGoesWithVarDeclWarning() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+  int *p = new int[10]; // not to warn
+#pragm

[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 490648.
ziqingluo-90 added a comment.

Rebased the code w.r.t. a series of refactoring in [-Wunsafe-buffer-usage].
Added a `FixableGadget` for array subscripts of the form `DRE[*]` in the 
context of lvalue-to-rvalue casting.

Also did a refactoring at the place where matchers are applied:  Matchers of a 
`FixableGadget` and  of a `WarningGadget` cat match for the same AST node.  So 
they should not be put in an `anyOf` group, otherwise they can disable each 
other due to short-circuiting.


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,86 @@
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+  int tmp;
+// CHECK: std::span p{new int [10], 10};
+// CHECK: std::span q{new int [10], 10};
+// CHECK: tmp = p[5];
+// CHECK: tmp = q[5];
+  int *p = new int[10];
+  const int *q = new int[10];
+  tmp = p[5];
+  tmp = q[5];
+
+// CHECK: std::span x{new int [10], 10};
+// CHECK: std::span y{new int, 1};
+// CHECK: std::span z{new int [10], 10};
+// CHECK: std::span w{new Int_t [10], 10};
+
+  Int_ptr_t x = new int[10];
+  Int_ptr_t y = new int;
+  Int_t * z = new int[10];
+  Int_t * w = new Int_t[10];
+
+  // CHECK: tmp = x[5];
+  tmp = x[5];
+  // CHECK: tmp = y[5];
+  tmp = y[5]; // y[5] will crash after being span
+  // CHECK: tmp = z[5];
+  tmp = z[5];
+  // CHECK: tmp = w[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  int tmp;
+// CHECK: std::span p{new int [10], 10};
+// CHECK: tmp = p[5];
+  auto p = new int[10];
+  tmp = p[5];
+}
+
+void local_array_subscript_variable_extent() {
+  int n = 10;
+  int tmp;
+  //FIXME: need to think it twice about side effects in the expressions
+  //used to initialize span objects
+  // CHECK: std::span p{new int [n], n};
+  // CHECK: std::span q{new int [n++], <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = new int[n];
+  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  int *q = new int[n++];
+  tmp = p[5];
+  tmp = q[5];
+}
+
+
+void local_ptr_to_array() {
+  int tmp;
+  int n = 10;
+  int a[10];
+  int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  // CHECK: std::span p{a, 10};
+  // CHECK: std::span q{b, <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = a;
+  int *q = b;
+  tmp = p[5];
+  tmp = q[5];
+}
+
+void local_ptr_addrof_init() {
+  int var;
+// CHECK: std::span q{&var, 1};
+// CHECK: var = q[5];
+  int * q = &var;
+  // This expression involves unsafe buffer accesses, which will crash
+  // at runtime after applying the fix-it,
+  var = q[5];
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -115,6 +115,21 @@
   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 auto isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) {
+  auto isLvalueToRvalueCast = [](internal::Matcher M) {
+return implicitCastExpr(hasCastKind(CastKind::CK_LValueToRValue),
+castSubExpr(M));
+  };
+  //FIXME: add assignmentTo context...
+  return isLvalueToRvalueCast(innerMatcher);
+}
 } // namespace clang::ast_matchers
 
 namespace {
@@ -282,7 +297,7 @@
 /// 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 = "arraySubscr";
+  static constexpr const char *const ArraySubscrTag = "ArraySubscript";
   const ArraySubscriptExpr *ASE;
 
 public:
@@ -366,6 +381,51 @@
   // FIXME: pointer adding zero should be fine
   //FIXME: this gadge will need a fix-it
 };
+
+class Strategy;
+
+// Represents ex

[PATCH] D140179: [WIP][-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

2023-01-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/include/clang/Basic/Diagnostic.h:1040-1043
+  // 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.
+  SmallVector, 8> 
SafeBufferOptOutMap;

NoQ wrote:
> Ok, now I no longer see why this data should live in DiagnosticEngine. It's 
> mostly about analysis, right? The pragma simply makes our analysis produce 
> different results, regardless of whether these results are used for producing 
> diagnostics or something else. Maybe let's keep it all in Preprocessor?
make sense to me!



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:543
 #define GADGET(x)  
\
-x ## Gadget::matcher().bind(#x),
+allOf(x ## Gadget::matcher().bind(#x), notInSafeBufferOptOut()),
 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

NoQ wrote:
> This prevents safe fixable gadgets from being found in the opt-out zone. I 
> think this clause should only apply to warning gadgets.
You are right!  Fixables should be found regardless of whether they are in an 
opt-out zone.  A Fixable could later be immediately discarded once we know that 
the variable declaration associated to the Fixable is in an opt-out zone.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:551-555
+allOf(declStmt().bind("any_ds"), notInSafeBufferOptOut())
+// We match all DREs regardless of whether they are in safe-buffer
+// opt-out region. Because an unclaimed DRE 'd', regardless of where 
it is,
+// should prevent a Fixable associated to the same variable as 'd'
+// from being emitting.

NoQ wrote:
> I think we should match all DeclStmts as well, because otherwise we may be 
> unable to find the variable to fix.
In case we are unable to find the variable to fix,  it means that the variable 
declaration is in an opt-out zone.  So we don't fix the variable anyway, right?

Or do you mean that a variable may still get fixed even if its declaration is 
in an opt-out zone?   I could imagine it is possible if the variable is 
involved in some assignments that we want to fix.


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

https://reviews.llvm.org/D140179

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


[PATCH] D140179: [WIP][-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

2023-01-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:543
 #define GADGET(x)  
\
-x ## Gadget::matcher().bind(#x),
+allOf(x ## Gadget::matcher().bind(#x), notInSafeBufferOptOut()),
 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

ziqingluo-90 wrote:
> NoQ wrote:
> > This prevents safe fixable gadgets from being found in the opt-out zone. I 
> > think this clause should only apply to warning gadgets.
> You are right!  Fixables should be found regardless of whether they are in an 
> opt-out zone.  A Fixable could later be immediately discarded once we know 
> that the variable declaration associated to the Fixable is in an opt-out zone.
Oh wait, scratch what I said above.

`FixableGadget`s should be found regardless of whether they are in an opt-out 
zone.   They will be attached to variable declarations.  The emission of an 
Unsafe Buffer diagnostic of a variable declaration only depends on 
`WarningGadget`s.   So `FixableGadget`s have nothing to do with opt-out 
regions. 



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

https://reviews.llvm.org/D140179

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


[PATCH] D140179: [WIP][-Wunsafe-buffer-usage] Add unsafe buffer checking opt-out pragmas

2023-01-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:551-555
+allOf(declStmt().bind("any_ds"), notInSafeBufferOptOut())
+// We match all DREs regardless of whether they are in safe-buffer
+// opt-out region. Because an unclaimed DRE 'd', regardless of where 
it is,
+// should prevent a Fixable associated to the same variable as 'd'
+// from being emitting.

NoQ wrote:
> ziqingluo-90 wrote:
> > NoQ wrote:
> > > I think we should match all DeclStmts as well, because otherwise we may 
> > > be unable to find the variable to fix.
> > In case we are unable to find the variable to fix,  it means that the 
> > variable declaration is in an opt-out zone.  So we don't fix the variable 
> > anyway, right?
> > 
> > Or do you mean that a variable may still get fixed even if its declaration 
> > is in an opt-out zone?   I could imagine it is possible if the variable is 
> > involved in some assignments that we want to fix.
> > do you mean that a variable may still get fixed even if its declaration is 
> > in an opt-out zone?
> 
> Yes I think that's the case. We do have tests about this right?:
> ```lang=c++
> #pragma clang unsafe_buffer_usage begin
>   ...
>   int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used 
> for buffer access}}
> 
> #pragma clang unsafe_buffer_usage end
>   ...
>   p3[5]; //expected-note{{used in buffer access here}}
> ```
> And I think it's safe to assume that every time we emit a warning, we also 
> want to emit a fixit.
> 
> If only we had any fixits implemented, this code would have been much easier 
> to refactor because we'd have some actual tests covering it 🤔
sorry I didn't think it through when I reply the comments.  You are right.
Fixables and variable declarations should be irrelevant to opt-out regions.

I'll rebase this patch w.r.t. https://reviews.llvm.org/D139737 so that we will 
have tests covering it.


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

https://reviews.llvm.org/D140179

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


[PATCH] D139737: [-Wunsafe-buffer-usage] Initiate Fix-it generation for local variable declarations

2023-01-23 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 491581.
ziqingluo-90 added a comment.

Refactored the fix-it generation code to stop using the pretty-printer.


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

https://reviews.llvm.org/D139737

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -0,0 +1,96 @@
+// RUN: cp %s %t.cpp
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fixit %t.cpp
+// RUN: grep -v CHECK %t.cpp | FileCheck %s
+typedef int * Int_ptr_t;
+typedef int Int_t;
+
+void local_array_subscript_simple() {
+  int tmp;
+// CHECK: std::span p {new int[10], 10};
+// CHECK: std::span q {new int[10], 10};
+// CHECK: tmp = p[5];
+// CHECK: tmp = q[5];
+  int *p = new int[10];
+  const int *q = new int[10];
+  tmp = p[5];
+  tmp = q[5];
+
+// CHECK: std::span x {new int[10], 10};
+// CHECK: std::span y {new int, 1};
+// CHECK: std::span z {new int[10], 10};
+// CHECK: std::span w {new Int_t[10], 10};
+
+  Int_ptr_t x = new int[10];
+  Int_ptr_t y = new int;
+  Int_t * z = new int[10];
+  Int_t * w = new Int_t[10];
+
+  // CHECK: tmp = x[5];
+  tmp = x[5];
+  // CHECK: tmp = y[5];
+  tmp = y[5]; // y[5] will crash after being span
+  // CHECK: tmp = z[5];
+  tmp = z[5];
+  // CHECK: tmp = w[5];
+  tmp = w[5];
+}
+
+void local_array_subscript_auto() {
+  int tmp;
+// CHECK: std::span p {new int[10], 10};
+// CHECK: tmp = p[5];
+  auto p = new int[10];
+  tmp = p[5];
+}
+
+void local_array_subscript_variable_extent() {
+  int n = 10;
+  int tmp;
+
+  // CHECK: std::span p {new int[n], n};
+  // CHECK: std::span q {new int[n++], <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = new int[n];
+  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  int *q = new int[n++];
+  tmp = p[5];
+  tmp = q[5];
+}
+
+
+void local_ptr_to_array() {
+  int tmp;
+  int n = 10;
+  int a[10];
+  int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
+  // CHECK: std::span p {a, 10};
+  // CHECK: std::span q {b, <# placeholder #>};
+  // CHECK: tmp = p[5];
+  // CHECK: tmp = q[5];
+  int *p = a;
+  int *q = b;
+  tmp = p[5];
+  tmp = q[5];
+}
+
+void local_ptr_addrof_init() {
+  int var;
+// CHECK: std::span q {&var, 1};
+// CHECK: var = q[5];
+  int * q = &var;
+  // This expression involves unsafe buffer accesses, which will crash
+  // at runtime after applying the fix-it,
+  var = q[5];
+}
+
+void decl_without_init() {
+  int tmp;
+  // CHECK: std::span p;
+  int * p;
+  // CHECK: std::span q;
+  Int_ptr_t q;
+
+  tmp = p[5];
+  tmp = q[5];
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ 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,21 @@
   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 auto isInUnspecifiedLvalueContext(internal::Matcher innerMatcher) {
+  auto isLvalueToRvalueCast = [](internal::Matcher M) {
+return implicitCastExpr(hasCastKind(CastKind::CK_LValueToRValue),
+castSubExpr(M));
+  };
+  //FIXME: add assignmentTo context...
+  return isLvalueToRvalueCast(innerMatcher);
+}
 } // namespace clang::ast_matchers
 
 namespace {
@@ -282,7 +298,7 @@
 /// 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 = "arraySubscr";
+  static constexpr const char *const ArraySubscrTag = "ArraySubscript";
   const ArraySubscriptExpr *ASE;
 
 public:
@@ -366,6 +382,51 @@
   // FIXME: pointer adding zero should be fine
   //FIXME: this gadge will need a fix-it
 };
+
+class Strategy;
+
+// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
+// Context (see `isInUn

[PATCH] D128314: [Clang-tidy] Fixing bugs in clang-tidy infinite-loop checker

2022-06-21 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, t-rasmud, usama54321, rsundahl, yln, 
kubamracek, krispy1994, jkorous, delcypher, chrisdangelo, thetruestblue, 
dcoughlin, aaron.ballman, alexfh, gribozavr, njames93, LegalizeAdulthood.
Herald added subscribers: carlosgalvezp, xazax.hun.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added projects: clang, clang-tools-extra.
Herald added a subscriber: cfe-commits.

Fixing 2 bugs in the infinite loop checker:

  Bug 1.  Missing handling of noreturn attributes for ObjC nodes.
  Bug 2.  The checker reports a false positive for a loop whose condition 
involves static local variables, which can be changed in recursion.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
  clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3829,6 +3829,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee, internal::Matcher,
+  InnerMatcher) {
+const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+return (msgDecl != nullptr &&
+InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
@@ -5070,6 +5089,14 @@
 /// \endcode
 AST_MATCHER(FunctionDecl, isNoReturn) { return Node.isNoReturn(); }
 
+/// matches a Decl if it has a  "no return" attribute of any kind
+AST_MATCHER(Decl, declHasNoReturnAttr) { return Node.hasAttr() ||
+   Node.hasAttr() ||
+   Node.hasAttr(); }
+
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {return Node.getNoReturnAttr();}
+
 /// Matches the return type of a function declaration.
 ///
 /// Given:
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
@@ -685,3 +685,31 @@
 0;
   }) x;
 }
+
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+  
+  i--; 
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i  
+  for (;i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (;i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i  
+  for (; i >= 0;i--)
+;   // no warning, i decrements  
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop] 
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
+
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
@@ -0,0 +1,66 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc
+
+@interface I
++(void) foo;
++(void) bar;
++(void) baz __attribute__ ((noreturn));
++(instancetype)alloc;
+-(instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i+j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-06-22 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439132.
ziqingluo-90 retitled this revision from "[Clang-tidy] Fixing bugs in 
clang-tidy infinite-loop checker" to "[Clang-tidy] Fixing a bug in clang-tidy 
infinite-loop checker".
ziqingluo-90 edited the summary of this revision.
ziqingluo-90 added a comment.

Separate the change adding handling of noreturn attributes for ObjC nodes.  
Have run clang-format.


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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3829,6 +3829,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {
+  const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+  return (msgDecl != nullptr &&
+  InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
@@ -5070,6 +5089,17 @@
 /// \endcode
 AST_MATCHER(FunctionDecl, isNoReturn) { return Node.isNoReturn(); }
 
+/// matches a Decl if it has a  "no return" attribute of any kind
+AST_MATCHER(Decl, declHasNoReturnAttr) {
+  return Node.hasAttr() || Node.hasAttr() ||
+ Node.hasAttr();
+}
+
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {
+  return Node.getNoReturnAttr();
+}
+
 /// Matches the return type of a function declaration.
 ///
 /// Given:
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fexceptions
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc -fexceptions
+
+@interface I
++ (void)foo;
++ (void)bar;
++ (void)baz __attribute__((noreturn));
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  int a[10];
+
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i + j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone-infinite-loop]
+[I foo];
+  }
+  while (i + j < 10) {
+[I foo];
+[I baz]; // no warning, class method baz has noreturn attribute
+  }
+
+  void (^block)() = ^{
+  };
+  void __attribute__((noreturn)) (^block_nr)(void) = ^void __attribute__((noreturn)) (void) { throw "err"; };
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+block();
+  }
+  while (i < 10) {
+// no warning, the block has "noreturn" arribute
+block_nr();
+  }
+}
+
+@implementation I
++ (void)bar {
+}
+
++ (void)foo {
+  static int i = 0;
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I bar];
+  }
+}
+@end
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -21,10 +21,17 @@
 
 static internal::Matcher
 loopEndingStmt(internal::Matcher Internal) {
-  // FIXME: Cover noreturn ObjC methods (and blocks?).
+  internal::Matcher isNoReturnFunType =
+  ignoringParens(functionType(typeHasNoReturnAttr()));
+  internal::Matcher isNoReturnDecl =
+  anyOf(declHasNoReturnAttr(), functionDecl(hasType(isNoReturnFunType)),
+varDecl(hasType(blockPointerType(pointee(isNoReturnFunType);
+
   return stmt(anyOf(
   mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
-  callExpr(Internal, callee(fu

[PATCH] D128401: [Clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-22 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, t-rasmud, usama54321, rsundahl, yln, 
kubamracek, krispy1994, jkorous, delcypher, chrisdangelo, thetruestblue, 
aaron.ballman, alexfh, gribozavr, njames93, LegalizeAdulthood.
Herald added subscribers: carlosgalvezp, kristof.beyls, xazax.hun.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

The Infinite Loop Checker could report a false positive on the example below:

  void f() {
static int i = 0;
i++;
while (i < 10)
   f();
  }

This patch adds checking for recursive calls in the Infinite Loop Checker when 
loop condition involves static local variables.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+  
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,111 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+if (const CallExpr *Call = dyn_cast(StmtNode)) {
+const Decl *Callee = Call->getDirectCallee();
+
+if (!Callee)
+return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+}
+if (const ObjCMessageExpr *Call = dyn_cast(StmtNode)) {
+const ObjCMethodDecl *Callee = Call->getMethodDecl();
+
+if (!Callee)
+return false;// unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+}
+  for (const Stmt *Child : StmtNode->children())
+if

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-06-23 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439545.
ziqingluo-90 removed reviewers: rsundahl, yln, kubamracek, krispy1994, jkorous, 
delcypher, chrisdangelo, thetruestblue, dcoughlin.
ziqingluo-90 added a comment.

rebased with the latest main's HEAD


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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3829,6 +3829,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {
+  const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+  return (msgDecl != nullptr &&
+  InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
@@ -5070,6 +5089,17 @@
 /// \endcode
 AST_MATCHER(FunctionDecl, isNoReturn) { return Node.isNoReturn(); }
 
+/// matches a Decl if it has a  "no return" attribute of any kind
+AST_MATCHER(Decl, declHasNoReturnAttr) {
+  return Node.hasAttr() || Node.hasAttr() ||
+ Node.hasAttr();
+}
+
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {
+  return Node.getNoReturnAttr();
+}
+
 /// Matches the return type of a function declaration.
 ///
 /// Given:
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop-noreturn.mm
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fexceptions
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc -fexceptions
+
+@interface I
++ (void)foo;
++ (void)bar;
++ (void)baz __attribute__((noreturn));
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  int a[10];
+
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i + j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone-infinite-loop]
+[I foo];
+  }
+  while (i + j < 10) {
+[I foo];
+[I baz]; // no warning, class method baz has noreturn attribute
+  }
+
+  void (^block)() = ^{
+  };
+  void __attribute__((noreturn)) (^block_nr)(void) = ^void __attribute__((noreturn)) (void) { throw "err"; };
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+block();
+  }
+  while (i < 10) {
+// no warning, the block has "noreturn" arribute
+block_nr();
+  }
+}
+
+@implementation I
++ (void)bar {
+}
+
++ (void)foo {
+  static int i = 0;
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I bar];
+  }
+}
+@end
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -21,10 +21,17 @@
 
 static internal::Matcher
 loopEndingStmt(internal::Matcher Internal) {
-  // FIXME: Cover noreturn ObjC methods (and blocks?).
+  internal::Matcher isNoReturnFunType =
+  ignoringParens(functionType(typeHasNoReturnAttr()));
+  internal::Matcher isNoReturnDecl =
+  anyOf(declHasNoReturnAttr(), functionDecl(hasType(isNoReturnFunType)),
+varDecl(hasType(blockPointerType(pointee(isNoReturnFunType);
+
   return stmt(anyOf(
   mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
-  callExpr(Internal, callee(functionDecl(isNoReturn());
+  callExpr(Internal, callee(functionDecl(isNoReturnDecl))),
+  objcMessageExpr(Internal, objcMessageCallee(isNoReturn

[PATCH] D128314: [Clang-tidy] Fixing a bug in clang-tidy infinite-loop checker

2022-06-23 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439549.
ziqingluo-90 added a comment.

adjusted my changes with respect to the recent file structure changes in 
clang-tidy test


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

https://reviews.llvm.org/D128314

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3829,6 +3829,25 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
+/// matches if ObjCMessageExpr's callee declaration matches
+///
+/// Given
+/// \code
+///   @interface I: NSObject
+///   +(void)foo;
+///   @end
+///   ...
+///   [I foo]
+/// \endcode
+/// The example above matches \code [I foo] \endcode with
+/// objcMessageExpr(objcMessageCallee(objcMethodDecl(hasName("foo"
+AST_MATCHER_P(ObjCMessageExpr, objcMessageCallee,
+  internal::Matcher, InnerMatcher) {
+  const ObjCMethodDecl *msgDecl = Node.getMethodDecl();
+  return (msgDecl != nullptr &&
+  InnerMatcher.matches(*msgDecl, Finder, Builder));
+}
+
 /// Matches if the call expression's callee's declaration matches the
 /// given matcher.
 ///
@@ -5070,6 +5089,17 @@
 /// \endcode
 AST_MATCHER(FunctionDecl, isNoReturn) { return Node.isNoReturn(); }
 
+/// matches a Decl if it has a  "no return" attribute of any kind
+AST_MATCHER(Decl, declHasNoReturnAttr) {
+  return Node.hasAttr() || Node.hasAttr() ||
+ Node.hasAttr();
+}
+
+/// matches a FunctionType if the type includes the GNU no return attribute
+AST_MATCHER(FunctionType, typeHasNoReturnAttr) {
+  return Node.getNoReturnAttr();
+}
+
 /// Matches the return type of a function declaration.
 ///
 /// Given:
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop-noreturn.mm
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fexceptions
+// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks -fobjc-arc -fexceptions
+
+@interface I
++ (void)foo;
++ (void)bar;
++ (void)baz __attribute__((noreturn));
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+_Noreturn void term();
+
+void plainCFunction() {
+  int i = 0;
+  int j = 0;
+  int a[10];
+
+  while (i < 10) {
+// no warning, function term has C noreturn attribute
+term();
+  }
+  while (i < 10) {
+// no warning, class method baz has noreturn attribute
+[I baz];
+  }
+  while (i + j < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, j) are updated in the loop body [bugprone-infinite-loop]
+[I foo];
+  }
+  while (i + j < 10) {
+[I foo];
+[I baz]; // no warning, class method baz has noreturn attribute
+  }
+
+  void (^block)() = ^{
+  };
+  void __attribute__((noreturn)) (^block_nr)(void) = ^void __attribute__((noreturn)) (void) { throw "err"; };
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+block();
+  }
+  while (i < 10) {
+// no warning, the block has "noreturn" arribute
+block_nr();
+  }
+}
+
+@implementation I
++ (void)bar {
+}
+
++ (void)foo {
+  static int i = 0;
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I bar];
+  }
+}
+@end
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -21,10 +21,17 @@
 
 static internal::Matcher
 loopEndingStmt(internal::Matcher Internal) {
-  // FIXME: Cover noreturn ObjC methods (and blocks?).
+  internal::Matcher isNoReturnFunType =
+  ignoringParens(functionType(typeHasNoReturnAttr()));
+  internal::Matcher isNoReturnDecl =
+  anyOf(declHasNoReturnAttr(), functionDecl(hasType(isNoReturnFunType)),
+varDecl(hasType(blockPointerType(pointee(isNoReturnFunType);
+
   return stmt(anyOf(
   mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
-  callExpr(Internal, callee(functionDecl(isNoReturn());
+  callExpr(Internal, callee(functionDecl(isNoReturnDecl))),
+  objcMessageExpr(Internal, objcMessageCallee(isNoReturnDecl)),
+  callExpr(Internal, callee(varDecl(isNoReturnDecl);
 }
 
 //

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-23 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439573.
ziqingluo-90 added a comment.

rebased with the latest `main:HEAD` where clang-tidy file structure has changed


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

https://reviews.llvm.org/D128401

Files:
  clang-tidy/bugprone/InfiniteLoopCheck.cpp
  test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,111 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const CallExpr *Call = dyn_cast(StmtNode)) {
+const Decl *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const ObjCMessageExpr *Call = dyn_cast(StmtNode)) {
+const ObjCMethodDecl *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const Stmt *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool containsFunc = false;
+  bool overlap = false;
+
+  for (CallGraphNode *GNode : SCC) {
+const Decl *CanoDecl = GNode->getDecl()->getCanonicalDecl();
+
+containsFunc |= (CanoDecl == Func);
+overlap |= Callees.contains(CanoDecl);
+if (containsFunc && overlap)
+  return true;
+  }
+  return containsFunc && overlap;
+}
+
+/// returns true iff `Cond` involves at least one static local variable.
+static bool hasStaticLocalVariable(const Stmt *Cond) {
+  if (const DeclRefExpr *DRE = dyn_cast(Cond))
+if (const VarDecl *V

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-23 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439580.
ziqingluo-90 added a comment.

Addressing Dmitri's comments.


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

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,111 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const CallExpr *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const ObjCMessageExpr *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const Stmt *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool containsFunc = false;
+  bool overlap = false;
+
+  for (CallGraphNode *GNode : SCC) {
+const Decl *CanDecl = GNode->getDecl()->getCanonicalDecl();
+
+containsFunc |= (CanDecl == Func);
+overlap |= Callees.contains(CanDecl);
+if (containsFunc && overlap)
+  return true;
+  }
+  return containsFunc && overlap;
+}
+
+/// returns true iff `Cond` involves at least one

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439882.
ziqingluo-90 added a comment.

addressing more comments


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

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,110 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const CallExpr *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const ObjCMessageExpr *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const Stmt *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool ContainsFunc = false, Overlap = false;
+
+  for (CallGraphNode *GNode : SCC) {
+const auto *CanDecl = GNode->getDecl()->getCanonicalDecl();
+
+ContainsFunc = ContainsFunc || (CanDecl == Func);
+Overlap = Overlap || Callees.contains(CanDecl);
+if (ContainsFunc && Overlap)
+  return true;
+  }
+  return ContainsFunc && Overlap;
+}
+
+/// returns true iff `Cond` involves 

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439883.

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

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,110 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const ObjCMessageExpr *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const Stmt *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool ContainsFunc = false, Overlap = false;
+
+  for (CallGraphNode *GNode : SCC) {
+const auto *CanDecl = GNode->getDecl()->getCanonicalDecl();
+
+ContainsFunc = ContainsFunc || (CanDecl == Func);
+Overlap = Overlap || Callees.contains(CanDecl);
+if (ContainsFunc && Overlap)
+  return true;
+  }
+  return ContainsFunc && Overlap;
+}
+
+/// returns true iff `Cond` involves at least one static local variable.
+static bool hasStaticLoc

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439884.
ziqingluo-90 added a comment.

trying to be consistent in that `auto` is used wherever possible


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

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,110 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const auto *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const auto *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool ContainsFunc = false, Overlap = false;
+
+  for (const auto *GNode : SCC) {
+const auto *CanDecl = GNode->getDecl()->getCanonicalDecl();
+
+ContainsFunc = ContainsFunc || (CanDecl == Func);
+Overlap = Overlap || Callees.contains(CanDecl);
+if (ContainsFunc && Overlap)
+  return true;
+  }
+  return ContainsFunc && Overlap;
+}
+
+/// returns tru

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 439903.
ziqingluo-90 added a comment.

I missed the part of coding standard about the usage of `auto`.   Now change to 
conform to it.


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

https://reviews.llvm.org/D128401

Files:
  clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.mm
@@ -44,6 +44,27 @@
 j++;
   }
 }
+
++ (void)recursiveMethod {
+  static int i = 0;
+
+  i++;
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[I classMethod];
+  }
+
+  id x = [[I alloc] init];
+
+  while (i < 10) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+[x instanceMethod];
+  }
+  while (i < 10) {
+// no warning, there is a recursive call that can mutate the static local variable
+[I recursiveMethod];
+  }
+}
 @end
 
 void testArrayCount() {
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -685,3 +685,29 @@
 0;
   }) x;
 }
+
+void test_local_static_recursion() {
+  static int i = 10;
+  int j = 0;
+
+  i--;
+  while (i >= 0)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i + j >= 0;)
+test_local_static_recursion(); // no warning, recursively decrement i
+  for (; i >= 0; i--)
+; // no warning, i decrements
+  while (j >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
+test_local_static_recursion();
+
+  int (*p)(int) = 0;
+
+  while (i >= 0)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
+p = 0;
+  while (i >= 0)
+p(0); // we don't know what p points to so no warning
+}
Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -11,6 +11,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang::ast_matchers;
 using clang::tidy::utils::hasPtrOrReferenceInFunc;
@@ -146,6 +149,110 @@
   return false;
 }
 
+/// populates the set `Callees` with all function (and objc method) declarations
+/// called in `StmtNode` if all visited call sites have resolved call targets.
+///
+/// \return true iff all `CallExprs` visited have callees; false otherwise
+/// indicating there is an unresolved indirect call.
+static bool populateCallees(const Stmt *StmtNode,
+llvm::SmallSet &Callees) {
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const Decl *Callee = Call->getDirectCallee();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  if (const auto *Call = dyn_cast(StmtNode)) {
+const Decl *Callee = Call->getMethodDecl();
+
+if (!Callee)
+  return false; // unresolved call
+Callees.insert(Callee->getCanonicalDecl());
+  }
+  for (const Stmt *Child : StmtNode->children())
+if (Child && !populateCallees(Child, Callees))
+  return false;
+  return true;
+}
+
+/// returns true iff `SCC` contains `Func` and its' function set overlaps with
+/// `Callees`
+static bool overlap(ArrayRef SCC,
+const llvm::SmallSet &Callees,
+const Decl *Func) {
+  bool ContainsFunc = false, Overlap = false;
+
+  for (const CallGraphNode *GNode : SCC) {
+const Decl *CanDecl = GNode->getDecl()->getCanonicalDecl();
+
+ContainsFunc = ContainsFunc || (CanDecl == Func);
+Overlap = Overlap || Callees.contains(CanDecl);
+if (ContainsFunc && Overlap)
+  return true;
+  }
+  return Contai

[PATCH] D128401: [clang-tidy] Fixing a bug raising false alarms on static local variables in the Infinite Loop Checker

2022-06-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp:34
 /// Return whether `Var` was changed in `LoopStmt`.
 static bool isChanged(const Stmt *LoopStmt, const VarDecl *Var,
   ASTContext *Context) {

NoQ wrote:
> Ok so this whole checker does this procedure involving `ExprMutationAnalyzer` 
> trying to figure out if the variable can be changed from within the loop. I 
> wonder if our problem of whether the static variable can be changed within 
> the loop is a sub-problem of this problem and therefore could have a general 
> solution inside `ExprMutationAnalyzer`. If so, other clang-tidy checks that 
> use `ExprMutationAnalyzer` could immediately benefit from such solution. WDYT?
I think this is a good point.  I can do it after this patch.


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

https://reviews.llvm.org/D128401

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


[PATCH] D153059: [-Wunsafe-buffer-usage] Group parameter fix-its

2023-09-21 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG33f6161d9eaa: [-Wunsafe-buffer-usage] Group parameter 
fix-its (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D153059?vs=553285&id=557194#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153059

Files:
  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-multi-parm-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -36,7 +36,7 @@
 void * voidPtrCall(void);
 char * charPtrCall(void);
 
-void testArraySubscripts(int *p, int **pp) { // expected-note{{change type of 'pp' to 'std::span' to preserve bounds information}}
+void testArraySubscripts(int *p, int **pp) {
 // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
 // expected-warning@-2{{'pp' is an unsafe pointer used for buffer access}}
   foo(p[1], // expected-note{{used in buffer access here}}
@@ -97,7 +97,6 @@
   sizeof(decltype(p[1])));  // no-warning
 }
 
-// expected-note@+1{{change type of 'a' to 'std::span' to preserve bounds information}}
 void testQualifiedParameters(const int * p, const int * const q, const int a[10], const int b[10][10]) {
   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
   // expected-warning@-2{{'q' is an unsafe pointer used for buffer access}}
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -29,8 +29,7 @@
   int tmp;
   tmp = p[5] + q[5];
 }
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void twoParms(int *p, int * q) {return twoParms(std::span(p, <# size #>), q);}\n"
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:2-[[@LINE-2]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void twoParms(int *p, int * q) {return twoParms(p, std::span(q, <# size #>));}\n"
+// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void twoParms(int *p, int * q) {return twoParms(std::span(p, <# size #>), std::span(q, <# size #>));}\n"
 
 void ptrToConst(const int * x) {
   // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:30}:"std::span x"
@@ -100,22 +99,33 @@
 // namned
   } NAMED_S;
 
+
   // FIXME: `decltype(ANON_S)` represents an unnamed type but it can
   // be referred as "`decltype(ANON_S)`", so the analysis should
   // fix-it.
-  void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,
- decltype(NAMED_S) ** rr) {
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:26-[[@LINE-2]]:41}:"std::span p"
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-3]]:65-[[@LINE-3]]:86}:"std::span r"
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-3]]:26-[[@LINE-3]]:49}:"std::span rr"
+  // As parameter `q` cannot be fixed, fixes to parameters are all being given up.
+  void decltypeSpecifierAnon(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,
+ decltype(NAMED_S) ** rr) {
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:30-[[@LINE-2]]:45}:"std::span p"
+// CHECK: fix-it:{{.*}}:{[[@LINE-3]]:47-[[@LINE-3]]:67}:"std::span q"
+// CHECK: fix-it:{{.*}}:{[[@LINE-4]]:69-[[@LINE-4]]:90}:"std::span r"
+// CHECK: fix-it:{{.*}}:{[[@LINE-4]]:30-[[@LINE-4]]:53}:"std::span rr"
 if (++p) {}
 if (++q) {}
 if (++r) {}
 if (++rr) {}
   }
-  // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(std::span(p, <# size #>), q, r, rr);}\n
-  // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:4-[[@LINE-2]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(p, q, std::span(r, <# size #>), rr);}\n"
-  // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-3]]:4-[[@LINE-3]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q

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

2023-09-21 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 marked an inline comment as done.
ziqingluo-90 added a comment.






Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:2586
 #endif
 it = FixablesForAllVars.byVar.erase(it);
   } else if (Tracker.hasUnclaimedUses(it->first)) {

NoQ wrote:
> ziqingluo-90 wrote:
> > We cannot fix reference type variable declarations for now.  
> > Besides, it seems that reference type is invisible to `Expr`s.  For example,
> > ```
> > void foo(int * &p) {
> >   p[5];  // the type of the DRE `p` is a pointer-to-int instead of a 
> > reference
> > }
> > ```
> > So we ignore such variables explicitly here.
> Ooo [[ https://xkcd.com/1053/ | you're one of today's lucky 10,000 ]]! (More 
> like today's 0.01, I doubt there are many C++ programmers who are aware of 
> this quirk, it's a very niche compiler gotcha.)
> 
> That's right, expressions in C++ can't have reference types. They have "value 
> kinds" instead (lvalue, rvalue, etc) which are more fundamental (not even 
> necessarily C++-specific). As far as I understand, from language formalism 
> point of view, references aren't values per se; they're just names given to 
> other values (glvalues, to be exact). There's no separate operation of 
> "reference dereference" in C++ formalism; "undereferenced reference" 
> expressions don't exist in the first place.
> 
> So in this example:
> ```lang=c++
> int x = 5;
> int &y = x;
> int a = x + y;
> ```
> not only the type of DeclRefExpr `y` is `int`, but also the *value* of the 
> DeclRefExpr `y` is the glvalue named `x`. And the value of DeclRefExpr `x` is 
> *also* the glvalue named `x`. From the point of view of C++ formalism, 
> there's no separate location in memory named `y`. The declaration `y` simply 
> declares a different way to refer to `x`.
> 
> This formalism is very different from pointers, where in
> ```lang=c++
> int x = 5;
> int *y = &x;
> int a = x + *y;
> ```
> you're allowed to assume that there's a separate memory location named `y` 
> where the address of memory location named `x` is written. You can further 
> take the address of that location and put it into a third location named `w` 
> by writing `int **z = &y;`. You can do that indefinitely, and then roll it 
> all back with sufficient amount of `*`s. You can also reassign your pointers, 
> eg. `y = &a` would overwrite the data at location `y`.
> 
> You cannot do any of that with references. You cannot reassign a reference. 
> You cannot take a reference to a reference; that's the same as a reference to 
> the original lvalue, both simply become names given to that actual value. 
> Similarly, the language doesn't let you take an address of the location in 
> which the reference is stored; it doesn't even allow you to assume that such 
> location exists.
> 
> Sure you can "convert a reference to a pointer" with the help of the operator 
> `&`, but that's just taking address of an lvalue it refers to. This doesn't 
> mean that the reference itself "is" an address, it just means that the lvalue 
> itself is technically still just an address. References can often be 
> implemented as pointers by the codegen, eg. reference-type fields in classes 
> or reference-type return values are probably just pointers "under the hood". 
> Conversely, in case of pointers the actual location for `y` and for `z` could 
> have also been optimized away by the optimizer, causing the pointers to "act 
> like references" "under the hood": from CodeGen perspective they're just a 
> different way to refer to `x`. But the purpose of this formalism isn't to 
> describe what happens under the hood; it's just to define what operations are 
> allowed on which expressions. And for that very simple and limited purpose, 
> references don't act like values, and expressions of reference type don't 
> really make sense, which is what we observe in the AST.
> 
> (None of this has anything to do with the patch, I just hope this helps you 
> approach this problem in the future.)
Thank you for explaining this!   You just provided me a brand new view on C++ 
references .  //"expressions of reference type don't really make sense"// could 
have confused me but now it makes perfect sense with your explanation.


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

https://reviews.llvm.org/D157441

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


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

2023-09-21 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
ziqingluo-90 marked an inline comment as done.
Closed by commit rG700baeb765cf: [-Wunsafe-buffer-usage] Use `Strategy` to 
determine whether to fix a parameter (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D157441?vs=553226&id=557209#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D157441

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-multi-parm-span.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -174,7 +174,7 @@
   int * b;
   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
   int * c;
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
 
   c[5] = 5;
 #pragma clang unsafe_buffer_usage begin
@@ -182,6 +182,7 @@
 #pragma clang unsafe_buffer_usage end
   a = b;
   b = c;
+// FIXME: we do not fix `a = b` and `b = c` because the `.data()`  fix-it is not generally correct.
 }
 
 // The implication edges are: `a` -> `b` -> `c` -> `a`.
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp
@@ -18,9 +18,9 @@
 void local_assign_rhs_span() {
   int tmp;
   int* p = new int[10];
-  int* q = new int[10];  // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note{{change type of 'q' to 'std::span' to preserve bounds information}}
+  int* q = new int[10];  // expected-warning{{'q' is an unsafe pointer used for buffer access}}
   tmp = q[4];  // expected-note{{used in buffer access here}}
-  p = q;
+  p = q;  // FIXME: we do not fix `p = q` here as the `.data()` fix-it is not generally correct
 }
 
 void local_assign_no_span() {
@@ -49,10 +49,10 @@
 
 void rhs_span() {
   int *x = new int[3];
-  int *y;  // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note{{change type of 'y' to 'std::span' to preserve bounds information}}
+  int *y;  // expected-warning{{'y' is an unsafe pointer used for buffer access}}
   y[5] = 10;  // expected-note{{used in buffer access here}}
 
-  x = y;
+  x = y; // FIXME: we do not fix `x = y` here as the `.data()` fix-it is not generally correct
 }
 
 void rhs_span1() {
@@ -65,43 +65,43 @@
 
 void rhs_span2() {
   int *q = new int[6];
-  int *p = q;  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  int *p = q; // expected-warning{{'p' is an unsafe pointer used for buffer access}}
   p[5] = 10;  // expected-note{{used in buffer access here}}
-  int *r = q;
+  int *r = q; // FIXME: we do not fix `int *r = q` here as the `.data()` fix-it is not generally correct
 }
 
 void test_grouping() {
   int *z = new int[8];
   int tmp;
-  int *y = new int[10];  // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note{{change type of 'y' to 'std::span' to preserve bounds information}}
+  int *y = new int[10];  // expected-warning{{'y' is an unsafe pointer used for buffer access}}
   tmp = y[5]; // expected-note{{used in buffer access here}}
 
   int *x = new int[10];
-  x = y;
+  x = y;  // FIXME: we do not fix `x = y` here as the `.data()` fix-it is not generally correct
 
   int *w = z;
 }
 
 void test_grouping1() {
   int tmp;
-  int *y = new int[10];  // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note{{change type of 'y' to 'std::span' to preserve bounds information}}
+  int *y = new int[10];  // expected-warning{{'y' is an unsafe pointer used for buffer access}}
   tmp = y[5];  // expected-note{{used in buffer access here}}
   int *x = new int[10];
-  x = y;
+  x = y;   // FIXME: we do not fix `x = y` here as the `.data()` fix-it is not generally correct
 
-  int *w = new int[10];  // expected-warning{{'w' is an unsafe pointer used for buffer access}} expected-note{{change type of 'w' to 'std::s

[PATCH] D158561: [-Wunsafe-buffer-usage] Add AST info to the unclaimed DRE debug notes for analysis

2023-09-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 557330.
ziqingluo-90 added a comment.

Address comments.


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

https://reviews.llvm.org/D158561

Files:
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
  
clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
  clang/utils/analyze_safe_buffer_debug_notes.py

Index: clang/utils/analyze_safe_buffer_debug_notes.py
===
--- /dev/null
+++ clang/utils/analyze_safe_buffer_debug_notes.py
@@ -0,0 +1,39 @@
+import sys
+from collections import OrderedDict
+
+class Trie:
+def __init__(self, name):
+self.name = name
+self.children = OrderedDict()
+self.count = 1
+
+def add(self, name):
+if name in self.children:
+self.children[name].count += 1
+else:
+self.children[name] = Trie(name)
+return self.children[name]
+
+def print(self, depth):
+if depth > 0:
+print('|', end="")
+for i in range(depth):
+print('-', end="")
+if depth > 0:
+print(end=" ")
+print(self.name, '#', self.count)
+for key, child in self.children.items():
+child.print(depth + 1)
+
+
+Root = Trie("Root")
+
+if __name__ == "__main__":
+for line in sys.stdin:
+words = line.split('==>')
+words = [word.strip() for word in words]
+MyTrie = Root;
+for word in words:
+MyTrie = MyTrie.add(word)
+
+Root.print(0)
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \
+// RUN:-mllvm -debug-only=SafeBuffers \
+// RUN:-std=c++20 -verify=expected %s
+
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \
+// RUN:-mllvm -debug-only=SafeBuffers \
+// RUN:-std=c++20 %s  \
+// RUN:2>&1 | grep 'The unclaimed DRE trace:' \
+// RUN: | sed 's/^The unclaimed DRE trace://' \
+// RUN: | PYTHON_EXE %S/../../../utils/analyze_safe_buffer_debug_notes.py \
+// RUN: | FileCheck %s
+
+// This debugging facility is only available in debug builds.
+//
+// REQUIRES: asserts
+
+void test_unclaimed_use(int *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  p++;   //  expected-note{{used in pointer arithmetic here}} \
+ expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+ The unclaimed DRE trace: DeclRefExpr, UnaryOperator(++), CompoundStmt}}
+  *((p + 1) + 1); // expected-warning{{unsafe pointer arithmetic}}  \
+ expected-note{{used in pointer arithmetic here}}			\
+		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, ImplicitCastExpr(LValueToRValue), BinaryOperator(+), ParenExpr, BinaryOperator(+), ParenExpr, UnaryOperator(*), CompoundStmt}}
+  p -= 1; // expected-note{{used in pointer arithmetic here}} \
+		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, BinaryOperator(-=), CompoundStmt}}
+  p--;// expected-note{{used in pointer arithmetic here}} \
+ 		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, UnaryOperator(--), CompoundStmt}}
+  p[5] = 5;   // expected-note{{used in buffer access here}}
+}
+
+// CHECK: Root # 1
+// CHECK: |- DeclRefExpr # 4
+// CHECK: |-- UnaryOperator(++) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- ImplicitCastExpr(LValueToRValue) # 1
+// CHECK: |--- BinaryOperator(+) # 1
+// CHECK: | ParenExpr # 1
+// CHECK: |- BinaryOperator(+) # 1
+// CHECK: |-- ParenExpr # 1
+// CHECK: |--- UnaryOperator(*) # 1
+// CHECK: | CompoundStmt # 1
+// CHECK: |-- BinaryOperator(-=) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- UnaryOperator(--) # 1
+// CHECK: |--- CompoundStmt # 1
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
@@ -0,0 +1,6 @@
+# -*- Python -*-
+import sys
+
+python_executable = sys.executable
+

[PATCH] D158561: [-Wunsafe-buffer-usage] Add AST info to the unclaimed DRE debug notes for analysis

2023-10-03 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 557579.
ziqingluo-90 added a comment.

address comments


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

https://reviews.llvm.org/D158561

Files:
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
  
clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
  clang/utils/analyze_safe_buffer_debug_notes.py

Index: clang/utils/analyze_safe_buffer_debug_notes.py
===
--- /dev/null
+++ clang/utils/analyze_safe_buffer_debug_notes.py
@@ -0,0 +1,39 @@
+import sys
+from collections import OrderedDict
+
+class Trie:
+def __init__(self, name):
+self.name = name
+self.children = OrderedDict()
+self.count = 1
+
+def add(self, name):
+if name in self.children:
+self.children[name].count += 1
+else:
+self.children[name] = Trie(name)
+return self.children[name]
+
+def print(self, depth):
+if depth > 0:
+print('|', end="")
+for i in range(depth):
+print('-', end="")
+if depth > 0:
+print(end=" ")
+print(self.name, '#', self.count)
+for key, child in self.children.items():
+child.print(depth + 1)
+
+
+Root = Trie("Root")
+
+if __name__ == "__main__":
+for line in sys.stdin:
+words = line.split('==>')
+words = [word.strip() for word in words]
+MyTrie = Root;
+for word in words:
+MyTrie = MyTrie.add(word)
+
+Root.print(0)
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \
+// RUN:-mllvm -debug-only=SafeBuffers \
+// RUN:-std=c++20 -verify=expected %s
+
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \
+// RUN:-mllvm -debug-only=SafeBuffers \
+// RUN:-std=c++20 %s  \
+// RUN:2>&1 | grep 'The unclaimed DRE trace:' \
+// RUN: | sed 's/^The unclaimed DRE trace://' \
+// RUN: | %analyze_safe_buffer_debug_notes \
+// RUN: | FileCheck %s
+
+// This debugging facility is only available in debug builds.
+//
+// REQUIRES: asserts
+// REQUIRES: shell
+
+void test_unclaimed_use(int *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  p++;   //  expected-note{{used in pointer arithmetic here}} \
+ expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+ The unclaimed DRE trace: DeclRefExpr, UnaryOperator(++), CompoundStmt}}
+  *((p + 1) + 1); // expected-warning{{unsafe pointer arithmetic}}  \
+ expected-note{{used in pointer arithmetic here}}			\
+		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, ImplicitCastExpr(LValueToRValue), BinaryOperator(+), ParenExpr, BinaryOperator(+), ParenExpr, UnaryOperator(*), CompoundStmt}}
+  p -= 1; // expected-note{{used in pointer arithmetic here}} \
+		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, BinaryOperator(-=), CompoundStmt}}
+  p--;// expected-note{{used in pointer arithmetic here}} \
+ 		 expected-note{{safe buffers debug: failed to produce fixit for 'p' : has an unclaimed use\n \
+  The unclaimed DRE trace: DeclRefExpr, UnaryOperator(--), CompoundStmt}}
+  p[5] = 5;   // expected-note{{used in buffer access here}}
+}
+
+// CHECK: Root # 1
+// CHECK: |- DeclRefExpr # 4
+// CHECK: |-- UnaryOperator(++) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- ImplicitCastExpr(LValueToRValue) # 1
+// CHECK: |--- BinaryOperator(+) # 1
+// CHECK: | ParenExpr # 1
+// CHECK: |- BinaryOperator(+) # 1
+// CHECK: |-- ParenExpr # 1
+// CHECK: |--- UnaryOperator(*) # 1
+// CHECK: | CompoundStmt # 1
+// CHECK: |-- BinaryOperator(-=) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- UnaryOperator(--) # 1
+// CHECK: |--- CompoundStmt # 1
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
===
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/lit.local.cfg
@@ -0,0 +1,11 @@
+# -*- Python -*-
+
+config.substitutions.append(
+  (
+"%analyze_safe_buffe

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

2023-07-17 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added subscribers: cfe-commits, wangpc.
Herald added a project: clang.

When some unsafe operations are suppressed,   there will be (non-strictly) 
fewer variables being warned about.   `FixableGadget`s  associated with the 
suppressed variables will not be fixed.  
Removing these `FixableGadget` as early as possible could help improve the 
performance and stability of the analysis.

The variable grouping algorithm introduced in D145739 
 nearly completes this task.  
For a variable that is neither warned about nor reachable from a warned 
variable, it does not exist in the graph computed by the algorithm. 
So this patch simply removes `FixableGadget`s whose variables are not present 
in the graph computed for variable grouping.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155524

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -107,3 +107,128 @@
 
   p[5]; // not to note since the associated warning is suppressed
 }
+
+
+// Test suppressing interacts with variable grouping:
+
+// The implication edges are: `a` -> `b` -> `c`.
+// If the unsafe operation on `a` is supressed, none of the variables
+// will be fixed.
+void suppressedVarInGroup() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// To show that if `a[5]` is not suppressed in the
+// `suppressedVarInGroup` function above, all variables will be fixed.
+void suppressedVarInGroup_control() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `a` is not suppressed. Variable `b` will still be
+// fixed when fixing `a`.
+void suppressedVarInGroup2() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Only variable `c` will be fixed
+// then.
+void suppressedVarInGroup3() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c` -> `a`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Since the implication graph
+// forms a cycle, all variables will be fixed.
+void suppressedVarInGroup4() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  c = a;
+}
+
+// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
+// Suppressing unsafe operations on variables in one group does not
+// affect other groups.
+void suppressedVarInGroup5() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * d;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d"
+  int * e;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e"
+  int * f;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f"
+
+#pra

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

2023-07-17 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 541269.

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

https://reviews.llvm.org/D155524

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -107,3 +107,128 @@
 
   p[5]; // not to note since the associated warning is suppressed
 }
+
+
+// Test suppressing interacts with variable grouping:
+
+// The implication edges are: `a` -> `b` -> `c`.
+// If the unsafe operation on `a` is supressed, none of the variables
+// will be fixed.
+void suppressedVarInGroup() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// To show that if `a[5]` is not suppressed in the
+// `suppressedVarInGroup` function above, all variables will be fixed.
+void suppressedVarInGroup_control() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `a` is not suppressed. Variable `b` will still be
+// fixed when fixing `a`.
+void suppressedVarInGroup2() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Only variable `c` will be fixed
+// then.
+void suppressedVarInGroup3() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c` -> `a`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Since the implication graph
+// forms a cycle, all variables will be fixed.
+void suppressedVarInGroup4() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  c = a;
+}
+
+// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
+// Suppressing unsafe operations on variables in one group does not
+// affect other groups.
+void suppressedVarInGroup5() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * d;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d"
+  int * e;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e"
+  int * f;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f"
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  
+  d[5] = 5;
+  d = e;
+  e = f;
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1092,6 +1092,18 @@
 
   M.match(*D->getBody(), D->getASTContext());
 
+  // If no `WarningGadget`s ever matched, there is no unsafe operations in the
+  // function under the analysis.  Thus, 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

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

2023-07-17 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 541279.

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

https://reviews.llvm.org/D155524

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -107,3 +107,128 @@
 
   p[5]; // not to note since the associated warning is suppressed
 }
+
+
+// Test suppressing interacts with variable grouping:
+
+// The implication edges are: `a` -> `b` -> `c`.
+// If the unsafe operation on `a` is supressed, none of the variables
+// will be fixed.
+void suppressedVarInGroup() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// To show that if `a[5]` is not suppressed in the
+// `suppressedVarInGroup` function above, all variables will be fixed.
+void suppressedVarInGroup_control() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `a` is not suppressed. Variable `b` will still be
+// fixed when fixing `a`.
+void suppressedVarInGroup2() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Only variable `c` will be fixed
+// then.
+void suppressedVarInGroup3() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c` -> `a`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Since the implication graph
+// forms a cycle, all variables will be fixed.
+void suppressedVarInGroup4() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  c = a;
+}
+
+// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
+// Suppressing unsafe operations on variables in one group does not
+// affect other groups.
+void suppressedVarInGroup5() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * d;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d"
+  int * e;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e"
+  int * f;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f"
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  
+  d[5] = 5;
+  d = e;
+  e = f;
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1092,6 +1092,18 @@
 
   M.match(*D->getBody(), D->getASTContext());
 
+  // If no `WarningGadget`s ever matched, there is no unsafe operations in the
+  // function under the analysis.  Thus, 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

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

2023-07-17 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 marked an inline comment as done.
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:2346
+  // computed above.  We do not want to generate fix-its for such variables,
+  // since they are neither warned nor reachable from a warned one.
+  for (auto I = FixablesForAllVars.byVar.begin();

t-rasmud wrote:
> Nit: Maybe also mention when a variable is neither warned nor is reachable? 
> Are there scenarios besides variables inside pragmas where this constraint is 
> satisfied? 
good question!  I add some explanation in the comment.


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

https://reviews.llvm.org/D155524

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


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

2023-07-18 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 541652.
ziqingluo-90 marked an inline comment as done.

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

https://reviews.llvm.org/D155524

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -107,3 +107,128 @@
 
   p[5]; // not to note since the associated warning is suppressed
 }
+
+
+// Test suppressing interacts with variable grouping:
+
+// The implication edges are: `a` -> `b` -> `c`.
+// If the unsafe operation on `a` is supressed, none of the variables
+// will be fixed.
+void suppressedVarInGroup() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// To show that if `a[5]` is not suppressed in the
+// `suppressedVarInGroup` function above, all variables will be fixed.
+void suppressedVarInGroup_control() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `a` is not suppressed. Variable `b` will still be
+// fixed when fixing `a`.
+void suppressedVarInGroup2() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Only variable `c` will be fixed
+// then.
+void suppressedVarInGroup3() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c` -> `a`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Since the implication graph
+// forms a cycle, all variables will be fixed.
+void suppressedVarInGroup4() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  c = a;
+}
+
+// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
+// Suppressing unsafe operations on variables in one group does not
+// affect other groups.
+void suppressedVarInGroup5() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * d;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d"
+  int * e;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e"
+  int * f;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f"
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  
+  d[5] = 5;
+  d = e;
+  e = f;
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2235,6 +2235,20 @@
 return;
   }
 
+  // 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.
+  //

[PATCH] D155641: [-Wunsafe-buffer-usage] Do not assert that function parameters have names.

2023-07-18 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: jkorous, NoQ, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

It is possible that a function parameter does not have a name even in a 
function definition. 
So we should not assert that function parameters always have names.

This patch lets the analysis give up on generating fix-its in cases where a 
function parameter of a definition has no name.
Later we can come up with a better solution, e.g., generating a name for the 
parameter.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155641

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


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -156,4 +156,9 @@
   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
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1886,8 +1886,11 @@
 
   if (Parm->isImplicit())
 continue;
-  assert(Parm->getIdentifier() &&
- "A parameter of a function definition has no name");
+  // A parameter of a function definition has no name.
+  // FIXME: We should create a name for the parameter as part of the 
fix-it.
+  // Go through declarations of the function and look for a name?
+  if (!Parm->getIdentifier())
+return std::nullopt;
   if (i == ParmIdx)
 // This is our spanified paramter!
 SS << NewTypeText.str() << "(" << 
Parm->getIdentifier()->getName().str() << ", "


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -156,4 +156,9 @@
   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
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1886,8 +1886,11 @@
 
   if (Parm->isImplicit())
 continue;
-  assert(Parm->getIdentifier() &&
- "A parameter of a function definition has no name");
+  // A parameter of a function definition has no name.
+  // FIXME: We should create a name for the parameter as part of the fix-it.
+  // Go through declarations of the function and look for a name?
+  if (!Parm->getIdentifier())
+return std::nullopt;
   if (i == ParmIdx)
 // This is our spanified paramter!
 SS << NewTypeText.str() << "(" << Parm->getIdentifier()->getName().str() << ", "
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2023-07-18 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:1095-1102
   // 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);
 }

NoQ wrote:
> Let's also skip this part when there are no warning gadgets? Your initial 
> patch was clearing the `FixableGadgets` list so it was naturally skipped, but 
> now it's active again.
Oh, I intentionally chose not to skip it:
 - 1. To keep the `Tracker` consistent with `CB.FixableGadgets` in case in the 
future we want to use these two objects even if there is no `WarningGadget`; 
 - 2. The `findGadgets` function can stay as straightforward as its name 
suggests.  It doesn't have to know the specific optimization for empty 
`WarningGadget`s.

But the two thoughts above probably aren't important enough.  I will change it 
back to skipping the loop when there is no warnings.




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

https://reviews.llvm.org/D155524

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


[PATCH] D155667: [-Wunsafe-buffer-usage] Check source location validity before using `TypeLoc`s

2023-07-18 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

The safe-buffer analysis analyzes `TypeLoc`s of types of variable declarations 
in order to get source locations of them.

However, in some cases, the source locations of a `TypeLoc` are not valid.  
Using invalid source locations results in assertion violation or incorrect 
analysis or fix-its.

It is still not clear to me in what circumstances a `TypeLoc`  does not have 
valid source locations (it looks like a bug in Clang to me, but it is not our 
responsibility to fix it).  So we will conservatively give up the analysis when 
required source locations are not valid.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155667

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


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
@@ -33,6 +33,26 @@
   }
 }
 
+// The analysis requires accurate source location informations from
+// `TypeLoc`s of types of variable (parameter) declarations in order
+// to generate fix-its for them. But those information is not always
+// available (probably due to some bugs in clang but it is irrelevant
+// to the safe-buffer project).  The following is an example.  When
+// `_Atomic` is used, we cannot get valid source locations of the
+// pointee type of `unsigned *`.  The analysis gives up in such a
+// case.
+// CHECK-NOT: fix-it:
+void typeLocSourceLocationInvalid(_Atomic unsigned *map) { // 
expected-warning{{'map' is an unsafe pointer used for buffer access}}
+  map[5] = 5; // expected-note{{used in buffer access here}}
+}
+
+// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:33-[[@LINE+1]]:46}:"std::span 
map"
+void typeLocSourceLocationValid(unsigned *map) { // expected-warning{{'map' is 
an unsafe pointer used for buffer access}} \
+   expected-note{{change type 
of 'map' to 'std::span' to preserve bounds information}}
+  map[5] = 5; // expected-note{{used in buffer access here}}
+}
+// CHECK: 
fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}
 void typeLocSourceLocationValid(unsigned *map) {return 
typeLocSourceLocationValid(std::span(map, <# size #>));}\n"
+
 // We do not fix parameters participating unsafe operations for the
 // following functions/methods or function-like expressions:
 
@@ -128,4 +148,3 @@
   int tmp;
   tmp = x[5]; // expected-note{{used in buffer access here}}
 }
-
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1385,8 +1385,18 @@
   TypeLoc PteTyLoc = TyLoc.getUnqualifiedLoc().getNextTypeLoc();
   SourceLocation VDNameStartLoc = VD->getLocation();
 
-  if (!SM.isBeforeInTranslationUnit(PteTyLoc.getSourceRange().getEnd(),
-VDNameStartLoc)) {
+  if (!(VDNameStartLoc.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 when it happens.
+return std::nullopt;
+  }
+
+  // Note that TypeLoc.getEndLoc() returns the begin location of the last 
token:
+  SourceLocation PteEndOfTokenLoc =
+  Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
+
+  if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, VDNameStartLoc)) {
 // We only deal with the cases where the source text of the pointee type
 // appears on the left-hand side of the variable identifier completely,
 // including the following forms:
@@ -1401,13 +1411,8 @@
 // `PteTy` via source ranges.
 *QualifiersToAppend = PteTy.getQualifiers();
   }
-
-  // Note that TypeLoc.getEndLoc() returns the begin location of the last 
token:
-  SourceRange CSR{
-  PteTyLoc.getBeginLoc(),
-  Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts)};
-
-  return getRangeText(CSR, SM, LangOpts)->str();
+  return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
+  ->str();
 }
 
 // Returns the text of the name (with qualifiers) of a `FunctionDecl`.


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.

[PATCH] D155641: [-Wunsafe-buffer-usage] Do not assert that function parameters have names.

2023-07-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 marked an inline comment as done.
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:1890-1891
+  // A parameter of a function definition has no name.
+  // FIXME: We should create a name for the parameter as part of the 
fix-it.
+  // Go through declarations of the function and look for a name?
+  if (!Parm->getIdentifier())

NoQ wrote:
> There doesn't have to be a name anywhere, and if it's not there in the 
> definition this usually means the parameter is unused. If it's unused, we 
> won't ever fix it, and this also means that there's no need to give it a 
> name. So I suspect that in the typical case the correct behavior is to just 
> preserve the anonymous parameter as-is.
> 
> This is just my usual argument: I think the fixit should simply take the part 
> that doesn't need fixing from the original code, and include it textually. In 
> this case this would mean duplicating the text rather than simply not 
> touching it, but that's probably still more precise than writing code from 
> scratch.
I think you are right.  I'll update the `FIXME` comment before land this patch.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155641

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


[PATCH] D155641: [-Wunsafe-buffer-usage] Do not assert that function parameters have names.

2023-07-19 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
ziqingluo-90 marked an inline comment as done.
Closed by commit rG4b5f17e008c6: [-Wunsafe-buffer-usage] Do not assert that 
function parameters have names (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D155641?vs=541723&id=542188#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155641

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


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -156,4 +156,9 @@
   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
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1885,9 +1885,12 @@
   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() << ", "


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -156,4 +156,9 @@
   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
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1885,9 +1885,12 @@
   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() << ", "
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2023-07-19 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 542199.
ziqingluo-90 added a comment.

Address comments


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

https://reviews.llvm.org/D155524

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -107,3 +107,128 @@
 
   p[5]; // not to note since the associated warning is suppressed
 }
+
+
+// Test suppressing interacts with variable grouping:
+
+// The implication edges are: `a` -> `b` -> `c`.
+// If the unsafe operation on `a` is supressed, none of the variables
+// will be fixed.
+void suppressedVarInGroup() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// To show that if `a[5]` is not suppressed in the
+// `suppressedVarInGroup` function above, all variables will be fixed.
+void suppressedVarInGroup_control() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `a` is not suppressed. Variable `b` will still be
+// fixed when fixing `a`.
+void suppressedVarInGroup2() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Only variable `c` will be fixed
+// then.
+void suppressedVarInGroup3() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c` -> `a`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Since the implication graph
+// forms a cycle, all variables will be fixed.
+void suppressedVarInGroup4() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  c = a;
+}
+
+// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
+// Suppressing unsafe operations on variables in one group does not
+// affect other groups.
+void suppressedVarInGroup5() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * d;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d"
+  int * e;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e"
+  int * f;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f"
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+
+  d[5] = 5;
+  d = e;
+  e = f;
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1092,15 +1092,18 @@
 
   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);
+  // If no `WarningGadget`s ever matched, there is no unsafe operation

[PATCH] D155667: [-Wunsafe-buffer-usage] Check source location validity before using `TypeLoc`s

2023-07-19 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa6302b6934b3: [-Wunsafe-buffer-usage] Check source location 
validity before using `TypeLoc`s (authored by ziqingluo-90).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155667

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


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
@@ -33,6 +33,26 @@
   }
 }
 
+// The analysis requires accurate source location informations from
+// `TypeLoc`s of types of variable (parameter) declarations in order
+// to generate fix-its for them. But those information is not always
+// available (probably due to some bugs in clang but it is irrelevant
+// to the safe-buffer project).  The following is an example.  When
+// `_Atomic` is used, we cannot get valid source locations of the
+// pointee type of `unsigned *`.  The analysis gives up in such a
+// case.
+// CHECK-NOT: fix-it:
+void typeLocSourceLocationInvalid(_Atomic unsigned *map) { // 
expected-warning{{'map' is an unsafe pointer used for buffer access}}
+  map[5] = 5; // expected-note{{used in buffer access here}}
+}
+
+// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:33-[[@LINE+1]]:46}:"std::span 
map"
+void typeLocSourceLocationValid(unsigned *map) { // expected-warning{{'map' is 
an unsafe pointer used for buffer access}} \
+   expected-note{{change type 
of 'map' to 'std::span' to preserve bounds information}}
+  map[5] = 5; // expected-note{{used in buffer access here}}
+}
+// CHECK: 
fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}
 void typeLocSourceLocationValid(unsigned *map) {return 
typeLocSourceLocationValid(std::span(map, <# size #>));}\n"
+
 // We do not fix parameters participating unsafe operations for the
 // following functions/methods or function-like expressions:
 
@@ -128,4 +148,3 @@
   int tmp;
   tmp = x[5]; // expected-note{{used in buffer access here}}
 }
-
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1385,8 +1385,18 @@
   TypeLoc PteTyLoc = TyLoc.getUnqualifiedLoc().getNextTypeLoc();
   SourceLocation VDNameStartLoc = VD->getLocation();
 
-  if (!SM.isBeforeInTranslationUnit(PteTyLoc.getSourceRange().getEnd(),
-VDNameStartLoc)) {
+  if (!(VDNameStartLoc.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 when it happens.
+return std::nullopt;
+  }
+
+  // Note that TypeLoc.getEndLoc() returns the begin location of the last 
token:
+  SourceLocation PteEndOfTokenLoc =
+  Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
+
+  if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, VDNameStartLoc)) {
 // We only deal with the cases where the source text of the pointee type
 // appears on the left-hand side of the variable identifier completely,
 // including the following forms:
@@ -1401,13 +1411,8 @@
 // `PteTy` via source ranges.
 *QualifiersToAppend = PteTy.getQualifiers();
   }
-
-  // Note that TypeLoc.getEndLoc() returns the begin location of the last 
token:
-  SourceRange CSR{
-  PteTyLoc.getBeginLoc(),
-  Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts)};
-
-  return getRangeText(CSR, SM, LangOpts)->str();
+  return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
+  ->str();
 }
 
 // Returns the text of the name (with qualifiers) of a `FunctionDecl`.


Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
@@ -33,6 +33,26 @@
   }
 }
 
+// The analysis requires accurate source location informations from
+// `TypeLoc`s of types of variable (parameter) declarations in order
+// to generate fix-its for them. But those information is not always
+// available (probably due to some bugs in clang but it is irrelevant
+// to the safe-buffer project).  The following is an example.  When
+// `_Atomic` is used, we cannot get valid source locations of the
+// pointee type of `unsi

[PATCH] D150338: [-Wunsafe-buffer-usage] Improving insertion of the [[clang::unsafe_buffer_usage]] attribute

2023-07-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 abandoned this revision.
ziqingluo-90 added a comment.

In D150338#4503802 , @ziqingluo-90 
wrote:

> Re-landed in `a07a6f6c74a03405eccdcd3832acb2187d8b9c21`
>
> Moved the use of `clang::Sema` from `UnsafeBufferAnalysis` to 
> `AnalysisBasedWarnings`.

@NoQ Thanks for accepting it again, I'm closing this revision.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D150338

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


[PATCH] D156188: Refactor and improve for parameter fix-its

2023-07-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- 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.  Because we will give up on fixing a 
pointer type variable `v`, if we cannot obtain the source range of the pointee 
type of `v`.  Using unnamed types is just one of the many causes of such 
scenarios.  Besides, in some cases, we have no problem in referring to unnamed 
types.  Therefore, we remove the special handling since we have a more general 
solution already.

For example,

  typedef struct {int x;} UNNAMED_T;
  
  UNNAMED_T * x; //  we can refer to the unnamed struct using `UNNAMED_T`
  
  typedef struct {int x;} * UNNAMED_PTR;
  
  UNNAMED_PTR y; // we cannot obtain the source range of the pointee type of 
`y`,   so we will give up



- Move tests for cv-qualified parameters and unnamed types out of the 
"...-unsupported.cpp" test file.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156188

Files:
  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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
@@ -1,34 +1,21 @@
 // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fcxx-exceptions -fsafe-buffer-usage-suggestions -verify %s
 // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fcxx-exceptions -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions %s 2>&1 | FileCheck %s
 
-void const_ptr(int * const x) { // expected-warning{{'x' is an unsafe pointer used for buffer access}} expected-note{{change type of 'x' to 'std::span' to preserve bounds information}}
-  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:29}:"std::span const x"
-  int tmp = x[5]; // expected-note{{used in buffer access here}}
-}
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr(int * const x) {return const_ptr(std::span(x, <# size #>));}\n"
-
-void const_ptr_to_const(const int * const x) {// expected-warning{{'x' is an unsafe pointer used for buffer access}} expected-note{{change type of 'x' to 'std::span' to preserve bounds information}}
-  // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:44}:"std::span const x"
-  int tmp = x[5]; // expected-note{{used in buffer access here}}
-}
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr_to_const(const int * const x) {return const_ptr_to_const(std::span(x, <# size #>));}\n"
+typedef int * TYPEDEF_PTR;
+#define MACRO_PTR int*
 
-typedef struct {int x;} NAMED_UNNAMED_STRUCT; // an unnamed struct type named by a typedef
-typedef struct {int x;} * PTR_TO_ANON;// pointer to an unnamed struct
-typedef NAMED_UNNAMED_STRUCT * PTR_TO_NAMED;  // pointer to a named type
+// We CANNOT fix a pointer whose type is defined in a typedef or a
+// macro. Because if the typedef is changed after the fix, the fix
+// becomes incorrect and may not be noticed.
 
-// We can fix a pointer to a named type
-void namedPointeeType(NAMED_UNNAMED_STRUCT * p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}\ expected-note{{change type of 'p' to 'std::span' to preserve bounds information}}
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:23-[[@LINE-1]]:47}:"std::span p"
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
+void typedefPointer(TYPEDEF_PTR p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
   if (++p) {  // expected-note{{used in pointer arithmetic here}}
-// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:10}:"(p = p.subspan(1)).data()"
   }
 }
-// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void namedPointeeType(NAMED_UNNAMED_STRUCT * p) {return namedPointeeType(std::span(p, <# size #>));}\n"
 
-// We CANNOT fix a pointer to an unnamed type
-// CHECK-NOT: fix-it:
-void unnamedPointeeType(PTR_TO_ANON p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
+void macroPointer(MACRO_PTR p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
   if (++p) {  // expected-note{{used in pointer arithmetic

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

2023-07-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156189

Files:
  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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,28 +80,16 @@
   );
 }
 
-void testArraySubscriptsWithAuto(int *p, int **pp) {
+void testArraySubscriptsWithAuto() {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
-		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
-
+  // We do not fix a declaration if the type is `auto`. Because the actual type may change later.
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
   foo(ap1[1]);// expected-note{{used in buffer access here}}
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
-  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
-
+  // In case the type is `auto *`, we know it must be a pointer. We can fix it.
+  auto * ap2 = a; // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+ expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
   foo(ap2[1]);// expected-note{{used in buffer access here}}
-
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
-		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
-
-  foo(ap3[1][1]); // expected-note{{used in buffer access here}}
-  // expected-warning@-1{{unsafe buffer access}}
-
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
-  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
-
-  foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
 
 void testUnevaluatedContext(int * p) {// no-warning
@@ -358,8 +346,9 @@
 }
 
 void testMultiLineDeclStmt(int * p) {
-  auto
+  int
 
+  *
 
   ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
  	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
@@ -15,7 +15,7 @@
 int bar(int *ptr);
 
 void uneval_context_fix_pointer_dereference() {
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
@@ -30,7 +30,7 @@
 }
 
 void uneval_context_fix_pointer_array_access() {
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
@@ -41,7 +41,7 @@
 }
 
 void uneval_context_fix_pointer_reference() {
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
@@ -63,7 +63,7 @@
 
 // FIXME: Emit fixits for each of the below use.
 void uneval_context_fix_pointer_dereference_not_handled() {
-  auto p = new int[10];
+  int* p = new int[10];
   int tmp = p[5];
 
   foo(sizeof(*p), sizeof(decltype(*p)));
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp
+++ clang/test/SemaCXX/warn-un

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

2023-07-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: 
clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp:242
   ptr[2] = 30;
-  auto p = new int[10];
-  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span 
p"

We no longer generate fix-its for a variable declaration where the type 
specifier is just `auto`.   This is to avoid that fix-its may become incorrect 
when the adopting codebase changes.  For example,

```
int x;
auto p = &x;
```
If we fix the declaration of `p` to `std::span p{&x, 1}`,  and later `int 
x` is changed to `long x`,  the program becomes incorrect and the programmer 
may not know that the type of `p` should simply follow the type of `x`.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D156189

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


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

2023-07-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

We have to give up on fixing a variable declaration if it has specifiers that 
are not supported yet.  We cannot support them for now due to the combination 
of following challenges:

- Sometimes we do not have accurate source range information for the type 
specifiers of a variable declaration,  especially when cv-qualifiers are used 
(source location is not provided for type qualifiers 
).
   So it is hard to generate fix-its that only touch type specifiers for a 
declaration.
- We do not have the source range information for most declaration specifiers, 
such as storage specifiers.  So it is hard to tell whether a fix-it may quietly 
remove a specifier by accidentally overwriting it.
- When the declarator is not a trivial identifier (e.g., `int a[]` as a 
parameter decl),  we have to replace the whole declaration in order to fix it 
(e.g., to `std::span a`).  If there are other specifiers involved, they 
may be accidentally removed (e.g.,  fix `int [[some_type_attribute]] a[]` to 
`std::span a` is incorrect).

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.

For example, storage specifiers could be always placed at the beginning of a 
declaration. So both `const static int * x` and `static const int * x` will be 
fixed to `static std::span x`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156192

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -56,12 +56,46 @@
   // 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 = a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:18-[[@LINE-1]]:33}:"std::span x"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:34-[[@LINE-2]]:34}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:35-[[@LINE-3]]:35}:", 10}"
+  const int * y [[deprecated]];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span y"
+
   int tmp;
+
   tmp = p[5];
   tmp = q[5];
+  tmp = x[5];
+  tmp = y[5];
 }
 
 
+void local_variable_unsupported_specifiers() {
+  int a[10];
+  const int * p [[deprecated]] = a; //  not supported because the attribute overlaps the source range of the declaration
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  static const int * q = a; //  storage specifier not supported yet
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  extern int * x; //  storage specifier not supported yet
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  constexpr int * y = 0; //  `constexpr` specifier not supported yet
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  int tmp;
+
+  tmp = p[5];
+  tmp = q[5];
+  tmp = x[5];
+  tmp = y[5];
+}
+
+
+
 void local_array_subscript_variable_extent() {
   int n = 10;
   int tmp;
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1381,6 +1381,26 @@
   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 accruate source ranges of cv-qualified type
+// specifiers.
+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->

[PATCH] D155814: Fix the linting problems which causes `clang/utils/ci/run-buildbot check-format` to return 1.

2023-07-24 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 accepted this revision.
ziqingluo-90 added a comment.
This revision is now accepted and ready to land.

Thanks for cleaning the trailing whites.


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

https://reviews.llvm.org/D155814

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


[PATCH] D154880: [-Wunsafe-buffer-usage][WIP] Add a facility for debugging low fixit coverage.

2023-07-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added a comment.

This is a lot of work, thank you @t-rasmud & @NoQ !

I have a minor suggestion: can we use some macros to make the debug stub even 
shorter?
The prefix `"failed to produce fixit for declaration"`  is used in many places 
so probably we do not have to repeat it everywhere.  And, maybe some prefixes 
could be a bit more blurry so that they can be shared.  For example, we can 
just replace `"failed to produce fixit for parm var decl"` with `"failed to 
produce fixit for declaration"`.   We have source location and more specific 
message attached to the note so we are not losing information I think.

I'm imagining something like this:

  #define DEBUG_NOTE_DECL_FAIL(D, Msg)  \
  Handler.addDebugNoteForVar((D), (D)->getBeginLoc(),  "failed to produce fixit 
for declaration "##Msg)
  
  #define DEBUG_NOTE_GADGET_FAIL(Gadget, Msg)  ...

Does it make sense to you?


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

https://reviews.llvm.org/D154880

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


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

2023-07-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 544104.

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

https://reviews.llvm.org/D156188

Files:
  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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp
@@ -1,34 +1,21 @@
 // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fcxx-exceptions -fsafe-buffer-usage-suggestions -verify %s
 // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fcxx-exceptions -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions %s 2>&1 | FileCheck %s
 
-void const_ptr(int * const x) { // expected-warning{{'x' is an unsafe pointer used for buffer access}} expected-note{{change type of 'x' to 'std::span' to preserve bounds information}}
-  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:29}:"std::span const x"
-  int tmp = x[5]; // expected-note{{used in buffer access here}}
-}
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr(int * const x) {return const_ptr(std::span(x, <# size #>));}\n"
-
-void const_ptr_to_const(const int * const x) {// expected-warning{{'x' is an unsafe pointer used for buffer access}} expected-note{{change type of 'x' to 'std::span' to preserve bounds information}}
-  // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:44}:"std::span const x"
-  int tmp = x[5]; // expected-note{{used in buffer access here}}
-}
-// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr_to_const(const int * const x) {return const_ptr_to_const(std::span(x, <# size #>));}\n"
+typedef int * TYPEDEF_PTR;
+#define MACRO_PTR int*
 
-typedef struct {int x;} NAMED_UNNAMED_STRUCT; // an unnamed struct type named by a typedef
-typedef struct {int x;} * PTR_TO_ANON;// pointer to an unnamed struct
-typedef NAMED_UNNAMED_STRUCT * PTR_TO_NAMED;  // pointer to a named type
+// We CANNOT fix a pointer whose type is defined in a typedef or a
+// macro. Because if the typedef is changed after the fix, the fix
+// becomes incorrect and may not be noticed.
 
-// We can fix a pointer to a named type
-void namedPointeeType(NAMED_UNNAMED_STRUCT * p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}\ expected-note{{change type of 'p' to 'std::span' to preserve bounds information}}
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:23-[[@LINE-1]]:47}:"std::span p"
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
+void typedefPointer(TYPEDEF_PTR p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
   if (++p) {  // expected-note{{used in pointer arithmetic here}}
-// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:10}:"(p = p.subspan(1)).data()"
   }
 }
-// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void namedPointeeType(NAMED_UNNAMED_STRUCT * p) {return namedPointeeType(std::span(p, <# size #>));}\n"
 
-// We CANNOT fix a pointer to an unnamed type
-// CHECK-NOT: fix-it:
-void unnamedPointeeType(PTR_TO_ANON p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
+// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
+void macroPointer(MACRO_PTR p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
   if (++p) {  // expected-note{{used in pointer arithmetic here}}
   }
 }
@@ -148,3 +135,17 @@
   int tmp;
   tmp = x[5]; // expected-note{{used in buffer access here}}
 }
+
+#define MACRO_NAME MyName
+
+// CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]
+void macroIdentifier(int * MACRO_NAME) { // The fix-it ends with a macro. It will be discarded due to overlap with macros. \
+	expected-warning{{'MyName' is an unsafe pointer used for buffer access}}
+  if (++MyName){} // expected-note{{used in pointer arithmetic here}}
+}
+
+// CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]
+void parmHasNoName(int *p, int *) { // cannot fix the function because there is one parameter has no name. \
+   expected-warning{{'p' is an unsafe pointer used for buffer access}}
+  p[5] = 5; // expected-note{{used in buffer access here}}
+}
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp
@@ -147,18 +147,78 @@
   if (++a){}
 }
 
-// Make sure we do not generate fixes for the following cases:
+// Tests parameters with cv-qualifiers
 
-#define MACRO_NAME MyName
+void co

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

2023-07-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 updated this revision to Diff 544111.

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

https://reviews.llvm.org/D156189

Files:
  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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -80,28 +80,16 @@
   );
 }
 
-void testArraySubscriptsWithAuto(int *p, int **pp) {
+void testArraySubscriptsWithAuto() {
   int a[10];
-  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
-		 expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
-
+  // We do not fix a declaration if the type is `auto`. Because the actual type may change later.
+  auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
   foo(ap1[1]);// expected-note{{used in buffer access here}}
 
-  auto ap2 = p;   // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
-  		 expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
-
+  // In case the type is `auto *`, we know it must be a pointer. We can fix it.
+  auto * ap2 = a; // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
+ expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
   foo(ap2[1]);// expected-note{{used in buffer access here}}
-
-  auto ap3 = pp;  // expected-warning{{'ap3' is an unsafe pointer used for buffer access}} \
-		 expected-note{{change type of 'ap3' to 'std::span' to preserve bounds information}}
-
-  foo(ap3[1][1]); // expected-note{{used in buffer access here}}
-  // expected-warning@-1{{unsafe buffer access}}
-
-  auto ap4 = *pp; // expected-warning{{'ap4' is an unsafe pointer used for buffer access}} \
-  		 expected-note{{change type of 'ap4' to 'std::span' to preserve bounds information}}
-
-  foo(ap4[1]);// expected-note{{used in buffer access here}}
 }
 
 void testUnevaluatedContext(int * p) {// no-warning
@@ -358,8 +346,9 @@
 }
 
 void testMultiLineDeclStmt(int * p) {
-  auto
+  int
 
+  *
 
   ap1 = p;  // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
  	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp
@@ -15,7 +15,7 @@
 int bar(int *ptr);
 
 void uneval_context_fix_pointer_dereference() {
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
@@ -30,7 +30,7 @@
 }
 
 void uneval_context_fix_pointer_array_access() {
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
@@ -41,7 +41,7 @@
 }
 
 void uneval_context_fix_pointer_reference() {
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
@@ -63,7 +63,7 @@
 
 // FIXME: Emit fixits for each of the below use.
 void uneval_context_fix_pointer_dereference_not_handled() {
-  auto p = new int[10];
+  int* p = new int[10];
   int tmp = p[5];
 
   foo(sizeof(*p), sizeof(decltype(*p)));
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp
@@ -4,7 +4,7 @@
 
 void basic_dereference() {
   int tmp;
-  auto p = new int[10];
+  int* p = new int[10];
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:

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

2023-07-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:1824
+
+  if (!IdentText)
+return {};

t-rasmud wrote:
> When will this condition be satisfied? I just want to understand if there are 
> code examples where there is no identifier text or is it that 
> `getVarDeclIdentifierText` fails.
`!IdentText` if `getVarDeclIdentifierText` fails.  

`getVarDeclIdentifierText` assumes that the identifier text is a single token.  
 It starts with the begin location of the identifier and tries to obtain the 
end location via `Lexer::getLocForEndOfToken`.
So it could fail if 1) the identifier text is not a single token or 2) any 
unexpected failure happens in obtaining valid source locations.

Case 2) could be rare I think since I don't know when it could happen.  We just 
need to assume that it is NOT always successful in manipulating source 
locations.

For case 1), I originally thought that if the identifier is expanded from a 
macro with parameters, e.g., 
```
#define MACRO(x)  name
int * MACRO(x);
```
, `MACRO(x)` is not a single token.  But it seems I was wrong---it is one token 
to the `Lexer`.  See my test at 
`warn-unsafe-buffer-usage-fixits-local-var-span.cpp:186--206`.

In conclusion, `getVarDeclIdentifierText` could fail but it should be rare.


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

https://reviews.llvm.org/D156189

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


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

2023-07-25 Thread Ziqing Luo via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
ziqingluo-90 marked an inline comment as done.
Closed by commit rGcfcf76c6ad72: [-Wunsafe-buffer-usage] Ignore the 
FixableGadgets that will not be fixed at an… (authored by ziqingluo-90).

Changed prior to commit:
  https://reviews.llvm.org/D155524?vs=542199&id=544151#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155524

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

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
===
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp
@@ -107,3 +107,128 @@
 
   p[5]; // not to note since the associated warning is suppressed
 }
+
+
+// Test suppressing interacts with variable grouping:
+
+// The implication edges are: `a` -> `b` -> `c`.
+// If the unsafe operation on `a` is supressed, none of the variables
+// will be fixed.
+void suppressedVarInGroup() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// To show that if `a[5]` is not suppressed in the
+// `suppressedVarInGroup` function above, all variables will be fixed.
+void suppressedVarInGroup_control() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `a` is not suppressed. Variable `b` will still be
+// fixed when fixing `a`.
+void suppressedVarInGroup2() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Only variable `c` will be fixed
+// then.
+void suppressedVarInGroup3() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+}
+
+// The implication edges are: `a` -> `b` -> `c` -> `a`.
+// The unsafe operation on `b` is supressed, while the unsafe
+// operation on `c` is not suppressed. Since the implication graph
+// forms a cycle, all variables will be fixed.
+void suppressedVarInGroup4() {
+  int * a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a"
+  int * b;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b"
+  int * c;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c"
+
+  c[5] = 5;
+#pragma clang unsafe_buffer_usage begin
+  b[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+  c = a;
+}
+
+// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
+// Suppressing unsafe operations on variables in one group does not
+// affect other groups.
+void suppressedVarInGroup5() {
+  int * a;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * b;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * c;
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+  int * d;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d"
+  int * e;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e"
+  int * f;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f"
+
+#pragma clang unsafe_buffer_usage begin
+  a[5] = 5;
+#pragma clang unsafe_buffer_usage end
+  a = b;
+  b = c;
+
+  d[5] = 5;
+  d = e;
+  e = f;
+}
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1090,16 +1090,6 @@
   }
 
   M.match(*D->getBody(), D->getASTContext());
-
-  // Gadgets "claim" variables they're responsible for. Once

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

2023-07-25 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 added inline comments.



Comment at: clang/lib/Analysis/UnsafeBufferUsage.cpp:1095-1102
   // 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);
 }

NoQ wrote:
> ziqingluo-90 wrote:
> > NoQ wrote:
> > > Let's also skip this part when there are no warning gadgets? Your initial 
> > > patch was clearing the `FixableGadgets` list so it was naturally skipped, 
> > > but now it's active again.
> > Oh, I intentionally chose not to skip it:
> >  - 1. To keep the `Tracker` consistent with `CB.FixableGadgets` in case in 
> > the future we want to use these two objects even if there is no 
> > `WarningGadget`; 
> >  - 2. The `findGadgets` function can stay as straightforward as its name 
> > suggests.  It doesn't have to know the specific optimization for empty 
> > `WarningGadget`s.
> > 
> > But the two thoughts above probably aren't important enough.  I will change 
> > it back to skipping the loop when there is no warnings.
> > 
> > 
> Hmm, I agree that this turns into a confusing contract. Maybe move this loop 
> out of the function, to the call site, so that it was more obvious that we 
> skip it? This would leave the tracker in a mildly inconsistent state at 
> return, so the caller will have to make it consistent by doing the claiming, 
> but that's arguably less confusing because we clearly communicate that this 
> is an optional step. So the function will do exactly what it says it'll do: 
> find gadgets and that's it.
> 
> Separately, we can probably consider not searching for fixable gadgets at all 
> when no warning gadgets are found. This could be a performance improvement, 
> but definitely a story for another time.
Thanks for the suggestion.  I moved the loop to the end of the call to 
`findGadgets` and landed it.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D155524

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


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

2023-07-27 Thread Ziqing Luo via Phabricator via cfe-commits
ziqingluo-90 created this revision.
Herald added a reviewer: NoQ.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156474

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

Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2163,6 +2163,41 @@
   Sema &S;
   bool SuggestSuggestions;  // Recommend -fsafe-buffer-usage-suggestions?
 
+  // Lists as a string the names of variables in `VarGroupForVD` except for `VD`
+  // itself:
+  std::string listVariableGroupAsString(
+  const VarDecl *VD, const ArrayRef &VarGroupForVD) const {
+if (VarGroupForVD.size() <= 1)
+  return "";
+
+std::vector VarNames;
+auto PutInQuotes = [](StringRef S) -> std::string {
+  return "'" + S.str() + "'";
+};
+
+for (auto *V : VarGroupForVD) {
+  if (V == VD)
+continue;
+  VarNames.push_back(V->getName());
+}
+if (VarNames.size() == 1) {
+  return PutInQuotes(VarNames[0]);
+}
+if (VarNames.size() == 2) {
+  return PutInQuotes(VarNames[0]) + " and " + PutInQuotes(VarNames[1]);
+}
+assert(VarGroupForVD.size() > 3);
+const unsigned N = VarNames.size() -
+   2; // need to print the last two names as "..., X, and Y"
+std::string AllVars = "";
+
+for (unsigned I = 0; I < N; ++I)
+  AllVars.append(PutInQuotes(VarNames[I]) + ", ");
+AllVars.append(PutInQuotes(VarNames[N]) + ", and " +
+   PutInQuotes(VarNames[N + 1]));
+return AllVars;
+  }
+
 public:
   UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions)
 : S(S), SuggestSuggestions(SuggestSuggestions) {}
@@ -2219,62 +2254,25 @@
   }
 
   void handleUnsafeVariableGroup(const VarDecl *Variable,
- const DefMapTy &VarGrpMap,
- FixItList &&Fixes) override {
+ const VariableGroupsManager &VarGrpMgr,
+ FixItList &&Fixes) override {
 assert(!SuggestSuggestions &&
"Unsafe buffer usage fixits displayed without suggestions!");
 S.Diag(Variable->getLocation(), diag::warn_unsafe_buffer_variable)
 << Variable << (Variable->getType()->isPointerType() ? 0 : 1)
 << Variable->getSourceRange();
 if (!Fixes.empty()) {
-  const auto VarGroupForVD = VarGrpMap.find(Variable)->second;
+  const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Variable);
   unsigned FixItStrategy = 0; // For now we only have 'std::span' strategy
   const auto &FD = S.Diag(Variable->getLocation(),
   diag::note_unsafe_buffer_variable_fixit_group);
 
   FD << Variable << FixItStrategy;
-  std::string AllVars = "";
-  if (VarGroupForVD.size() > 1) {
-if (VarGroupForVD.size() == 2) {
-  if (VarGroupForVD[0] == Variable) {
-AllVars.append("'" + VarGroupForVD[1]->getName().str() + "'");
-  } else {
-AllVars.append("'" + VarGroupForVD[0]->getName().str() + "'");
-  }
-} else {
-  bool first = false;
-  if (VarGroupForVD.size() == 3) {
-for (const VarDecl * V : VarGroupForVD) {
-  if (V == Variable) {
-continue;
-  }
-  if (!first) {
-first = true;
-AllVars.append("'" + V->getName().str() + "'" + " and ");
-  } else {
-AllVars.append("'" + V->getName().str() + "'");
-  }
-}
-  } else {
-for (const VarDecl * V : VarGroupForVD) {
-  if (V == Variable) {
-continue;
-  }
-  if (VarGroupForVD.back() != V) {
-AllVars.append("'" + V->getName().str() + "'" + ", ");
-  } else {
-AllVars.append("and '" + V->getName().str() + "'");
-  }
-}
-  }
-}
-FD << AllVars << 1;
-  } else {
-FD << "" << 0;
-  }
-
-  for (const auto &F : Fixes)
+  FD << listVariableGroupAsString(Variable, VarGroupForVD)
+ << (VarGroupForVD.size() > 1);
+  for (const auto &F : Fixes) {
 FD << F;
+  }
 }
   }
 
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1137,7 +1137,11 @@
 }
 
 struct Fixa

  1   2   3   >