Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def =================================================================== --- clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -29,9 +29,10 @@ WARNING_GADGET(Decrement) WARNING_GADGET(ArraySubscript) WARNING_GADGET(PointerArithmetic) -FIXABLE_GADGET(ULCArraySubscript) +FIXABLE_GADGET(ULCArraySubscript) FIXABLE_GADGET(DerefSimplePtrArithFixable) +FIXABLE_GADGET(PointerDereference) #undef FIXABLE_GADGET #undef WARNING_GADGET Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -643,6 +643,47 @@ } }; +class PointerDereferenceGadget: public FixableGadget { + static constexpr const char *const BaseDeclRefExprTag = "BaseDRE"; + + const DeclRefExpr *BaseDeclRefExpr = nullptr; + const UnaryOperator *Op; + const MatchFinder::MatchResult* result; + +public: + PointerDereferenceGadget(const MatchFinder::MatchResult &Result) + : FixableGadget(Kind::PointerDereference), + BaseDeclRefExpr(Result.Nodes.getNodeAs(BaseDeclRefExprTag)), + Op(Result.Nodes.getNodeAs("op")) {result = &Result;} + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::PointerDereference; + } + + static Matcher matcher() { + auto Target = + unaryOperator( + hasOperatorName("*"), + has( + expr( + ignoringParenImpCasts(declRefExpr().bind(BaseDeclRefExprTag)) + ) + ) + ).bind("op"); + + return expr(isInUnspecifiedLvalueContext(Target)); + } + + DeclUseList getClaimedVarUseSites() const override { + return {BaseDeclRefExpr}; + } + + virtual const Stmt *getBaseStmt() const final { + return nullptr; + } + + virtual std::optional getFixits(const Strategy &S) const override; +}; /// Scan the function and return a list of gadgets found with provided kits. static std::tuple findGadgets(const Decl *D) { @@ -857,6 +898,30 @@ return LastToken->getEndLoc(); } +std::optional +PointerDereferenceGadget::getFixits(const Strategy &S) const { + if (const auto *DRE = dyn_cast(Op->getSubExpr()->IgnoreImpCasts())) + if (const auto *VD = dyn_cast(DRE->getDecl())) { + if (S.lookup(VD) == Strategy::Kind::Span) { + ASTContext& Ctx = VD->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + DeclarationNameInfo name = DRE->getNameInfo(); + // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0] + // Deletes the *operand + CharSourceRange derefRange = clang::CharSourceRange::getCharRange(Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1)); + // Inserts the [0] + Optional endOfOperand = getPassedLoc(DRE, SM, Ctx.getLangOpts()); + + return FixItList{{ + FixItHint::CreateRemoval(derefRange), + FixItHint::CreateInsertion(endOfOperand.value(), "[0]") + }}; + } + } + llvm_unreachable("unsupported strategies for FixableGadgets"); + return std::nullopt; +} + // For a non-null initializer `Init` of `T *` type, this function returns // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it // to output stream. Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp @@ -0,0 +1,44 @@ +// RUN: cp %s %t.cpp +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fsyntax-only -fixit %t.cpp +// RUN: grep -v CHECK %t.cpp | FileCheck %s + +// TODO test we don't mess up vertical whitespace +// TODO test different whitespaces +// TODO test different contexts + // when it's on the right side + +void basic() { + int *ptr; + *(ptr+5)=1; + // CHECK-DAG: ptr[5]=1; + int val = *ptr; + // CHECK-DAG: int val = ptr[0]; +} + +int return_method() { + int *ptr; + *(ptr+5)=1; + // CHECK-DAG: ptr[5]=1; + return *ptr; + // CHECK-DAG: return ptr[0]; +} + +void foo(int v) { + +} + +void method_invocation() { + int *ptr; + *(ptr+5)=1; + // CHECK-DAG: ptr[5]=1; + foo(*ptr); + // CHECK-DAG: foo(ptr[0]); + int k = *ptr + 20; + // CHECK-DAG: int k = ptr[0] + 20; +} + +void method_not_handled() { + int *ptr; + *(ptr+5)=1; + int *k = &*ptr; +}