60 AnalyzePointers(Options.get(
"AnalyzePointers", true)),
61 AnalyzeReferences(Options.get(
"AnalyzeReferences", true)),
62 AnalyzeValues(Options.get(
"AnalyzeValues", true)),
63 AnalyzeAutoVariables(Options.get(
"AnalyzeAutoVariables", true)),
64 AnalyzeLambdas(Options.get(
"AnalyzeLambdas", true)),
65 AnalyzeParameters(Options.get(
"AnalyzeParameters", true)),
67 WarnPointersAsPointers(Options.get(
"WarnPointersAsPointers", true)),
68 WarnPointersAsValues(Options.get(
"WarnPointersAsValues", false)),
70 TransformPointersAsPointers(
71 Options.get(
"TransformPointersAsPointers", true)),
72 TransformPointersAsValues(
73 Options.get(
"TransformPointersAsValues", false)),
74 TransformReferences(Options.get(
"TransformReferences", true)),
75 TransformValues(Options.get(
"TransformValues", true)),
78 utils::options::parseStringList(Options.get(
"AllowedTypes",
""))) {
79 if (AnalyzeValues ==
false && AnalyzeReferences ==
false &&
80 AnalyzePointers ==
false)
81 this->configurationDiag(
82 "The check 'misc-const-correctness' will not "
83 "perform any analysis because 'AnalyzeValues', "
84 "'AnalyzeReferences' and 'AnalyzePointers' are false.");
86 if (AnalyzeLambdas && !AnalyzeAutoVariables)
87 this->configurationDiag(
"The check 'misc-const-correctness' will not "
88 "analyze lambdas because 'AnalyzeLambdas' has no "
89 "effect while 'AnalyzeAutoVariables' is false.");
114 const auto ConstType =
115 hasType(qualType(isConstQualified(),
117 unless(pointerType())));
119 const auto ConstReference = hasType(references(isConstQualified()));
120 const auto RValueReference = hasType(
121 referenceType(anyOf(rValueReferenceType(), unless(isSpelledAsLValue()))));
123 const auto TemplateType = anyOf(
124 hasType(hasCanonicalType(templateTypeParmType())),
125 hasType(substTemplateTypeParmType()), hasType(isDependentType()),
128 hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
129 hasType(referenceType(pointee(substTemplateTypeParmType()))));
131 const auto AllowedTypeDecl = namedDecl(anyOf(
134 const auto AllowedType = hasType(qualType(
135 anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl),
136 pointerType(pointee(hasDeclaration(AllowedTypeDecl))))));
138 const auto AutoTemplateType = varDecl(
139 anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),
140 hasType(pointerType(pointee(autoType())))));
142 const auto FunctionPointerRef =
143 hasType(hasCanonicalType(referenceType(pointee(functionType()))));
145 const auto CommonExcludeTypes =
146 anyOf(ConstType, ConstReference, RValueReference, TemplateType,
147 FunctionPointerRef, hasType(cxxRecordDecl(isLambda())),
148 AutoTemplateType, isImplicit(), AllowedType);
152 const auto LocalValDecl = varDecl(
153 isLocal(), hasInitializer(anything()),
154 unless(anyOf(ConstType, ConstReference, TemplateType,
155 hasInitializer(isInstantiationDependent()), RValueReference,
156 FunctionPointerRef, isImplicit(), AllowedType)),
158 ? Matcher<VarDecl>(anything())
159 : Matcher<VarDecl>(unless(hasType(cxxRecordDecl(isLambda())))),
161 ? Matcher<VarDecl>(anything())
162 : Matcher<VarDecl>(unless(hasTypeLoc(hasContainedAutoType()))));
166 const auto FunctionScope =
167 functionDecl(hasBody(stmt(forEachDescendant(
168 declStmt(containsAnyDeclaration(
169 LocalValDecl.bind(
"value")),
170 unless(has(decompositionDecl())))
173 .bind(
"function-decl");
175 Finder->addMatcher(FunctionScope,
this);
177 if (AnalyzeParameters) {
178 const auto ParamMatcher =
179 parmVarDecl(unless(CommonExcludeTypes), unless(isUnnamed()),
180 anyOf(hasType(referenceType()), hasType(pointerType())))
185 const auto FunctionWithParams =
187 hasBody(stmt().bind(
"scope")), has(typeLoc(forEach(ParamMatcher))),
188 unless(cxxMethodDecl()), unless(isFunctionTemplateSpecialization()),
189 unless(isTemplate()))
190 .bind(
"function-decl");
192 Finder->addMatcher(FunctionWithParams,
this);
197 const VarDecl *Variable,
198 const FunctionDecl *Function,
199 const ASTContext &Context, Qualifiers::TQ Qualifier,
204 if (
const auto *ParamDecl = dyn_cast<ParmVarDecl>(Variable)) {
205 const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
208 Function->redecls(), [ParamIdx](
const FunctionDecl *Redecl) {
209 const QualType Type = Redecl->getParamDecl(ParamIdx)->getType();
210 return Type->isTypedefNameType() || Type->getAs<UsingType>();
214 for (
const FunctionDecl *Redecl : Function->redecls()) {
215 Diag << addQualifierToVarDecl(*Redecl->getParamDecl(ParamIdx), Context,
216 Qualifier, Target, Policy);
219 Diag << addQualifierToVarDecl(*Variable, Context, Qualifier, Target,
232 const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>(
"scope");
233 const auto *Variable = Result.Nodes.getNodeAs<VarDecl>(
"value");
234 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(
"function-decl");
235 const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>(
"decl-stmt");
237 assert(Variable && LocalScope && Function);
243 const bool CanBeFixIt = isa<ParmVarDecl>(Variable) ||
244 (VarDeclStmt && VarDeclStmt->isSingleDecl());
251 bool IsNormalVariableInTemplate = Function->isTemplateInstantiation();
252 if (IsNormalVariableInTemplate &&
253 TemplateDiagnosticsCache.contains(Variable->getBeginLoc()))
256 VariableCategory VC = VariableCategory::Value;
257 const QualType VT = Variable->getType();
258 if (VT->isReferenceType()) {
259 VC = VariableCategory::Reference;
260 }
else if (VT->isPointerType()) {
261 VC = VariableCategory::Pointer;
262 }
else if (
const auto *ArrayT = dyn_cast<ArrayType>(VT)) {
263 if (ArrayT->getElementType()->isPointerType())
264 VC = VariableCategory::Pointer;
267 auto CheckValue = [&]() {
269 if (isMutated(Variable, LocalScope, Function, Result.Context))
272 auto Diag = diag(Variable->getBeginLoc(),
273 "variable %0 of type %1 can be declared 'const'")
275 if (IsNormalVariableInTemplate)
276 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
281 if (VC == VariableCategory::Value && TransformValues) {
283 Qualifiers::Const, QualifierTarget::Value,
284 QualifierPolicy::Right);
290 if (VC == VariableCategory::Reference && TransformReferences) {
292 Qualifiers::Const, QualifierTarget::Value,
293 QualifierPolicy::Right);
297 if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
299 Qualifiers::Const, QualifierTarget::Value,
300 QualifierPolicy::Right);
305 auto CheckPointee = [&]() {
306 assert(VC == VariableCategory::Pointer);
307 registerScope(LocalScope, Result.Context);
308 if (ScopesCache[LocalScope]->isPointeeMutated(Variable))
311 diag(Variable->getBeginLoc(),
312 "pointee of variable %0 of type %1 can be declared 'const'")
314 if (IsNormalVariableInTemplate)
315 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
319 if (TransformPointersAsPointers) {
321 Qualifiers::Const, QualifierTarget::Pointee,
322 QualifierPolicy::Right);
328 if (VC == VariableCategory::Value && AnalyzeValues) {
332 if (VC == VariableCategory::Reference && AnalyzeReferences) {
333 if (VT->getPointeeType()->isPointerType() && !WarnPointersAsValues)
338 if (VC == VariableCategory::Pointer && AnalyzePointers) {
339 if (WarnPointersAsValues && !VT.isConstQualified())
341 if (WarnPointersAsPointers) {
342 if (
const auto *PT = dyn_cast<PointerType>(VT)) {
343 if (!PT->getPointeeType().isConstQualified() &&
344 !PT->getPointeeType()->isFunctionType())
347 if (
const auto *AT = dyn_cast<ArrayType>(VT)) {
348 assert(AT->getElementType()->isPointerType());
349 if (!AT->getElementType()->getPointeeType().isConstQualified())
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.