13
13
#include " clang/AST/ASTContext.h"
14
14
#include " clang/ASTMatchers/ASTMatchFinder.h"
15
15
#include " clang/ASTMatchers/ASTMatchers.h"
16
+ #include < cassert>
16
17
17
18
using namespace clang ::ast_matchers;
18
19
@@ -37,36 +38,54 @@ AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
37
38
ConstCorrectnessCheck::ConstCorrectnessCheck (StringRef Name,
38
39
ClangTidyContext *Context)
39
40
: ClangTidyCheck(Name, Context),
40
- AnalyzeValues (Options.get(" AnalyzeValues " , true )),
41
+ AnalyzePointers (Options.get(" AnalyzePointers " , true )),
41
42
AnalyzeReferences(Options.get(" AnalyzeReferences" , true )),
43
+ AnalyzeValues(Options.get(" AnalyzeValues" , true )),
44
+
45
+ WarnPointersAsPointers(Options.get(" WarnPointersAsPointers" , true )),
42
46
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 )),
45
50
TransformPointersAsValues(
46
51
Options.get(" TransformPointersAsValues" , false )),
52
+ TransformReferences(Options.get(" TransformReferences" , true )),
53
+ TransformValues(Options.get(" TransformValues" , true )),
54
+
47
55
AllowedTypes(
48
56
utils::options::parseStringList (Options.get(" AllowedTypes" , " " ))) {
49
- if (AnalyzeValues == false && AnalyzeReferences == false )
57
+ if (AnalyzeValues == false && AnalyzeReferences == false &&
58
+ AnalyzePointers == false )
50
59
this ->configurationDiag (
51
60
" 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." );
54
63
}
55
64
56
65
void ConstCorrectnessCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
57
- Options.store (Opts, " AnalyzeValues " , AnalyzeValues );
66
+ Options.store (Opts, " AnalyzePointers " , AnalyzePointers );
58
67
Options.store (Opts, " AnalyzeReferences" , AnalyzeReferences);
68
+ Options.store (Opts, " AnalyzeValues" , AnalyzeValues);
69
+
70
+ Options.store (Opts, " WarnPointersAsPointers" , WarnPointersAsPointers);
59
71
Options.store (Opts, " WarnPointersAsValues" , WarnPointersAsValues);
60
72
61
- Options.store (Opts, " TransformValues " , TransformValues);
62
- Options. store (Opts, " TransformReferences " , TransformReferences );
73
+ Options.store (Opts, " TransformPointersAsPointers " ,
74
+ TransformPointersAsPointers );
63
75
Options.store (Opts, " TransformPointersAsValues" , TransformPointersAsValues);
76
+ Options.store (Opts, " TransformReferences" , TransformReferences);
77
+ Options.store (Opts, " TransformValues" , TransformValues);
78
+
64
79
Options.store (Opts, " AllowedTypes" ,
65
80
utils::options::serializeStringList (AllowedTypes));
66
81
}
67
82
68
83
void 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
+
70
89
const auto ConstReference = hasType (references (isConstQualified ()));
71
90
const auto RValueReference = hasType (
72
91
referenceType (anyOf (rValueReferenceType (), unless (isSpelledAsLValue ()))));
@@ -124,6 +143,11 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
124
143
const auto *LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope" );
125
144
const auto *Variable = Result.Nodes .getNodeAs <VarDecl>(" local-value" );
126
145
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 ();
127
151
128
152
// / If the variable was declared in a template it might be analyzed multiple
129
153
// / times. Only one of those instantiations shall emit a warning. NOTE: This
@@ -137,72 +161,102 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
137
161
138
162
VariableCategory VC = VariableCategory::Value;
139
163
const QualType VT = Variable->getType ();
140
- if (VT->isReferenceType ())
164
+ if (VT->isReferenceType ()) {
141
165
VC = VariableCategory::Reference;
142
- else if (VT->isPointerType ())
166
+ } else if (VT->isPointerType ()) {
143
167
VC = VariableCategory::Pointer;
144
- else if (const auto *ArrayT = dyn_cast<ArrayType>(VT))
168
+ } else if (const auto *ArrayT = dyn_cast<ArrayType>(VT)) {
145
169
if (ArrayT->getElementType ()->isPointerType ())
146
170
VC = VariableCategory::Pointer;
171
+ }
147
172
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
+ }
176
197
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
+ }
178
204
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
+ };
183
233
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 ();
191
238
return ;
192
239
}
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 ();
198
244
return ;
199
245
}
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
+ }
206
260
}
207
261
return ;
208
262
}
0 commit comments