Skip to content

Commit 60afce2

Browse files
authored
[clang-tidy] fix fp when modifying variant by operator[] with template in parameters (#128407)
`ArraySubscriptExpr` can switch base and idx. For dependent array subscript access, we should check both base and idx conservatively.
1 parent 416c7b3 commit 60afce2

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ Changes in existing checks
122122
- Improved :doc:`misc-const-correctness
123123
<clang-tidy/checks/misc/const-correctness>` check by adding the option
124124
`AllowedTypes`, that excludes specified types from const-correctness
125-
checking.
125+
checking and fixing false positives when modifying variant by ``operator[]``
126+
with template in parameters.
126127

127128
- Improved :doc:`misc-redundant-expression
128129
<clang-tidy/checks/misc/redundant-expression>` check by providing additional

clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,3 +998,11 @@ void member_pointer_const(Value &x, PointerToConstMemberFunction m) {
998998
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'member_pointer_tmp' of type 'Value &' can be declared 'const'
999999
(member_pointer_tmp.*m)();
10001000
}
1001+
1002+
namespace gh127776_false_positive {
1003+
template <class T> struct vector { T &operator[](int t); };
1004+
template <typename T> void f() {
1005+
vector<int> x;
1006+
x[T{}] = 3;
1007+
}
1008+
} // namespace gh127776_false_positive

clang/lib/Analysis/ExprMutationAnalyzer.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
8080

8181
namespace {
8282

83+
// `ArraySubscriptExpr` can switch base and idx, e.g. `a[4]` is the same as
84+
// `4[a]`. When type is dependent, we conservatively assume both sides are base.
85+
AST_MATCHER_P(ArraySubscriptExpr, hasBaseConservative,
86+
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
87+
if (Node.isTypeDependent()) {
88+
return InnerMatcher.matches(*Node.getLHS(), Finder, Builder) ||
89+
InnerMatcher.matches(*Node.getRHS(), Finder, Builder);
90+
}
91+
return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
92+
}
93+
8394
AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
8495

8596
AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
@@ -513,8 +524,8 @@ ExprMutationAnalyzer::Analyzer::findArrayElementMutation(const Expr *Exp) {
513524
// Check whether any element of an array is mutated.
514525
const auto SubscriptExprs = match(
515526
findAll(arraySubscriptExpr(
516-
anyOf(hasBase(canResolveToExpr(Exp)),
517-
hasBase(implicitCastExpr(allOf(
527+
anyOf(hasBaseConservative(canResolveToExpr(Exp)),
528+
hasBaseConservative(implicitCastExpr(allOf(
518529
hasCastKind(CK_ArrayToPointerDecay),
519530
hasSourceExpression(canResolveToExpr(Exp)))))))
520531
.bind(NodeID<Expr>::value)),
@@ -716,7 +727,8 @@ ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
716727
unaryOperator(hasOperatorName("*"),
717728
hasUnaryOperand(canResolveToExprPointee(Exp))),
718729
// deref by []
719-
arraySubscriptExpr(hasBase(canResolveToExprPointee(Exp)))))
730+
arraySubscriptExpr(
731+
hasBaseConservative(canResolveToExprPointee(Exp)))))
720732
.bind(NodeID<Expr>::value))),
721733
Stm, Context);
722734
return findExprMutation(Matches);

clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,19 @@ TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
870870
EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
871871
}
872872

873+
TEST(ExprMutationAnalyzerTest, T1) {
874+
const auto AST = buildASTFromCodeWithArgs(
875+
"template <class T> struct vector { T &operator[](int t); };"
876+
"template <typename T> void func() {"
877+
" vector<int> x;"
878+
" x[T{}] = 3;"
879+
"}",
880+
{"-fno-delayed-template-parsing"});
881+
const auto Results =
882+
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
883+
EXPECT_TRUE(isMutated(Results, AST.get()));
884+
}
885+
873886
// section: special case: all created references are non-mutating themself
874887
// and therefore all become 'const'/the value is not modified!
875888

0 commit comments

Comments
 (0)