@@ -1028,6 +1028,46 @@ class UPCPreIncrementGadget : public FixableGadget {
1028
1028
}
1029
1029
};
1030
1030
1031
+ // Representing a pointer type expression of the form `Ptr += n` in an
1032
+ // Unspecified Untyped Context (UUC):
1033
+ class UUCAddAssignGadget : public FixableGadget {
1034
+ private:
1035
+ static constexpr const char *const UUCAddAssignTag =
1036
+ " PointerAddAssignUnderUUC" ;
1037
+ static constexpr const char *const OffsetTag = " Offset" ;
1038
+
1039
+ const BinaryOperator *Node; // the `Ptr += n` node
1040
+ const Expr *Offset = nullptr ;
1041
+
1042
+ public:
1043
+ UUCAddAssignGadget (const MatchFinder::MatchResult &Result)
1044
+ : FixableGadget(Kind::UUCAddAssign),
1045
+ Node (Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1046
+ Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1047
+ assert (Node != nullptr && " Expecting a non-null matching result" );
1048
+ }
1049
+
1050
+ static bool classof (const Gadget *G) {
1051
+ return G->getKind () == Kind::UUCAddAssign;
1052
+ }
1053
+
1054
+ static Matcher matcher () {
1055
+ return stmt (isInUnspecifiedUntypedContext (expr (ignoringImpCasts (
1056
+ binaryOperator (hasOperatorName (" +=" ),
1057
+ hasLHS (declRefExpr (toSupportedVariable ())),
1058
+ hasRHS (expr ().bind (OffsetTag)))
1059
+ .bind (UUCAddAssignTag)))));
1060
+ }
1061
+
1062
+ virtual std::optional<FixItList> getFixits (const Strategy &S) const override ;
1063
+
1064
+ virtual const Stmt *getBaseStmt () const override { return Node; }
1065
+
1066
+ virtual DeclUseList getClaimedVarUseSites () const override {
1067
+ return {dyn_cast<DeclRefExpr>(Node->getLHS ())};
1068
+ }
1069
+ };
1070
+
1031
1071
// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
1032
1072
// ptr)`:
1033
1073
class DerefSimplePtrArithFixableGadget : public FixableGadget {
@@ -1312,21 +1352,29 @@ PointerInitGadget::getFixits(const Strategy &S) const {
1312
1352
return std::nullopt;
1313
1353
}
1314
1354
1355
+ static bool isNonNegativeIntegerExpr (const Expr *Expr, const VarDecl *VD,
1356
+ const ASTContext &Ctx) {
1357
+ if (auto ConstVal = Expr->getIntegerConstantExpr (Ctx)) {
1358
+ if (ConstVal->isNegative ())
1359
+ return false ;
1360
+ } else if (!Expr->getType ()->isUnsignedIntegerType ())
1361
+ return false ;
1362
+ return true ;
1363
+ }
1364
+
1315
1365
std::optional<FixItList>
1316
1366
ULCArraySubscriptGadget::getFixits (const Strategy &S) const {
1317
1367
if (const auto *DRE =
1318
1368
dyn_cast<DeclRefExpr>(Node->getBase ()->IgnoreImpCasts ()))
1319
1369
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl ())) {
1320
1370
switch (S.lookup (VD)) {
1321
1371
case Strategy::Kind::Span: {
1372
+
1322
1373
// If the index has a negative constant value, we give up as no valid
1323
1374
// fix-it can be generated:
1324
1375
const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
1325
1376
VD->getASTContext ();
1326
- if (auto ConstVal = Node->getIdx ()->getIntegerConstantExpr (Ctx)) {
1327
- if (ConstVal->isNegative ())
1328
- return std::nullopt;
1329
- } else if (!Node->getIdx ()->getType ()->isUnsignedIntegerType ())
1377
+ if (!isNonNegativeIntegerExpr (Node->getIdx (), VD, Ctx))
1330
1378
return std::nullopt;
1331
1379
// no-op is a good fix-it, otherwise
1332
1380
return FixItList{};
@@ -1405,10 +1453,8 @@ static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
1405
1453
const LangOptions &LangOpts) {
1406
1454
SourceLocation Loc =
1407
1455
Lexer::getLocForEndOfToken (Node->getEndLoc (), 0 , SM, LangOpts);
1408
-
1409
1456
if (Loc.isValid ())
1410
1457
return Loc;
1411
-
1412
1458
return std::nullopt;
1413
1459
}
1414
1460
@@ -1766,6 +1812,47 @@ fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
1766
1812
FixItHint::CreateReplacement (Node->getSourceRange (), SS.str ())};
1767
1813
}
1768
1814
1815
+ std::optional<FixItList>
1816
+ UUCAddAssignGadget::getFixits (const Strategy &S) const {
1817
+ DeclUseList DREs = getClaimedVarUseSites ();
1818
+
1819
+ if (DREs.size () != 1 )
1820
+ return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
1821
+ // give up
1822
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front ()->getDecl ())) {
1823
+ if (S.lookup (VD) == Strategy::Kind::Span) {
1824
+ FixItList Fixes;
1825
+
1826
+ const Stmt *AddAssignNode = getBaseStmt ();
1827
+ StringRef varName = VD->getName ();
1828
+ const ASTContext &Ctx = VD->getASTContext ();
1829
+
1830
+ if (!isNonNegativeIntegerExpr (Offset, VD, Ctx))
1831
+ return std::nullopt;
1832
+
1833
+ // To transform UUC(p += n) to UUC(p = p.subspan(..)):
1834
+ bool NotParenExpr =
1835
+ (Offset->IgnoreParens ()->getBeginLoc () == Offset->getBeginLoc ());
1836
+ std::string SS = varName.str () + " = " + varName.str () + " .subspan" ;
1837
+ if (NotParenExpr)
1838
+ SS += " (" ;
1839
+
1840
+ std::optional<SourceLocation> AddAssignLocation = getEndCharLoc (
1841
+ AddAssignNode, Ctx.getSourceManager (), Ctx.getLangOpts ());
1842
+ if (!AddAssignLocation)
1843
+ return std::nullopt;
1844
+
1845
+ Fixes.push_back (FixItHint::CreateReplacement (
1846
+ SourceRange (AddAssignNode->getBeginLoc (), Node->getOperatorLoc ()),
1847
+ SS));
1848
+ if (NotParenExpr)
1849
+ Fixes.push_back (FixItHint::CreateInsertion (
1850
+ Offset->getEndLoc ().getLocWithOffset (1 ), " )" ));
1851
+ return Fixes;
1852
+ }
1853
+ }
1854
+ return std::nullopt; // Not in the cases that we can handle for now, give up.
1855
+ }
1769
1856
1770
1857
std::optional<FixItList> UPCPreIncrementGadget::getFixits (const Strategy &S) const {
1771
1858
DeclUseList DREs = getClaimedVarUseSites ();
0 commit comments