ziqingluo-90 updated this revision to Diff 556070.
ziqingluo-90 added a comment.
Add a test
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.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,44 @@
+class Trie:
+ def __init__(self, name):
+ self.name = name
+ self.children = {}
+ 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")
+
+def main():
+ while True:
+ try:
+ line = input()
+ except EOFError:
+ break
+
+ words = line.split(',')
+ words = [word.strip() for word in words]
+ MyTrie = Root;
+ for word in words:
+ MyTrie = MyTrie.add(word)
+
+ Root.print(0)
+
+if __name__ == "__main__":
+ main()
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/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: | python3 %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/lib/Analysis/UnsafeBufferUsage.cpp
===================================================================
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -8,6 +8,7 @@
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
@@ -22,6 +23,84 @@
using namespace clang;
using namespace ast_matchers;
+#ifndef NDEBUG
+namespace {
+static std::string printUnaryOperator(const Stmt *Stmt) {
+ const UnaryOperator *UO = cast<UnaryOperator>(Stmt);
+ return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
+}
+
+static std::string printBinaryOperator(const Stmt *Stmt) {
+ const BinaryOperator *BO = cast<BinaryOperator>(Stmt);
+ StringRef BOOpStr = BO->getOpcodeStr();
+
+ if (BO->getOpcode() == BinaryOperator::Opcode::BO_Comma)
+ BOOpStr = "COMMA"; // Do not print `,` as it is used as the separator
+ return "BinaryOperator(" + BOOpStr.str() + ")";
+}
+
+static std::string printCompoundAssignOperator(const Stmt *Stmt) {
+ return printBinaryOperator(Stmt);
+}
+
+static std::string printImplicitCastExpr(const Stmt *Stmt) {
+ const CastExpr *CE = cast<CastExpr>(Stmt);
+ return "ImplicitCastExpr(" + std::string(CE->getCastKindName()) + ")";
+}
+
+// Prints the `StmtClass` of the given `Stmt`. For `UnaryOperator`s and
+// `BinaryOperator`s, it also prints the operator. For `CastExpr`, it also
+// prints the cast kind.
+static std::optional<std::string> printStmtClass(const Stmt *Stmt) {
+ switch (Stmt->getStmtClass()) {
+#define STMT(CLASS, PARENT) \
+ case Stmt::CLASS##Class: \
+ return #CLASS;
+#define UNARYOPERATOR(TYPE, BASE) \
+ case Stmt::TYPE##Class: \
+ return print##TYPE(Stmt);
+#define BINARYOPERATOR(TYPE, BASE) \
+ case Stmt::TYPE##Class: \
+ return print##TYPE(Stmt);
+#define IMPLICITCASTEXPR(TYPE, BASE) \
+ case Stmt::TYPE##Class: \
+ return print##TYPE(Stmt);
+#define ABSTRACT_STMT(STMT)
+#include "clang/AST/StmtNodes.inc"
+ default:
+ return std::nullopt;
+ }
+}
+
+// Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
+// "DRE, parent-of-DRE, grandparent-of-DRE, ...".
+static std::string getDREAncestorString(const DeclRefExpr *DRE,
+ ASTContext &Ctx) {
+ std::stringstream SS;
+ const Stmt *St = DRE;
+
+ do {
+ if (auto Printed = printStmtClass(St))
+ SS << *Printed;
+ else
+ return "unavailable due to unknown statement: " +
+ std::string(St->getStmtClassName());
+
+ DynTypedNodeList StParents = Ctx.getParents(*St);
+
+ if (StParents.size() > 1)
+ return "unavailable due to multiple parents";
+ if (StParents.size() == 0)
+ break;
+ St = StParents.begin()->get<Stmt>();
+ if (St)
+ SS << ", ";
+ } while (St);
+ return SS.str();
+}
+} // namespace
+#endif /* NDEBUG */
+
namespace clang::ast_matchers {
// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
// except for those belonging to a different callable of "n".
@@ -2478,11 +2557,15 @@
#ifndef NDEBUG
auto AllUnclaimed = Tracker.getUnclaimedUses(it->first);
for (auto UnclaimedDRE : AllUnclaimed) {
- Handler.addDebugNoteForVar(
- it->first, UnclaimedDRE->getBeginLoc(),
- ("failed to produce fixit for '" + it->first->getNameAsString() +
- "' : has an unclaimed use"));
- }
+ std::string UnclaimedUseTrace =
+ getDREAncestorString(UnclaimedDRE, D->getASTContext());
+
+ Handler.addDebugNoteForVar(
+ it->first, UnclaimedDRE->getBeginLoc(),
+ ("failed to produce fixit for '" + it->first->getNameAsString() +
+ "' : has an unclaimed use\nThe unclaimed DRE trace: " +
+ UnclaimedUseTrace));
+ }
#endif
it = FixablesForAllVars.byVar.erase(it);
} else if (it->first->isInitCapture()) {
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits