@@ -316,6 +316,15 @@ class Gadget {
316
316
317
317
Kind getKind () const { return K; }
318
318
319
+ #ifndef NDEBUG
320
+ StringRef getDebugName () const {
321
+ switch (K) {
322
+ #define GADGET (x ) case Kind::x: return #x;
323
+ #include " clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
324
+ }
325
+ }
326
+ #endif
327
+
319
328
virtual bool isWarningGadget () const = 0;
320
329
virtual const Stmt *getBaseStmt () const = 0;
321
330
@@ -565,7 +574,11 @@ class PointerInitGadget : public FixableGadget {
565
574
566
575
virtual std::optional<FixItList> getFixits (const Strategy &S) const override ;
567
576
568
- virtual const Stmt *getBaseStmt () const override { return nullptr ; }
577
+ virtual const Stmt *getBaseStmt () const override {
578
+ // FIXME: This needs to be the entire DeclStmt, assuming that this method
579
+ // makes sense at all on a FixableGadget.
580
+ return PtrInitRHS;
581
+ }
569
582
570
583
virtual DeclUseList getClaimedVarUseSites () const override {
571
584
return DeclUseList{PtrInitRHS};
@@ -613,7 +626,11 @@ class PointerAssignmentGadget : public FixableGadget {
613
626
614
627
virtual std::optional<FixItList> getFixits (const Strategy &S) const override ;
615
628
616
- virtual const Stmt *getBaseStmt () const override { return nullptr ; }
629
+ virtual const Stmt *getBaseStmt () const override {
630
+ // FIXME: This should be the binary operator, assuming that this method
631
+ // makes sense at all on a FixableGadget.
632
+ return PtrLHS;
633
+ }
617
634
618
635
virtual DeclUseList getClaimedVarUseSites () const override {
619
636
return DeclUseList{PtrLHS, PtrRHS};
@@ -845,6 +862,16 @@ class DeclUseTracker {
845
862
});
846
863
}
847
864
865
+ UseSetTy getUnclaimedUses (const VarDecl *VD) const {
866
+ UseSetTy ReturnSet;
867
+ for (auto use : *Uses) {
868
+ if (use->getDecl ()->getCanonicalDecl () == VD->getCanonicalDecl ()) {
869
+ ReturnSet.insert (use);
870
+ }
871
+ }
872
+ return ReturnSet;
873
+ }
874
+
848
875
void discoverDecl (const DeclStmt *DS) {
849
876
for (const Decl *D : DS->decls ()) {
850
877
if (const auto *VD = dyn_cast<VarDecl>(D)) {
@@ -1703,6 +1730,13 @@ populateInitializerFixItWithSpan(const Expr *Init, ASTContext &Ctx,
1703
1730
return FixIts;
1704
1731
}
1705
1732
1733
+ #ifndef NDEBUG
1734
+ #define DEBUG_NOTE_DECL_FAIL (D, Msg ) \
1735
+ Handler.addDebugNoteForVar((D), (D)->getBeginLoc (), "failed to produce fixit for declaration '" + (D)->getNameAsString() + "'" + (Msg))
1736
+ #else
1737
+ #define DEBUG_NOTE_DECL_FAIL (D, Msg )
1738
+ #endif
1739
+
1706
1740
// For a `VarDecl` of the form `T * var (= Init)?`, this
1707
1741
// function generates a fix-it for the declaration, which re-declares `var` to
1708
1742
// be of `span<T>` type and transforms the initializer, if present, to a span
@@ -1717,7 +1751,9 @@ populateInitializerFixItWithSpan(const Expr *Init, ASTContext &Ctx,
1717
1751
// Returns:
1718
1752
// the generated fix-it
1719
1753
static FixItList fixVarDeclWithSpan (const VarDecl *D, ASTContext &Ctx,
1720
- const StringRef UserFillPlaceHolder) {
1754
+ const StringRef UserFillPlaceHolder,
1755
+ UnsafeBufferUsageHandler &Handler) {
1756
+ (void )Handler; // Suppress unused variable warning in release builds.
1721
1757
const QualType &SpanEltT = D->getType ()->getPointeeType ();
1722
1758
assert (!SpanEltT.isNull () && " Trying to fix a non-pointer type variable!" );
1723
1759
@@ -1730,8 +1766,10 @@ static FixItList fixVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
1730
1766
FixItList InitFixIts =
1731
1767
populateInitializerFixItWithSpan (Init, Ctx, UserFillPlaceHolder);
1732
1768
1733
- if (InitFixIts.empty ())
1769
+ if (InitFixIts.empty ()) {
1770
+ DEBUG_NOTE_DECL_FAIL (D, " : empty initializer" );
1734
1771
return {};
1772
+ }
1735
1773
1736
1774
// The loc right before the initializer:
1737
1775
ReplacementLastLoc = Init->getBeginLoc ().getLocWithOffset (-1 );
@@ -1746,8 +1784,10 @@ static FixItList fixVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
1746
1784
1747
1785
OS << " std::span<" << SpanEltT.getAsString () << " > " << D->getName ();
1748
1786
1749
- if (!ReplacementLastLoc)
1787
+ if (!ReplacementLastLoc) {
1788
+ DEBUG_NOTE_DECL_FAIL (D, " : failed to get end char loc (macro)" );
1750
1789
return {};
1790
+ }
1751
1791
1752
1792
FixIts.push_back (FixItHint::CreateReplacement (
1753
1793
SourceRange{D->getBeginLoc (), *ReplacementLastLoc}, OS.str ()));
@@ -1939,26 +1979,35 @@ createOverloadsForFixedParams(unsigned ParmIdx, StringRef NewTyText,
1939
1979
// `createOverloadsForFixedParams`).
1940
1980
static FixItList fixParamWithSpan (const ParmVarDecl *PVD, const ASTContext &Ctx,
1941
1981
UnsafeBufferUsageHandler &Handler) {
1942
- if (PVD->hasDefaultArg ())
1982
+ if (PVD->hasDefaultArg ()) {
1943
1983
// FIXME: generate fix-its for default values:
1984
+ DEBUG_NOTE_DECL_FAIL (PVD, " : has default arg" );
1944
1985
return {};
1986
+ }
1987
+
1945
1988
assert (PVD->getType ()->isPointerType ());
1946
1989
auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext ());
1947
1990
1948
- if (!FD)
1991
+ if (!FD) {
1992
+ DEBUG_NOTE_DECL_FAIL (PVD, " : invalid func decl" );
1949
1993
return {};
1994
+ }
1950
1995
1951
1996
std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
1952
1997
std::optional<std::string> PteTyText = getPointeeTypeText (
1953
1998
PVD, Ctx.getSourceManager (), Ctx.getLangOpts (), &PteTyQualifiers);
1954
1999
1955
- if (!PteTyText)
2000
+ if (!PteTyText) {
2001
+ DEBUG_NOTE_DECL_FAIL (PVD, " : invalid pointee type" );
1956
2002
return {};
2003
+ }
1957
2004
1958
2005
std::optional<StringRef> PVDNameText = PVD->getIdentifier ()->getName ();
1959
2006
1960
- if (!PVDNameText)
2007
+ if (!PVDNameText) {
2008
+ DEBUG_NOTE_DECL_FAIL (PVD, " : invalid identifier name" );
1961
2009
return {};
2010
+ }
1962
2011
1963
2012
std::string SpanOpen = " std::span<" ;
1964
2013
std::string SpanClose = " >" ;
@@ -1994,6 +2043,7 @@ static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
1994
2043
Fixes.append (*OverloadFix);
1995
2044
return Fixes;
1996
2045
}
2046
+ DEBUG_NOTE_DECL_FAIL (PVD, " : invalid number of parameters" );
1997
2047
return {};
1998
2048
}
1999
2049
@@ -2005,6 +2055,7 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
2005
2055
assert (DS && " Fixing non-local variables not implemented yet!" );
2006
2056
if (!DS->isSingleDecl ()) {
2007
2057
// FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2058
+ DEBUG_NOTE_DECL_FAIL (VD, " : multiple VarDecls" );
2008
2059
return {};
2009
2060
}
2010
2061
// Currently DS is an unused variable but we'll need it when
@@ -2013,7 +2064,8 @@ static FixItList fixVariableWithSpan(const VarDecl *VD,
2013
2064
(void )DS;
2014
2065
2015
2066
// FIXME: handle cases where DS has multiple declarations
2016
- return fixVarDeclWithSpan (VD, Ctx, getUserFillPlaceHolder ());
2067
+ return fixVarDeclWithSpan (VD, Ctx, getUserFillPlaceHolder (),
2068
+ Handler);
2017
2069
}
2018
2070
2019
2071
// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
@@ -2025,10 +2077,12 @@ fixVariable(const VarDecl *VD, Strategy::Kind K,
2025
2077
UnsafeBufferUsageHandler &Handler) {
2026
2078
if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
2027
2079
auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext ());
2028
- if (!FD || FD != D)
2080
+ if (!FD || FD != D) {
2029
2081
// `FD != D` means that `PVD` belongs to a function that is not being
2030
2082
// analyzed currently. Thus `FD` may not be complete.
2083
+ DEBUG_NOTE_DECL_FAIL (VD, " : function not currently analyzed" );
2031
2084
return {};
2085
+ }
2032
2086
2033
2087
// TODO If function has a try block we can't change params unless we check
2034
2088
// also its catch block for their use.
@@ -2041,8 +2095,10 @@ fixVariable(const VarDecl *VD, Strategy::Kind K,
2041
2095
isa<CXXMethodDecl>(FD) ||
2042
2096
// skip when the function body is a try-block
2043
2097
(FD->hasBody () && isa<CXXTryStmt>(FD->getBody ())) ||
2044
- FD->isOverloadedOperator ())
2098
+ FD->isOverloadedOperator ()) {
2099
+ DEBUG_NOTE_DECL_FAIL (VD, " : unsupported function decl" );
2045
2100
return {}; // TODO test all these cases
2101
+ }
2046
2102
}
2047
2103
2048
2104
switch (K) {
@@ -2054,6 +2110,7 @@ fixVariable(const VarDecl *VD, Strategy::Kind K,
2054
2110
if (VD->isLocalVarDecl ())
2055
2111
return fixVariableWithSpan (VD, Tracker, Ctx, Handler);
2056
2112
}
2113
+ DEBUG_NOTE_DECL_FAIL (VD, " : not a pointer" );
2057
2114
return {};
2058
2115
}
2059
2116
case Strategy::Kind::Iterator:
@@ -2113,6 +2170,12 @@ getFixIts(FixableGadgetSets &FixablesForAllVars, const Strategy &S,
2113
2170
for (const auto &F : Fixables) {
2114
2171
std::optional<FixItList> Fixits = F->getFixits (S);
2115
2172
if (!Fixits) {
2173
+ #ifndef NDEBUG
2174
+ Handler.addDebugNoteForVar (
2175
+ VD, F->getBaseStmt ()->getBeginLoc (),
2176
+ (" gadget '" + F->getDebugName () + " ' refused to produce a fix" )
2177
+ .str ());
2178
+ #endif
2116
2179
ImpossibleToFix = true ;
2117
2180
break ;
2118
2181
} else {
@@ -2198,6 +2261,10 @@ getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) {
2198
2261
void clang::checkUnsafeBufferUsage (const Decl *D,
2199
2262
UnsafeBufferUsageHandler &Handler,
2200
2263
bool EmitSuggestions) {
2264
+ #ifndef NDEBUG
2265
+ Handler.clearDebugNotes ();
2266
+ #endif
2267
+
2201
2268
assert (D && D->getBody ());
2202
2269
2203
2270
// We do not want to visit a Lambda expression defined inside a method independently.
@@ -2277,10 +2344,34 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
2277
2344
for (auto it = FixablesForAllVars.byVar .cbegin ();
2278
2345
it != FixablesForAllVars.byVar .cend ();) {
2279
2346
// FIXME: need to deal with global variables later
2280
- if ((!it->first ->isLocalVarDecl () && !isa<ParmVarDecl>(it->first )) ||
2281
- Tracker.hasUnclaimedUses (it->first ) || it->first ->isInitCapture ()) {
2282
- it = FixablesForAllVars.byVar .erase (it);
2283
- } else {
2347
+ if ((!it->first ->isLocalVarDecl () && !isa<ParmVarDecl>(it->first ))) {
2348
+ #ifndef NDEBUG
2349
+ Handler.addDebugNoteForVar (
2350
+ it->first , it->first ->getBeginLoc (),
2351
+ (" failed to produce fixit for '" + it->first ->getNameAsString () +
2352
+ " ' : neither local nor a parameter" ));
2353
+ #endif
2354
+ it = FixablesForAllVars.byVar .erase (it);
2355
+ } else if (Tracker.hasUnclaimedUses (it->first )) {
2356
+ #ifndef NDEBUG
2357
+ auto AllUnclaimed = Tracker.getUnclaimedUses (it->first );
2358
+ for (auto UnclaimedDRE : AllUnclaimed) {
2359
+ Handler.addDebugNoteForVar (
2360
+ it->first , UnclaimedDRE->getBeginLoc (),
2361
+ (" failed to produce fixit for '" + it->first ->getNameAsString () +
2362
+ " ' : has an unclaimed use" ));
2363
+ }
2364
+ #endif
2365
+ it = FixablesForAllVars.byVar .erase (it);
2366
+ } else if (it->first ->isInitCapture ()) {
2367
+ #ifndef NDEBUG
2368
+ Handler.addDebugNoteForVar (
2369
+ it->first , it->first ->getBeginLoc (),
2370
+ (" failed to produce fixit for '" + it->first ->getNameAsString () +
2371
+ " ' : init capture" ));
2372
+ #endif
2373
+ it = FixablesForAllVars.byVar .erase (it);
2374
+ }else {
2284
2375
++it;
2285
2376
}
2286
2377
}
0 commit comments