Skip to content

Commit f40f4fc

Browse files
authored
[clang analysis] ExprMutationAnalyzer support recursive forwarding reference (#88843)
Reapply for #88765. Partially fixes: #60895.
1 parent 0af8cae commit f40f4fc

File tree

7 files changed

+220
-103
lines changed

7 files changed

+220
-103
lines changed

clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
8585

8686
TraversalKindScope RAII(*Result.Context, TK_AsIs);
8787

88-
FunctionParmMutationAnalyzer &Analyzer =
89-
MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
90-
.first->second;
91-
if (Analyzer.isMutated(Param))
88+
FunctionParmMutationAnalyzer *Analyzer =
89+
FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer(
90+
*Function, *Result.Context, MutationAnalyzerCache);
91+
if (Analyzer->isMutated(Param))
9292
return;
9393

9494
const bool IsConstQualified =
@@ -169,7 +169,7 @@ void UnnecessaryValueParamCheck::storeOptions(
169169
}
170170

171171
void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
172-
MutationAnalyzers.clear();
172+
MutationAnalyzerCache.clear();
173173
}
174174

175175
void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,

clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ class UnnecessaryValueParamCheck : public ClangTidyCheck {
3737
void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
3838
const ASTContext &Context);
3939

40-
llvm::DenseMap<const FunctionDecl *, FunctionParmMutationAnalyzer>
41-
MutationAnalyzers;
40+
ExprMutationAnalyzer::Memoized MutationAnalyzerCache;
4241
utils::IncludeInserter Inserter;
4342
const std::vector<StringRef> AllowedTypes;
4443
};

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ Changes in existing checks
221221
<clang-tidy/checks/llvm/header-guard>` check by replacing the local
222222
option `HeaderFileExtensions` by the global option of the same name.
223223

224+
- Improved :doc:`misc-const-correctness
225+
<clang-tidy/checks/misc/const-correctness>` check by avoiding infinite recursion
226+
for recursive forwarding reference.
227+
224228
- Improved :doc:`misc-definitions-in-headers
225229
<clang-tidy/checks/misc/definitions-in-headers>` check by replacing the local
226230
option `HeaderFileExtensions` by the global option of the same name.

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,18 @@ void concatenate3(Args... args)
5858
(..., (stream << args));
5959
}
6060
} // namespace gh70323
61+
62+
namespace gh60895 {
63+
64+
template <class T> void f1(T &&a);
65+
template <class T> void f2(T &&a);
66+
template <class T> void f1(T &&a) { f2<T>(a); }
67+
template <class T> void f2(T &&a) { f1<T>(a); }
68+
void f() {
69+
int x = 0;
70+
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'x' of type 'int' can be declared 'const'
71+
// CHECK-FIXES: int const x = 0;
72+
f1(x);
73+
}
74+
75+
} // namespace gh60895

clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h

Lines changed: 93 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_EXPRMUTATIONANALYZER_H
99
#define LLVM_CLANG_ANALYSIS_ANALYSES_EXPRMUTATIONANALYZER_H
1010

11-
#include <type_traits>
12-
13-
#include "clang/AST/AST.h"
1411
#include "clang/ASTMatchers/ASTMatchers.h"
1512
#include "llvm/ADT/DenseMap.h"
13+
#include <memory>
1614

1715
namespace clang {
1816

@@ -21,75 +19,127 @@ class FunctionParmMutationAnalyzer;
2119
/// Analyzes whether any mutative operations are applied to an expression within
2220
/// a given statement.
2321
class ExprMutationAnalyzer {
22+
friend class FunctionParmMutationAnalyzer;
23+
2424
public:
25+
struct Memoized {
26+
using ResultMap = llvm::DenseMap<const Expr *, const Stmt *>;
27+
using FunctionParaAnalyzerMap =
28+
llvm::SmallDenseMap<const FunctionDecl *,
29+
std::unique_ptr<FunctionParmMutationAnalyzer>>;
30+
31+
ResultMap Results;
32+
ResultMap PointeeResults;
33+
FunctionParaAnalyzerMap FuncParmAnalyzer;
34+
35+
void clear() {
36+
Results.clear();
37+
PointeeResults.clear();
38+
FuncParmAnalyzer.clear();
39+
}
40+
};
41+
struct Analyzer {
42+
Analyzer(const Stmt &Stm, ASTContext &Context, Memoized &Memorized)
43+
: Stm(Stm), Context(Context), Memorized(Memorized) {}
44+
45+
const Stmt *findMutation(const Expr *Exp);
46+
const Stmt *findMutation(const Decl *Dec);
47+
48+
const Stmt *findPointeeMutation(const Expr *Exp);
49+
const Stmt *findPointeeMutation(const Decl *Dec);
50+
static bool isUnevaluated(const Stmt *Smt, const Stmt &Stm,
51+
ASTContext &Context);
52+
53+
private:
54+
using MutationFinder = const Stmt *(Analyzer::*)(const Expr *);
55+
56+
const Stmt *findMutationMemoized(const Expr *Exp,
57+
llvm::ArrayRef<MutationFinder> Finders,
58+
Memoized::ResultMap &MemoizedResults);
59+
const Stmt *tryEachDeclRef(const Decl *Dec, MutationFinder Finder);
60+
61+
bool isUnevaluated(const Expr *Exp);
62+
63+
const Stmt *findExprMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
64+
const Stmt *findDeclMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
65+
const Stmt *
66+
findExprPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
67+
const Stmt *
68+
findDeclPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
69+
70+
const Stmt *findDirectMutation(const Expr *Exp);
71+
const Stmt *findMemberMutation(const Expr *Exp);
72+
const Stmt *findArrayElementMutation(const Expr *Exp);
73+
const Stmt *findCastMutation(const Expr *Exp);
74+
const Stmt *findRangeLoopMutation(const Expr *Exp);
75+
const Stmt *findReferenceMutation(const Expr *Exp);
76+
const Stmt *findFunctionArgMutation(const Expr *Exp);
77+
78+
const Stmt &Stm;
79+
ASTContext &Context;
80+
Memoized &Memorized;
81+
};
82+
2583
ExprMutationAnalyzer(const Stmt &Stm, ASTContext &Context)
26-
: Stm(Stm), Context(Context) {}
84+
: Memorized(), A(Stm, Context, Memorized) {}
2785

2886
bool isMutated(const Expr *Exp) { return findMutation(Exp) != nullptr; }
2987
bool isMutated(const Decl *Dec) { return findMutation(Dec) != nullptr; }
30-
const Stmt *findMutation(const Expr *Exp);
31-
const Stmt *findMutation(const Decl *Dec);
88+
const Stmt *findMutation(const Expr *Exp) { return A.findMutation(Exp); }
89+
const Stmt *findMutation(const Decl *Dec) { return A.findMutation(Dec); }
3290

3391
bool isPointeeMutated(const Expr *Exp) {
3492
return findPointeeMutation(Exp) != nullptr;
3593
}
3694
bool isPointeeMutated(const Decl *Dec) {
3795
return findPointeeMutation(Dec) != nullptr;
3896
}
39-
const Stmt *findPointeeMutation(const Expr *Exp);
40-
const Stmt *findPointeeMutation(const Decl *Dec);
97+
const Stmt *findPointeeMutation(const Expr *Exp) {
98+
return A.findPointeeMutation(Exp);
99+
}
100+
const Stmt *findPointeeMutation(const Decl *Dec) {
101+
return A.findPointeeMutation(Dec);
102+
}
103+
41104
static bool isUnevaluated(const Stmt *Smt, const Stmt &Stm,
42-
ASTContext &Context);
105+
ASTContext &Context) {
106+
return Analyzer::isUnevaluated(Smt, Stm, Context);
107+
}
43108

44109
private:
45-
using MutationFinder = const Stmt *(ExprMutationAnalyzer::*)(const Expr *);
46-
using ResultMap = llvm::DenseMap<const Expr *, const Stmt *>;
47-
48-
const Stmt *findMutationMemoized(const Expr *Exp,
49-
llvm::ArrayRef<MutationFinder> Finders,
50-
ResultMap &MemoizedResults);
51-
const Stmt *tryEachDeclRef(const Decl *Dec, MutationFinder Finder);
52-
53-
bool isUnevaluated(const Expr *Exp);
54-
55-
const Stmt *findExprMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
56-
const Stmt *findDeclMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
57-
const Stmt *
58-
findExprPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
59-
const Stmt *
60-
findDeclPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches);
61-
62-
const Stmt *findDirectMutation(const Expr *Exp);
63-
const Stmt *findMemberMutation(const Expr *Exp);
64-
const Stmt *findArrayElementMutation(const Expr *Exp);
65-
const Stmt *findCastMutation(const Expr *Exp);
66-
const Stmt *findRangeLoopMutation(const Expr *Exp);
67-
const Stmt *findReferenceMutation(const Expr *Exp);
68-
const Stmt *findFunctionArgMutation(const Expr *Exp);
69-
70-
const Stmt &Stm;
71-
ASTContext &Context;
72-
llvm::DenseMap<const FunctionDecl *,
73-
std::unique_ptr<FunctionParmMutationAnalyzer>>
74-
FuncParmAnalyzer;
75-
ResultMap Results;
76-
ResultMap PointeeResults;
110+
Memoized Memorized;
111+
Analyzer A;
77112
};
78113

79114
// A convenient wrapper around ExprMutationAnalyzer for analyzing function
80115
// params.
81116
class FunctionParmMutationAnalyzer {
82117
public:
83-
FunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context);
118+
static FunctionParmMutationAnalyzer *
119+
getFunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context,
120+
ExprMutationAnalyzer::Memoized &Memorized) {
121+
auto it = Memorized.FuncParmAnalyzer.find(&Func);
122+
if (it == Memorized.FuncParmAnalyzer.end())
123+
it =
124+
Memorized.FuncParmAnalyzer
125+
.try_emplace(&Func, std::unique_ptr<FunctionParmMutationAnalyzer>(
126+
new FunctionParmMutationAnalyzer(
127+
Func, Context, Memorized)))
128+
.first;
129+
return it->getSecond().get();
130+
}
84131

85132
bool isMutated(const ParmVarDecl *Parm) {
86133
return findMutation(Parm) != nullptr;
87134
}
88135
const Stmt *findMutation(const ParmVarDecl *Parm);
89136

90137
private:
91-
ExprMutationAnalyzer BodyAnalyzer;
138+
ExprMutationAnalyzer::Analyzer BodyAnalyzer;
92139
llvm::DenseMap<const ParmVarDecl *, const Stmt *> Results;
140+
141+
FunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context,
142+
ExprMutationAnalyzer::Memoized &Memorized);
93143
};
94144

95145
} // namespace clang

0 commit comments

Comments
 (0)