[-Wunsafe-buffer-usage] Emit Fix-Its only for C++20 and later standards

The transformation strategy we are bringing up heavily relies on std::span which was introduced as part of C++20.

Differential Revision: https://reviews.llvm.org/D143455
This commit is contained in:
Jan Korous 2023-02-09 17:01:30 -08:00
parent f71de259c3
commit 8b6ae9bd74
4 changed files with 115 additions and 23 deletions

View File

@ -53,7 +53,8 @@ public:
// This function invokes the analysis and allows the caller to react to it
// through the handler class.
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler);
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler,
bool EmitFixits);
namespace internal {
// Tests if any two `FixItHint`s in `FixIts` conflict. Two `FixItHint`s

View File

@ -1008,7 +1008,8 @@ getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) {
}
void clang::checkUnsafeBufferUsage(const Decl *D,
UnsafeBufferUsageHandler &Handler) {
UnsafeBufferUsageHandler &Handler,
bool EmitFixits) {
assert(D && D->getBody());
WarningGadgetSets UnsafeOps;
@ -1016,40 +1017,46 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
DeclUseTracker Tracker;
{
// FIXME: We could skip even matching Fixables' matchers if EmitFixits ==
// false.
auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D, Handler);
UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets));
Tracker = std::move(TrackerRes);
}
// Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
for (auto it = FixablesForUnsafeVars.byVar.cbegin();
it != FixablesForUnsafeVars.byVar.cend();) {
// FIXME: Support ParmVarDecl as well.
if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) {
it = FixablesForUnsafeVars.byVar.erase(it);
} else {
++it;
std::map<const VarDecl *, FixItList> FixItsForVariable;
if (EmitFixits) {
// Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
for (auto it = FixablesForUnsafeVars.byVar.cbegin();
it != FixablesForUnsafeVars.byVar.cend();) {
// FIXME: Support ParmVarDecl as well.
if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) {
it = FixablesForUnsafeVars.byVar.erase(it);
} else {
++it;
}
}
llvm::SmallVector<const VarDecl *, 16> UnsafeVars;
for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar)
UnsafeVars.push_back(VD);
Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
FixItsForVariable = getFixIts(FixablesForUnsafeVars, NaiveStrategy, Tracker,
D->getASTContext(), Handler);
// FIXME Detect overlapping FixIts.
}
llvm::SmallVector<const VarDecl *, 16> UnsafeVars;
for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar)
UnsafeVars.push_back(VD);
Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
std::map<const VarDecl *, FixItList> FixItsForVariable =
getFixIts(FixablesForUnsafeVars, NaiveStrategy, Tracker,
D->getASTContext(), Handler);
// FIXME Detect overlapping FixIts.
for (const auto &G : UnsafeOps.noVar) {
Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false);
}
for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
auto FixItsIt = FixItsForVariable.find(VD);
auto FixItsIt =
EmitFixits ? FixItsForVariable.find(VD) : FixItsForVariable.end();
Handler.handleFixableVariable(VD, FixItsIt != FixItsForVariable.end()
? std::move(FixItsIt->second)
: FixItList{});

View File

@ -2516,7 +2516,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
if (!Diags.isIgnored(diag::warn_unsafe_buffer_operation, D->getBeginLoc()) ||
!Diags.isIgnored(diag::warn_unsafe_buffer_variable, D->getBeginLoc())) {
UnsafeBufferUsageReporter R(S);
checkUnsafeBufferUsage(D, R);
checkUnsafeBufferUsage(
D, R,
/*EmitFixits=*/S.getLangOpts().CPlusPlus20);
}
// If none of the previous checks caused a CFG build, trigger one here

View File

@ -0,0 +1,82 @@
// RUN: %clang_cc1 -x c -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=c89 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=gnu89 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=iso9899:1990 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=c17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=gnu17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=iso9899:2017 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c -std=c2x -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c++ -std=c++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c++ -std=gnu++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c++ -std=c++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x c++ -std=gnu++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x objective-c++ -std=c++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x objective-c++ -std=gnu++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x objective-c++ -std=c++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: %clang_cc1 -x objective-c++ -std=gnu++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// CHECK-NOT: fix-it:
typedef int * Int_ptr_t;
typedef int Int_t;
void local_array_subscript_simple() {
int tmp;
int *p;
const int *q;
tmp = p[5];
tmp = q[5];
Int_ptr_t x;
Int_ptr_t y;
Int_t * z;
Int_t * w;
tmp = x[5];
tmp = y[5];
tmp = z[5];
tmp = w[5];
}
void local_ptr_to_array() {
int tmp;
int n = 10;
int a[10];
int b[n];
int *p = a;
int *q = b;
tmp = p[5];
tmp = q[5];
}
void local_ptr_addrof_init() {
int var;
int * q = &var;
var = q[5];
}
void decl_without_init() {
int tmp;
int * p;
Int_ptr_t q;
tmp = p[5];
tmp = q[5];
}
void explict_cast() {
int tmp;
int * p;
tmp = p[5];
int a;
char * q = (char *)&a;
tmp = (int) q[5];
void * r = &a;
char * s = (char *) r;
tmp = (int) s[5];
}