1313#include " clang/AST/ASTContext.h"
1414#include " clang/ASTMatchers/ASTMatchFinder.h"
1515#include " clang/ASTMatchers/ASTMatchers.h"
16+ #include < cassert>
1617
1718using namespace clang ::ast_matchers;
1819
@@ -37,36 +38,54 @@ AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
3738ConstCorrectnessCheck::ConstCorrectnessCheck (StringRef Name,
3839 ClangTidyContext *Context)
3940 : ClangTidyCheck(Name, Context),
40- AnalyzeValues (Options.get(" AnalyzeValues " , true )),
41+ AnalyzePointers (Options.get(" AnalyzePointers " , true )),
4142 AnalyzeReferences(Options.get(" AnalyzeReferences" , true )),
43+ AnalyzeValues(Options.get(" AnalyzeValues" , true )),
44+
45+ WarnPointersAsPointers(Options.get(" WarnPointersAsPointers" , true )),
4246 WarnPointersAsValues(Options.get(" WarnPointersAsValues" , false )),
43- TransformValues(Options.get(" TransformValues" , true )),
44- TransformReferences(Options.get(" TransformReferences" , true )),
47+
48+ TransformPointersAsPointers(
49+ Options.get(" TransformPointersAsPointers" , true )),
4550 TransformPointersAsValues(
4651 Options.get(" TransformPointersAsValues" , false )),
52+ TransformReferences(Options.get(" TransformReferences" , true )),
53+ TransformValues(Options.get(" TransformValues" , true )),
54+
4755 AllowedTypes(
4856 utils::options::parseStringList (Options.get(" AllowedTypes" , " " ))) {
49- if (AnalyzeValues == false && AnalyzeReferences == false )
57+ if (AnalyzeValues == false && AnalyzeReferences == false &&
58+ AnalyzePointers == false )
5059 this ->configurationDiag (
5160 " The check 'misc-const-correctness' will not "
52- " perform any analysis because both 'AnalyzeValues' and "
53- " 'AnalyzeReferences' are false." );
61+ " perform any analysis because 'AnalyzeValues', "
62+ " 'AnalyzeReferences' and 'AnalyzePointers' are false." );
5463}
5564
5665void ConstCorrectnessCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
57- Options.store (Opts, " AnalyzeValues " , AnalyzeValues );
66+ Options.store (Opts, " AnalyzePointers " , AnalyzePointers );
5867 Options.store (Opts, " AnalyzeReferences" , AnalyzeReferences);
68+ Options.store (Opts, " AnalyzeValues" , AnalyzeValues);
69+
70+ Options.store (Opts, " WarnPointersAsPointers" , WarnPointersAsPointers);
5971 Options.store (Opts, " WarnPointersAsValues" , WarnPointersAsValues);
6072
61- Options.store (Opts, " TransformValues " , TransformValues);
62- Options. store (Opts, " TransformReferences " , TransformReferences );
73+ Options.store (Opts, " TransformPointersAsPointers " ,
74+ TransformPointersAsPointers );
6375 Options.store (Opts, " TransformPointersAsValues" , TransformPointersAsValues);
76+ Options.store (Opts, " TransformReferences" , TransformReferences);
77+ Options.store (Opts, " TransformValues" , TransformValues);
78+
6479 Options.store (Opts, " AllowedTypes" ,
6580 utils::options::serializeStringList (AllowedTypes));
6681}
6782
6883void ConstCorrectnessCheck::registerMatchers (MatchFinder *Finder) {
69- const auto ConstType = hasType (isConstQualified ());
84+ const auto ConstType = hasType (
85+ qualType (isConstQualified (),
86+ // pointee check will check the const pointer and const array
87+ unless (pointerType ()), unless (arrayType ())));
88+
7089 const auto ConstReference = hasType (references (isConstQualified ()));
7190 const auto RValueReference = hasType (
7291 referenceType (anyOf (rValueReferenceType (), unless (isSpelledAsLValue ()))));
@@ -124,6 +143,11 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
124143 const auto *LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope" );
125144 const auto *Variable = Result.Nodes .getNodeAs <VarDecl>(" local-value" );
126145 const auto *Function = Result.Nodes .getNodeAs <FunctionDecl>(" function-decl" );
146+ const auto *VarDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(" decl-stmt" );
147+ // It can not be guaranteed that the variable is declared isolated,
148+ // therefore a transformation might effect the other variables as well and
149+ // be incorrect.
150+ const bool CanBeFixIt = VarDeclStmt != nullptr && VarDeclStmt->isSingleDecl ();
127151
128152 // / If the variable was declared in a template it might be analyzed multiple
129153 // / times. Only one of those instantiations shall emit a warning. NOTE: This
@@ -137,72 +161,102 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
137161
138162 VariableCategory VC = VariableCategory::Value;
139163 const QualType VT = Variable->getType ();
140- if (VT->isReferenceType ())
164+ if (VT->isReferenceType ()) {
141165 VC = VariableCategory::Reference;
142- else if (VT->isPointerType ())
166+ } else if (VT->isPointerType ()) {
143167 VC = VariableCategory::Pointer;
144- else if (const auto *ArrayT = dyn_cast<ArrayType>(VT))
168+ } else if (const auto *ArrayT = dyn_cast<ArrayType>(VT)) {
145169 if (ArrayT->getElementType ()->isPointerType ())
146170 VC = VariableCategory::Pointer;
171+ }
147172
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 ());
173+ auto CheckValue = [&]() {
174+ // The scope is only registered if the analysis shall be run.
175+ registerScope (LocalScope, Result.Context );
176+
177+ // Offload const-analysis to utility function.
178+ if (ScopesCache[LocalScope]->isMutated (Variable))
179+ return ;
180+
181+ auto Diag = diag (Variable->getBeginLoc (),
182+ " variable %0 of type %1 can be declared 'const'" )
183+ << Variable << VT;
184+ if (IsNormalVariableInTemplate)
185+ TemplateDiagnosticsCache.insert (Variable->getBeginLoc ());
186+ if (!CanBeFixIt)
187+ return ;
188+ using namespace utils ::fixit;
189+ if (VC == VariableCategory::Value && TransformValues) {
190+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
191+ Qualifiers::Const, QualifierTarget::Value,
192+ QualifierPolicy::Right);
193+ // FIXME: Add '{}' for default initialization if no user-defined default
194+ // constructor exists and there is no initializer.
195+ return ;
196+ }
176197
177- const auto *VarDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(" decl-stmt" );
198+ if (VC == VariableCategory::Reference && TransformReferences) {
199+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
200+ Qualifiers::Const, QualifierTarget::Value,
201+ QualifierPolicy::Right);
202+ return ;
203+ }
178204
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 ;
205+ if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
206+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
207+ Qualifiers::Const, QualifierTarget::Value,
208+ QualifierPolicy::Right);
209+ return ;
210+ }
211+ };
212+
213+ auto CheckPointee = [&]() {
214+ assert (VC == VariableCategory::Pointer);
215+ registerScope (LocalScope, Result.Context );
216+ if (ScopesCache[LocalScope]->isPointeeMutated (Variable))
217+ return ;
218+ auto Diag =
219+ diag (Variable->getBeginLoc (),
220+ " pointee of variable %0 of type %1 can be declared 'const'" )
221+ << Variable << VT;
222+ if (IsNormalVariableInTemplate)
223+ TemplateDiagnosticsCache.insert (Variable->getBeginLoc ());
224+ if (!CanBeFixIt)
225+ return ;
226+ using namespace utils ::fixit;
227+ if (TransformPointersAsPointers) {
228+ Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
229+ Qualifiers::Const, QualifierTarget::Pointee,
230+ QualifierPolicy::Right);
231+ }
232+ };
183233
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.
234+ // Each variable can only be in one category: Value, Pointer, Reference.
235+ // Analysis can be controlled for every category.
236+ if (VC == VariableCategory::Value && AnalyzeValues) {
237+ CheckValue ();
191238 return ;
192239 }
193-
194- if (VC == VariableCategory::Reference && TransformReferences) {
195- Diag << addQualifierToVarDecl (*Variable, *Result.Context , Qualifiers::Const,
196- QualifierTarget::Value,
197- QualifierPolicy::Right);
240+ if (VC == VariableCategory::Reference && AnalyzeReferences) {
241+ if (VT->getPointeeType ()->isPointerType () && !WarnPointersAsValues)
242+ return ;
243+ CheckValue ();
198244 return ;
199245 }
200-
201- if (VC == VariableCategory::Pointer) {
202- if (WarnPointersAsValues && TransformPointersAsValues) {
203- Diag << addQualifierToVarDecl (*Variable, *Result.Context ,
204- Qualifiers::Const, QualifierTarget::Value,
205- QualifierPolicy::Right);
246+ if (VC == VariableCategory::Pointer && AnalyzePointers) {
247+ if (WarnPointersAsValues && !VT.isConstQualified ())
248+ CheckValue ();
249+ if (WarnPointersAsPointers) {
250+ if (const auto *PT = dyn_cast<PointerType>(VT)) {
251+ if (!PT->getPointeeType ().isConstQualified ())
252+ CheckPointee ();
253+ }
254+ if (const auto *AT = dyn_cast<ArrayType>(VT)) {
255+ if (!AT->getElementType ().isConstQualified ()) {
256+ assert (AT->getElementType ()->isPointerType ());
257+ CheckPointee ();
258+ }
259+ }
206260 }
207261 return ;
208262 }
0 commit comments