@@ -33,6 +33,14 @@ AST_MATCHER(ReferenceType, isSpelledAsLValue) {
3333 return Node.isSpelledAsLValue ();
3434}
3535AST_MATCHER (Type, isDependentType) { return Node.isDependentType (); }
36+
37+ AST_MATCHER (FunctionDecl, isTemplate) {
38+ return Node.getDescribedFunctionTemplate () != nullptr ;
39+ }
40+
41+ AST_MATCHER (FunctionDecl, isFunctionTemplateSpecialization) {
42+ return Node.isFunctionTemplateSpecialization ();
43+ }
3644} // namespace
3745
3846ConstCorrectnessCheck::ConstCorrectnessCheck (StringRef Name,
@@ -41,6 +49,7 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name,
4149 AnalyzePointers (Options.get(" AnalyzePointers" , true )),
4250 AnalyzeReferences(Options.get(" AnalyzeReferences" , true )),
4351 AnalyzeValues(Options.get(" AnalyzeValues" , true )),
52+ AnalyzeParameters(Options.get(" AnalyzeParameters" , true )),
4453
4554 WarnPointersAsPointers(Options.get(" WarnPointersAsPointers" , true )),
4655 WarnPointersAsValues(Options.get(" WarnPointersAsValues" , false )),
@@ -66,6 +75,7 @@ void ConstCorrectnessCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
6675 Options.store (Opts, " AnalyzePointers" , AnalyzePointers);
6776 Options.store (Opts, " AnalyzeReferences" , AnalyzeReferences);
6877 Options.store (Opts, " AnalyzeValues" , AnalyzeValues);
78+ Options.store (Opts, " AnalyzeParameters" , AnalyzeParameters);
6979
7080 Options.store (Opts, " WarnPointersAsPointers" , WarnPointersAsPointers);
7181 Options.store (Opts, " WarnPointersAsValues" , WarnPointersAsValues);
@@ -112,15 +122,17 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
112122 const auto FunctionPointerRef =
113123 hasType (hasCanonicalType (referenceType (pointee (functionType ()))));
114124
125+ const auto CommonExcludeTypes =
126+ anyOf (ConstType, ConstReference, RValueReference, TemplateType,
127+ FunctionPointerRef, hasType (cxxRecordDecl (isLambda ())),
128+ AutoTemplateType, isImplicit (), AllowedType);
129+
115130 // Match local variables which could be 'const' if not modified later.
116131 // Example: `int i = 10` would match `int i`.
117- const auto LocalValDecl = varDecl (
118- isLocal (), hasInitializer (anything ()),
119- unless (anyOf (ConstType, ConstReference, TemplateType,
120- hasInitializer (isInstantiationDependent ()), AutoTemplateType,
121- RValueReference, FunctionPointerRef,
122- hasType (cxxRecordDecl (isLambda ())), isImplicit (),
123- AllowedType)));
132+ const auto LocalValDecl =
133+ varDecl (isLocal (), hasInitializer (anything ()),
134+ unless (anyOf (CommonExcludeTypes,
135+ hasInitializer (isInstantiationDependent ()))));
124136
125137 // Match the function scope for which the analysis of all local variables
126138 // shall be run.
@@ -135,20 +147,83 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
135147 .bind (" function-decl" );
136148
137149 Finder->addMatcher (FunctionScope, this );
150+
151+ if (AnalyzeParameters) {
152+ const auto ParamMatcher =
153+ parmVarDecl (unless (CommonExcludeTypes),
154+ anyOf (hasType (referenceType ()), hasType (pointerType ())))
155+ .bind (" param-var" );
156+
157+ // Match function parameters which could be 'const' if not modified later.
158+ // Example: `void foo(int* ptr)` would match `int* ptr`.
159+ const auto FunctionWithParams =
160+ functionDecl (
161+ hasBody (stmt ().bind (" scope-params" )),
162+ has (typeLoc (forEach (ParamMatcher))), unless (cxxMethodDecl ()),
163+ unless (isFunctionTemplateSpecialization ()), unless (isTemplate ()))
164+ .bind (" function-params" );
165+
166+ Finder->addMatcher (FunctionWithParams, this );
167+ }
168+ }
169+
170+ static void addConstFixits (DiagnosticBuilder &Diag, const VarDecl *Variable,
171+ const FunctionDecl *Function, ASTContext &Context,
172+ Qualifiers::TQ Qualifier,
173+ utils::fixit::QualifierTarget Target,
174+ utils::fixit::QualifierPolicy Policy) {
175+ Diag << addQualifierToVarDecl (*Variable, Context, Qualifier, Target, Policy);
176+
177+ // If this is a parameter, also add fixits for corresponding parameters in
178+ // function declarations
179+ if (const auto *ParamDecl = dyn_cast<ParmVarDecl>(Variable)) {
180+ const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex ();
181+
182+ for (const FunctionDecl *Redecl : Function->redecls ()) {
183+ if (Redecl == Function)
184+ continue ;
185+
186+ if (ParamIdx < Redecl->getNumParams ()) {
187+ const ParmVarDecl *RedeclParam = Redecl->getParamDecl (ParamIdx);
188+ Diag << addQualifierToVarDecl (*RedeclParam, Context, Qualifier, Target,
189+ Policy);
190+ }
191+ }
192+ }
138193}
139194
140195// / Classify for a variable in what the Const-Check is interested.
141196enum class VariableCategory { Value, Reference, Pointer };
142197
143198void ConstCorrectnessCheck::check (const MatchFinder::MatchResult &Result) {
144- const auto *LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope" );
145- const auto *Variable = Result.Nodes .getNodeAs <VarDecl>(" local-value" );
146- const auto *Function = Result.Nodes .getNodeAs <FunctionDecl>(" function-decl" );
147- const auto *VarDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(" decl-stmt" );
199+ const Stmt *LocalScope = nullptr ;
200+ const VarDecl *Variable = nullptr ;
201+ const FunctionDecl *Function = nullptr ;
202+ const DeclStmt *VarDeclStmt = nullptr ;
203+
204+ if (const auto *LocalVariable =
205+ Result.Nodes .getNodeAs <VarDecl>(" local-value" )) {
206+ Variable = LocalVariable;
207+ LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope" );
208+ Function = Result.Nodes .getNodeAs <FunctionDecl>(" function-decl" );
209+ VarDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(" decl-stmt" );
210+ } else if (const auto *Parameter =
211+ Result.Nodes .getNodeAs <ParmVarDecl>(" param-var" )) {
212+ Variable = Parameter;
213+ LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope-params" );
214+ Function = Result.Nodes .getNodeAs <FunctionDecl>(" function-params" );
215+ } else {
216+ llvm_unreachable (" didn't match local-value or param-var" );
217+ }
218+
219+ assert (Variable && LocalScope && Function);
220+
148221 // It can not be guaranteed that the variable is declared isolated,
149222 // therefore a transformation might effect the other variables as well and
150- // be incorrect.
151- const bool CanBeFixIt = VarDeclStmt != nullptr && VarDeclStmt->isSingleDecl ();
223+ // be incorrect. Parameters don't need this check - they receive values from
224+ // callers
225+ const bool CanBeFixIt = isa<ParmVarDecl>(Variable) ||
226+ (VarDeclStmt && VarDeclStmt->isSingleDecl ());
152227
153228 // / If the variable was declared in a template it might be analyzed multiple
154229 // / times. Only one of those instantiations shall emit a warning. NOTE: This
@@ -172,11 +247,8 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
172247 }
173248
174249 auto CheckValue = [&]() {
175- // The scope is only registered if the analysis shall be run.
176- registerScope (LocalScope, Result.Context );
177-
178250 // Offload const-analysis to utility function.
179- if (ScopesCache[LocalScope]-> isMutated (Variable))
251+ if (isMutated (Variable, LocalScope, Function, Result. Context ))
180252 return ;
181253
182254 auto Diag = diag (Variable->getBeginLoc (),
@@ -187,26 +259,27 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
187259 if (!CanBeFixIt)
188260 return ;
189261 using namespace utils ::fixit;
262+
190263 if (VC == VariableCategory::Value && TransformValues) {
191- Diag << addQualifierToVarDecl (* Variable, *Result.Context ,
192- Qualifiers::Const, QualifierTarget::Value,
193- QualifierPolicy::Right);
264+ addConstFixits ( Diag, Variable, Function , *Result.Context ,
265+ Qualifiers::Const, QualifierTarget::Value,
266+ QualifierPolicy::Right);
194267 // FIXME: Add '{}' for default initialization if no user-defined default
195268 // constructor exists and there is no initializer.
196269 return ;
197270 }
198271
199272 if (VC == VariableCategory::Reference && TransformReferences) {
200- Diag << addQualifierToVarDecl (* Variable, *Result.Context ,
201- Qualifiers::Const, QualifierTarget::Value,
202- QualifierPolicy::Right);
273+ addConstFixits ( Diag, Variable, Function , *Result.Context ,
274+ Qualifiers::Const, QualifierTarget::Value,
275+ QualifierPolicy::Right);
203276 return ;
204277 }
205278
206279 if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
207- Diag << addQualifierToVarDecl (* Variable, *Result.Context ,
208- Qualifiers::Const, QualifierTarget::Value,
209- QualifierPolicy::Right);
280+ addConstFixits ( Diag, Variable, Function , *Result.Context ,
281+ Qualifiers::Const, QualifierTarget::Value,
282+ QualifierPolicy::Right);
210283 return ;
211284 }
212285 };
@@ -226,9 +299,9 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
226299 return ;
227300 using namespace utils ::fixit;
228301 if (TransformPointersAsPointers) {
229- Diag << addQualifierToVarDecl (* Variable, *Result.Context ,
230- Qualifiers::Const, QualifierTarget::Pointee,
231- QualifierPolicy::Right);
302+ addConstFixits ( Diag, Variable, Function , *Result.Context ,
303+ Qualifiers::Const, QualifierTarget::Pointee,
304+ QualifierPolicy::Right);
232305 }
233306 };
234307
@@ -271,4 +344,18 @@ void ConstCorrectnessCheck::registerScope(const Stmt *LocalScope,
271344 Analyzer = std::make_unique<ExprMutationAnalyzer>(*LocalScope, *Context);
272345}
273346
347+ bool ConstCorrectnessCheck::isMutated (const VarDecl *Variable,
348+ const Stmt *Scope,
349+ const FunctionDecl *Func,
350+ ASTContext *Context) {
351+ if (const auto *Param = dyn_cast<ParmVarDecl>(Variable)) {
352+ return FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer (
353+ *Func, *Context, ParamMutationAnalyzerMemoized)
354+ ->isMutated (Param);
355+ }
356+
357+ registerScope (Scope, Context);
358+ return ScopesCache[Scope]->isMutated (Variable);
359+ }
360+
274361} // namespace clang::tidy::misc
0 commit comments