1313#include " clang/AST/ASTContext.h"
1414#include " clang/ASTMatchers/ASTMatchFinder.h"
1515#include " clang/ASTMatchers/ASTMatchers.h"
16+ #include " llvm/Support/Casting.h"
17+ #include < cassert>
1618
1719using namespace clang ::ast_matchers;
1820
@@ -39,34 +41,47 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name,
3941 : ClangTidyCheck(Name, Context),
4042 AnalyzeValues (Options.get(" AnalyzeValues" , true )),
4143 AnalyzeReferences(Options.get(" AnalyzeReferences" , true )),
44+ AnalyzePointers(Options.get(" AnalyzePointers" , true )),
4245 WarnPointersAsValues(Options.get(" WarnPointersAsValues" , false )),
46+ WarnPointersAsPointers(Options.get(" WarnPointersAsPointers" , true )),
4347 TransformValues(Options.get(" TransformValues" , true )),
4448 TransformReferences(Options.get(" TransformReferences" , true )),
4549 TransformPointersAsValues(
4650 Options.get(" TransformPointersAsValues" , false )),
51+ TransformPointersAsPointers(
52+ Options.get(" TransformPointersAsPointers" , true )),
4753 AllowedTypes(
4854 utils::options::parseStringList (Options.get(" AllowedTypes" , " " ))) {
49- if (AnalyzeValues == false && AnalyzeReferences == false )
55+ if (AnalyzeValues == false && AnalyzeReferences == false &&
56+ AnalyzePointers == false )
5057 this ->configurationDiag (
5158 " The check 'misc-const-correctness' will not "
52- " perform any analysis because both 'AnalyzeValues' and "
53- " 'AnalyzeReferences' are false." );
59+ " perform any analysis because both 'AnalyzeValues', "
60+ " 'AnalyzeReferences' and 'AnalyzePointers' are false." );
5461}
5562
5663void ConstCorrectnessCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
5764 Options.store (Opts, " AnalyzeValues" , AnalyzeValues);
5865 Options.store (Opts, " AnalyzeReferences" , AnalyzeReferences);
66+ Options.store (Opts, " AnalyzePointers" , AnalyzePointers);
5967 Options.store (Opts, " WarnPointersAsValues" , WarnPointersAsValues);
68+ Options.store (Opts, " WarnPointersAsPointers" , WarnPointersAsPointers);
6069
6170 Options.store (Opts, " TransformValues" , TransformValues);
6271 Options.store (Opts, " TransformReferences" , TransformReferences);
6372 Options.store (Opts, " TransformPointersAsValues" , TransformPointersAsValues);
73+ Options.store (Opts, " TransformPointersAsPointers" ,
74+ TransformPointersAsPointers);
6475 Options.store (Opts, " AllowedTypes" ,
6576 utils::options::serializeStringList (AllowedTypes));
6677}
6778
6879void ConstCorrectnessCheck::registerMatchers (MatchFinder *Finder) {
69- const auto ConstType = hasType (isConstQualified ());
80+ const auto ConstType = hasType (
81+ qualType (isConstQualified (),
82+ // pointee check will check the const pointer and const array
83+ unless (pointerType ()), unless (arrayType ())));
84+
7085 const auto ConstReference = hasType (references (isConstQualified ()));
7186 const auto RValueReference = hasType (
7287 referenceType (anyOf (rValueReferenceType (), unless (isSpelledAsLValue ()))));
@@ -124,6 +139,11 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
124139 const auto *LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope" );
125140 const auto *Variable = Result.Nodes .getNodeAs <VarDecl>(" local-value" );
126141 const auto *Function = Result.Nodes .getNodeAs <FunctionDecl>(" function-decl" );
142+ const auto *VarDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(" decl-stmt" );
143+ // It can not be guaranteed that the variable is declared isolated,
144+ // therefore a transformation might effect the other variables as well and
145+ // be incorrect.
146+ const bool CanBeFixIt = VarDeclStmt != nullptr && VarDeclStmt->isSingleDecl ();
127147
128148 // / If the variable was declared in a template it might be analyzed multiple
129149 // / times. Only one of those instantiations shall emit a warning. NOTE: This
@@ -145,64 +165,90 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
145165 if (ArrayT->getElementType ()->isPointerType ())
146166 VC = VariableCategory::Pointer;
147167
148- // Each variable can only be in one category: Value, Pointer, Reference.
149- // Analysis can be controlled for every category.
150- if (VC == VariableCategory::Reference && !AnalyzeReferences)
151- return ;
152-
153- if (VC == VariableCategory::Reference &&
154- Variable->getType ()->getPointeeType ()->isPointerType () &&
155- !WarnPointersAsValues)
156- return ;
157-
158- if (VC == VariableCategory::Pointer && !WarnPointersAsValues)
159- return ;
160-
161- if (VC == VariableCategory::Value && !AnalyzeValues)
162- return ;
163-
164- // The scope is only registered if the analysis shall be run.
165- registerScope (LocalScope, Result.Context );
166-
167- // Offload const-analysis to utility function.
168- if (ScopesCache[LocalScope]->isMutated (Variable))
169- return ;
170-
171- auto Diag = diag (Variable->getBeginLoc (),
172- " variable %0 of type %1 can be declared 'const'" )
173- << Variable << Variable->getType ();
174- if (IsNormalVariableInTemplate)
175- TemplateDiagnosticsCache.insert (Variable->getBeginLoc ());
168+ auto CheckValue = [&]() {
169+ // The scope is only registered if the analysis shall be run.
170+ registerScope (LocalScope, Result.Context );
171+
172+ // Offload const-analysis to utility function.
173+ if (ScopesCache[LocalScope]->isMutated (Variable))
174+ return ;
175+
176+ auto Diag = diag (Variable->getBeginLoc (),
177+ " variable %0 of type %1 can be declared 'const'" )
178+ << Variable << VT;
179+ if (IsNormalVariableInTemplate)
180+ TemplateDiagnosticsCache.insert (Variable->getBeginLoc ());
181+ if (!CanBeFixIt)
182+ return ;
183+ using namespace utils ::fixit;
184+ if (VC == VariableCategory::Value && TransformValues) {
185+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
186+ Qualifiers::Const, QualifierTarget::Value,
187+ QualifierPolicy::Right);
188+ // FIXME: Add '{}' for default initialization if no user-defined default
189+ // constructor exists and there is no initializer.
190+ return ;
191+ }
176192
177- const auto *VarDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(" decl-stmt" );
193+ if (VC == VariableCategory::Reference && TransformReferences) {
194+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
195+ Qualifiers::Const, QualifierTarget::Value,
196+ QualifierPolicy::Right);
197+ return ;
198+ }
178199
179- // It can not be guaranteed that the variable is declared isolated, therefore
180- // a transformation might effect the other variables as well and be incorrect.
181- if (VarDeclStmt == nullptr || !VarDeclStmt->isSingleDecl ())
182- return ;
200+ if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
201+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
202+ Qualifiers::Const, QualifierTarget::Value,
203+ QualifierPolicy::Right);
204+ return ;
205+ }
206+ };
207+
208+ auto CheckPointee = [&]() {
209+ assert (VC == VariableCategory::Pointer);
210+ registerScope (LocalScope, Result.Context );
211+ if (ScopesCache[LocalScope]->isPointeeMutated (Variable))
212+ return ;
213+ auto Diag = diag (Variable->getBeginLoc (),
214+ " variable %0 of type %1 can be declared 'const'" )
215+ << Variable << VT;
216+ if (IsNormalVariableInTemplate)
217+ TemplateDiagnosticsCache.insert (Variable->getBeginLoc ());
218+ if (!CanBeFixIt)
219+ return ;
220+ using namespace utils ::fixit;
221+ if (TransformPointersAsPointers) {
222+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
223+ Qualifiers::Const, QualifierTarget::Pointee,
224+ QualifierPolicy::Right);
225+ }
226+ };
183227
184- using namespace utils ::fixit;
185- if (VC == VariableCategory::Value && TransformValues) {
186- Diag << addQualifierToVarDecl (*Variable, *Result.Context , Qualifiers::Const,
187- QualifierTarget::Value,
188- QualifierPolicy::Right);
189- // FIXME: Add '{}' for default initialization if no user-defined default
190- // constructor exists and there is no initializer.
228+ // Each variable can only be in one category: Value, Pointer, Reference.
229+ // Analysis can be controlled for every category.
230+ if (VC == VariableCategory::Value && AnalyzeValues) {
231+ CheckValue ();
191232 return ;
192233 }
193-
194- if (VC == VariableCategory::Reference && TransformReferences) {
195- Diag << addQualifierToVarDecl (*Variable, *Result.Context , Qualifiers::Const,
196- QualifierTarget::Value,
197- QualifierPolicy::Right);
234+ if (VC == VariableCategory::Reference && AnalyzeReferences) {
235+ if (VT->getPointeeType ()->isPointerType () && !WarnPointersAsValues)
236+ return ;
237+ CheckValue ();
198238 return ;
199239 }
200-
201- if (VC == VariableCategory::Pointer) {
202- if (WarnPointersAsValues && TransformPointersAsValues) {
203- Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
204- Qualifiers::Const, QualifierTarget::Value,
205- QualifierPolicy::Right);
240+ if (VC == VariableCategory::Pointer && AnalyzePointers) {
241+ if (WarnPointersAsValues && !VT.isConstQualified ())
242+ CheckValue ();
243+ if (WarnPointersAsPointers) {
244+ if (const auto *PT = dyn_cast<PointerType>(VT))
245+ if (!PT->getPointeeType ().isConstQualified ())
246+ CheckPointee ();
247+ if (const auto *AT = dyn_cast<ArrayType>(VT))
248+ if (!AT->getElementType ().isConstQualified ()) {
249+ assert (AT->getElementType ()->isPointerType ());
250+ CheckPointee ();
251+ }
206252 }
207253 return ;
208254 }
0 commit comments