84 const auto ConstType =
85 hasType(qualType(isConstQualified(),
87 unless(pointerType())));
89 const auto ConstReference = hasType(references(isConstQualified()));
90 const auto RValueReference = hasType(
91 referenceType(anyOf(rValueReferenceType(), unless(isSpelledAsLValue()))));
93 const auto TemplateType = anyOf(
94 hasType(hasCanonicalType(templateTypeParmType())),
95 hasType(substTemplateTypeParmType()), hasType(isDependentType()),
98 hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
99 hasType(referenceType(pointee(substTemplateTypeParmType()))));
101 auto AllowedTypeDecl = namedDecl(anyOf(
104 const auto AllowedType = hasType(qualType(
105 anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl),
106 pointerType(pointee(hasDeclaration(AllowedTypeDecl))))));
108 const auto AutoTemplateType = varDecl(
109 anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),
110 hasType(pointerType(pointee(autoType())))));
112 const auto FunctionPointerRef =
113 hasType(hasCanonicalType(referenceType(pointee(functionType()))));
117 const auto LocalValDecl = varDecl(
118 isLocal(), hasInitializer(anything()),
119 unless(anyOf(ConstType, ConstReference, TemplateType,
120 hasInitializer(isInstantiationDependent()), AutoTemplateType,
121 RValueReference, FunctionPointerRef,
122 hasType(cxxRecordDecl(isLambda())), isImplicit(),
127 const auto FunctionScope =
129 hasBody(stmt(forEachDescendant(
130 declStmt(containsAnyDeclaration(
131 LocalValDecl.bind(
"local-value")),
132 unless(has(decompositionDecl())))
135 .bind(
"function-decl");
137 Finder->addMatcher(FunctionScope,
this);
148 const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>(
"scope");
149 const auto *Variable = Result.Nodes.getNodeAs<VarDecl>(
"local-value");
150 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(
"function-decl");
151 const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>(
"decl-stmt");
155 const bool CanBeFixIt = VarDeclStmt !=
nullptr && VarDeclStmt->isSingleDecl();
162 bool IsNormalVariableInTemplate = Function->isTemplateInstantiation();
163 if (IsNormalVariableInTemplate &&
164 TemplateDiagnosticsCache.contains(Variable->getBeginLoc()))
167 VariableCategory VC = VariableCategory::Value;
168 const QualType VT = Variable->getType();
169 if (VT->isReferenceType()) {
170 VC = VariableCategory::Reference;
171 }
else if (VT->isPointerType()) {
172 VC = VariableCategory::Pointer;
173 }
else if (
const auto *ArrayT = dyn_cast<ArrayType>(VT)) {
174 if (ArrayT->getElementType()->isPointerType())
175 VC = VariableCategory::Pointer;
178 auto CheckValue = [&]() {
180 registerScope(LocalScope, Result.Context);
183 if (ScopesCache[LocalScope]->isMutated(Variable))
186 auto Diag = diag(Variable->getBeginLoc(),
187 "variable %0 of type %1 can be declared 'const'")
189 if (IsNormalVariableInTemplate)
190 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
194 if (VC == VariableCategory::Value && TransformValues) {
195 Diag << addQualifierToVarDecl(*Variable, *Result.Context,
196 Qualifiers::Const, QualifierTarget::Value,
197 QualifierPolicy::Right);
203 if (VC == VariableCategory::Reference && TransformReferences) {
204 Diag << addQualifierToVarDecl(*Variable, *Result.Context,
205 Qualifiers::Const, QualifierTarget::Value,
206 QualifierPolicy::Right);
210 if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
211 Diag << addQualifierToVarDecl(*Variable, *Result.Context,
212 Qualifiers::Const, QualifierTarget::Value,
213 QualifierPolicy::Right);
218 auto CheckPointee = [&]() {
219 assert(VC == VariableCategory::Pointer);
220 registerScope(LocalScope, Result.Context);
221 if (ScopesCache[LocalScope]->isPointeeMutated(Variable))
224 diag(Variable->getBeginLoc(),
225 "pointee of variable %0 of type %1 can be declared 'const'")
227 if (IsNormalVariableInTemplate)
228 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
232 if (TransformPointersAsPointers) {
233 Diag << addQualifierToVarDecl(*Variable, *Result.Context,
234 Qualifiers::Const, QualifierTarget::Pointee,
235 QualifierPolicy::Right);
241 if (VC == VariableCategory::Value && AnalyzeValues) {
245 if (VC == VariableCategory::Reference && AnalyzeReferences) {
246 if (VT->getPointeeType()->isPointerType() && !WarnPointersAsValues)
251 if (VC == VariableCategory::Pointer && AnalyzePointers) {
252 if (WarnPointersAsValues && !VT.isConstQualified())
254 if (WarnPointersAsPointers) {
255 if (
const auto *PT = dyn_cast<PointerType>(VT)) {
256 if (!PT->getPointeeType().isConstQualified() &&
257 !PT->getPointeeType()->isFunctionType())
260 if (
const auto *AT = dyn_cast<ArrayType>(VT)) {
261 if (!AT->getElementType().isConstQualified()) {
262 assert(AT->getElementType()->isPointerType());
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.