@@ -1021,6 +1021,46 @@ class UPCPreIncrementGadget : public FixableGadget {
1021
1021
}
1022
1022
};
1023
1023
1024
+ // Representing a pointer type expression of the form `Ptr += n` in an
1025
+ // Unspecified Untyped Context (UUC):
1026
+ class UUCAddAssignGadget : public FixableGadget {
1027
+ private:
1028
+ static constexpr const char *const UUCAddAssignTag =
1029
+ " PointerAddAssignUnderUUC" ;
1030
+ static constexpr const char *const OffsetTag = " Offset" ;
1031
+
1032
+ const BinaryOperator *Node; // the `Ptr += n` node
1033
+ const Expr *Offset = nullptr ;
1034
+
1035
+ public:
1036
+ UUCAddAssignGadget (const MatchFinder::MatchResult &Result)
1037
+ : FixableGadget(Kind::UUCAddAssign),
1038
+ Node (Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1039
+ Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1040
+ assert (Node != nullptr && " Expecting a non-null matching result" );
1041
+ }
1042
+
1043
+ static bool classof (const Gadget *G) {
1044
+ return G->getKind () == Kind::UUCAddAssign;
1045
+ }
1046
+
1047
+ static Matcher matcher () {
1048
+ return stmt (isInUnspecifiedUntypedContext (expr (ignoringImpCasts (
1049
+ binaryOperator (hasOperatorName (" +=" ),
1050
+ hasLHS (declRefExpr (toSupportedVariable ())),
1051
+ hasRHS (expr ().bind (OffsetTag)))
1052
+ .bind (UUCAddAssignTag)))));
1053
+ }
1054
+
1055
+ virtual std::optional<FixItList> getFixits (const Strategy &S) const override ;
1056
+
1057
+ virtual const Stmt *getBaseStmt () const override { return Node; }
1058
+
1059
+ virtual DeclUseList getClaimedVarUseSites () const override {
1060
+ return {dyn_cast<DeclRefExpr>(Node->getLHS ())};
1061
+ }
1062
+ };
1063
+
1024
1064
// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
1025
1065
// ptr)`:
1026
1066
class DerefSimplePtrArithFixableGadget : public FixableGadget {
@@ -1304,21 +1344,29 @@ std::optional<FixItList> PointerInitGadget::getFixits(const Strategy &S) const {
1304
1344
return std::nullopt;
1305
1345
}
1306
1346
1347
+ static bool isNonNegativeIntegerExpr (const Expr *Expr, const VarDecl *VD,
1348
+ const ASTContext &Ctx) {
1349
+ if (auto ConstVal = Expr->getIntegerConstantExpr (Ctx)) {
1350
+ if (ConstVal->isNegative ())
1351
+ return false ;
1352
+ } else if (!Expr->getType ()->isUnsignedIntegerType ())
1353
+ return false ;
1354
+ return true ;
1355
+ }
1356
+
1307
1357
std::optional<FixItList>
1308
1358
ULCArraySubscriptGadget::getFixits (const Strategy &S) const {
1309
1359
if (const auto *DRE =
1310
1360
dyn_cast<DeclRefExpr>(Node->getBase ()->IgnoreImpCasts ()))
1311
1361
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl ())) {
1312
1362
switch (S.lookup (VD)) {
1313
1363
case Strategy::Kind::Span: {
1364
+
1314
1365
// If the index has a negative constant value, we give up as no valid
1315
1366
// fix-it can be generated:
1316
1367
const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
1317
1368
VD->getASTContext ();
1318
- if (auto ConstVal = Node->getIdx ()->getIntegerConstantExpr (Ctx)) {
1319
- if (ConstVal->isNegative ())
1320
- return std::nullopt;
1321
- } else if (!Node->getIdx ()->getType ()->isUnsignedIntegerType ())
1369
+ if (!isNonNegativeIntegerExpr (Node->getIdx (), VD, Ctx))
1322
1370
return std::nullopt;
1323
1371
// no-op is a good fix-it, otherwise
1324
1372
return FixItList{};
@@ -1397,10 +1445,8 @@ static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
1397
1445
const LangOptions &LangOpts) {
1398
1446
SourceLocation Loc =
1399
1447
Lexer::getLocForEndOfToken (Node->getEndLoc (), 0 , SM, LangOpts);
1400
-
1401
1448
if (Loc.isValid ())
1402
1449
return Loc;
1403
-
1404
1450
return std::nullopt;
1405
1451
}
1406
1452
@@ -1788,6 +1834,49 @@ UPCPreIncrementGadget::getFixits(const Strategy &S) const {
1788
1834
return std::nullopt; // Not in the cases that we can handle for now, give up.
1789
1835
}
1790
1836
1837
+ std::optional<FixItList>
1838
+ UUCAddAssignGadget::getFixits (const Strategy &S) const {
1839
+ DeclUseList DREs = getClaimedVarUseSites ();
1840
+
1841
+ if (DREs.size () != 1 )
1842
+ return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
1843
+ // give up
1844
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front ()->getDecl ())) {
1845
+ if (S.lookup (VD) == Strategy::Kind::Span) {
1846
+ FixItList Fixes;
1847
+
1848
+ const Stmt *AddAssignNode = getBaseStmt ();
1849
+ StringRef varName = VD->getName ();
1850
+ const ASTContext &Ctx = VD->getASTContext ();
1851
+
1852
+ if (!isNonNegativeIntegerExpr (Offset, VD, Ctx))
1853
+ return std::nullopt;
1854
+
1855
+ // To transform UUC(p += n) to UUC(p = p.subspan(..)):
1856
+ bool NotParenExpr =
1857
+ (Offset->IgnoreParens ()->getBeginLoc () == Offset->getBeginLoc ());
1858
+ std::string SS = varName.str () + " = " + varName.str () + " .subspan" ;
1859
+ if (NotParenExpr)
1860
+ SS += " (" ;
1861
+
1862
+ std::optional<SourceLocation> AddAssignLocation = getEndCharLoc (
1863
+ AddAssignNode, Ctx.getSourceManager (), Ctx.getLangOpts ());
1864
+ if (!AddAssignLocation)
1865
+ return std::nullopt;
1866
+
1867
+ Fixes.push_back (FixItHint::CreateReplacement (
1868
+ SourceRange (AddAssignNode->getBeginLoc (), Node->getOperatorLoc ()),
1869
+ SS));
1870
+ if (NotParenExpr)
1871
+ Fixes.push_back (FixItHint::CreateInsertion (
1872
+ Offset->getEndLoc ().getLocWithOffset (1 ), " )" ));
1873
+ return Fixes;
1874
+ }
1875
+ }
1876
+ return std::nullopt; // Not in the cases that we can handle for now, give up.
1877
+ }
1878
+
1879
+
1791
1880
// For a non-null initializer `Init` of `T *` type, this function returns
1792
1881
// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
1793
1882
// to output stream.
0 commit comments