13
13
#include " clang/AST/ASTContext.h"
14
14
#include " clang/ASTMatchers/ASTMatchFinder.h"
15
15
#include " clang/ASTMatchers/ASTMatchers.h"
16
+ #include " llvm/Support/Casting.h"
17
+ #include < cassert>
16
18
17
19
using namespace clang ::ast_matchers;
18
20
@@ -39,34 +41,47 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name,
39
41
: ClangTidyCheck(Name, Context),
40
42
AnalyzeValues (Options.get(" AnalyzeValues" , true )),
41
43
AnalyzeReferences(Options.get(" AnalyzeReferences" , true )),
44
+ AnalyzePointers(Options.get(" AnalyzePointers" , true )),
42
45
WarnPointersAsValues(Options.get(" WarnPointersAsValues" , false )),
46
+ WarnPointersAsPointers(Options.get(" WarnPointersAsPointers" , true )),
43
47
TransformValues(Options.get(" TransformValues" , true )),
44
48
TransformReferences(Options.get(" TransformReferences" , true )),
45
49
TransformPointersAsValues(
46
50
Options.get(" TransformPointersAsValues" , false )),
51
+ TransformPointersAsPointers(
52
+ Options.get(" TransformPointersAsPointers" , true )),
47
53
AllowedTypes(
48
54
utils::options::parseStringList (Options.get(" AllowedTypes" , " " ))) {
49
- if (AnalyzeValues == false && AnalyzeReferences == false )
55
+ if (AnalyzeValues == false && AnalyzeReferences == false &&
56
+ AnalyzePointers == false )
50
57
this ->configurationDiag (
51
58
" 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." );
54
61
}
55
62
56
63
void ConstCorrectnessCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
57
64
Options.store (Opts, " AnalyzeValues" , AnalyzeValues);
58
65
Options.store (Opts, " AnalyzeReferences" , AnalyzeReferences);
66
+ Options.store (Opts, " AnalyzePointers" , AnalyzePointers);
59
67
Options.store (Opts, " WarnPointersAsValues" , WarnPointersAsValues);
68
+ Options.store (Opts, " WarnPointersAsPointers" , WarnPointersAsPointers);
60
69
61
70
Options.store (Opts, " TransformValues" , TransformValues);
62
71
Options.store (Opts, " TransformReferences" , TransformReferences);
63
72
Options.store (Opts, " TransformPointersAsValues" , TransformPointersAsValues);
73
+ Options.store (Opts, " TransformPointersAsPointers" ,
74
+ TransformPointersAsPointers);
64
75
Options.store (Opts, " AllowedTypes" ,
65
76
utils::options::serializeStringList (AllowedTypes));
66
77
}
67
78
68
79
void 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
+
70
85
const auto ConstReference = hasType (references (isConstQualified ()));
71
86
const auto RValueReference = hasType (
72
87
referenceType (anyOf (rValueReferenceType (), unless (isSpelledAsLValue ()))));
@@ -124,6 +139,11 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
124
139
const auto *LocalScope = Result.Nodes .getNodeAs <Stmt>(" scope" );
125
140
const auto *Variable = Result.Nodes .getNodeAs <VarDecl>(" local-value" );
126
141
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 ();
127
147
128
148
// / If the variable was declared in a template it might be analyzed multiple
129
149
// / times. Only one of those instantiations shall emit a warning. NOTE: This
@@ -145,64 +165,90 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
145
165
if (ArrayT->getElementType ()->isPointerType ())
146
166
VC = VariableCategory::Pointer;
147
167
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
+ }
176
192
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
+ }
178
199
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
+ };
183
227
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 ();
191
232
return ;
192
233
}
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 ();
198
238
return ;
199
239
}
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
+ }
206
252
}
207
253
return ;
208
254
}
0 commit comments