55 AnalyzePointers(Options.get(
"AnalyzePointers", true)),
56 AnalyzeReferences(Options.get(
"AnalyzeReferences", true)),
57 AnalyzeValues(Options.get(
"AnalyzeValues", true)),
58 AnalyzeParameters(Options.get(
"AnalyzeParameters", true)),
60 WarnPointersAsPointers(Options.get(
"WarnPointersAsPointers", true)),
61 WarnPointersAsValues(Options.get(
"WarnPointersAsValues", false)),
63 TransformPointersAsPointers(
64 Options.get(
"TransformPointersAsPointers", true)),
65 TransformPointersAsValues(
66 Options.get(
"TransformPointersAsValues", false)),
67 TransformReferences(Options.get(
"TransformReferences", true)),
68 TransformValues(Options.get(
"TransformValues", true)),
71 utils::options::parseStringList(Options.get(
"AllowedTypes",
""))) {
72 if (AnalyzeValues ==
false && AnalyzeReferences ==
false &&
73 AnalyzePointers ==
false)
74 this->configurationDiag(
75 "The check 'misc-const-correctness' will not "
76 "perform any analysis because 'AnalyzeValues', "
77 "'AnalyzeReferences' and 'AnalyzePointers' are false.");
100 const auto ConstType =
101 hasType(qualType(isConstQualified(),
103 unless(pointerType())));
105 const auto ConstReference = hasType(references(isConstQualified()));
106 const auto RValueReference = hasType(
107 referenceType(anyOf(rValueReferenceType(), unless(isSpelledAsLValue()))));
109 const auto TemplateType = anyOf(
110 hasType(hasCanonicalType(templateTypeParmType())),
111 hasType(substTemplateTypeParmType()), hasType(isDependentType()),
114 hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
115 hasType(referenceType(pointee(substTemplateTypeParmType()))));
117 auto AllowedTypeDecl = namedDecl(anyOf(
120 const auto AllowedType = hasType(qualType(
121 anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl),
122 pointerType(pointee(hasDeclaration(AllowedTypeDecl))))));
124 const auto AutoTemplateType = varDecl(
125 anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),
126 hasType(pointerType(pointee(autoType())))));
128 const auto FunctionPointerRef =
129 hasType(hasCanonicalType(referenceType(pointee(functionType()))));
131 const auto CommonExcludeTypes =
132 anyOf(ConstType, ConstReference, RValueReference, TemplateType,
133 FunctionPointerRef, hasType(cxxRecordDecl(isLambda())),
134 AutoTemplateType, isImplicit(), AllowedType);
138 const auto LocalValDecl =
139 varDecl(isLocal(), hasInitializer(unless(isInstantiationDependent())),
140 unless(CommonExcludeTypes));
144 const auto FunctionScope =
145 functionDecl(hasBody(stmt(forEachDescendant(
146 declStmt(containsAnyDeclaration(
147 LocalValDecl.bind(
"value")),
148 unless(has(decompositionDecl())))
151 .bind(
"function-decl");
153 Finder->addMatcher(FunctionScope,
this);
155 if (AnalyzeParameters) {
156 const auto ParamMatcher =
157 parmVarDecl(unless(CommonExcludeTypes), unless(isUnnamed()),
158 anyOf(hasType(referenceType()), hasType(pointerType())))
163 const auto FunctionWithParams =
165 hasBody(stmt().bind(
"scope")), has(typeLoc(forEach(ParamMatcher))),
166 unless(cxxMethodDecl()), unless(isFunctionTemplateSpecialization()),
167 unless(isTemplate()))
168 .bind(
"function-decl");
170 Finder->addMatcher(FunctionWithParams,
this);
175 const VarDecl *Variable,
176 const FunctionDecl *Function,
177 const ASTContext &Context, Qualifiers::TQ Qualifier,
182 if (
const auto *ParamDecl = dyn_cast<ParmVarDecl>(Variable)) {
183 const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
186 Function->redecls(), [ParamIdx](
const FunctionDecl *Redecl) {
187 const QualType Type = Redecl->getParamDecl(ParamIdx)->getType();
188 return Type->isTypedefNameType() || Type->getAs<UsingType>();
192 for (
const FunctionDecl *Redecl : Function->redecls()) {
193 Diag << addQualifierToVarDecl(*Redecl->getParamDecl(ParamIdx), Context,
194 Qualifier, Target, Policy);
197 Diag << addQualifierToVarDecl(*Variable, Context, Qualifier, Target,
210 const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>(
"scope");
211 const auto *Variable = Result.Nodes.getNodeAs<VarDecl>(
"value");
212 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(
"function-decl");
213 const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>(
"decl-stmt");
215 assert(Variable && LocalScope && Function);
221 const bool CanBeFixIt = isa<ParmVarDecl>(Variable) ||
222 (VarDeclStmt && VarDeclStmt->isSingleDecl());
229 bool IsNormalVariableInTemplate = Function->isTemplateInstantiation();
230 if (IsNormalVariableInTemplate &&
231 TemplateDiagnosticsCache.contains(Variable->getBeginLoc()))
234 VariableCategory VC = VariableCategory::Value;
235 const QualType VT = Variable->getType();
236 if (VT->isReferenceType()) {
237 VC = VariableCategory::Reference;
238 }
else if (VT->isPointerType()) {
239 VC = VariableCategory::Pointer;
240 }
else if (
const auto *ArrayT = dyn_cast<ArrayType>(VT)) {
241 if (ArrayT->getElementType()->isPointerType())
242 VC = VariableCategory::Pointer;
245 auto CheckValue = [&]() {
247 if (isMutated(Variable, LocalScope, Function, Result.Context))
250 auto Diag = diag(Variable->getBeginLoc(),
251 "variable %0 of type %1 can be declared 'const'")
253 if (IsNormalVariableInTemplate)
254 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
259 if (VC == VariableCategory::Value && TransformValues) {
261 Qualifiers::Const, QualifierTarget::Value,
262 QualifierPolicy::Right);
268 if (VC == VariableCategory::Reference && TransformReferences) {
270 Qualifiers::Const, QualifierTarget::Value,
271 QualifierPolicy::Right);
275 if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
277 Qualifiers::Const, QualifierTarget::Value,
278 QualifierPolicy::Right);
283 auto CheckPointee = [&]() {
284 assert(VC == VariableCategory::Pointer);
285 registerScope(LocalScope, Result.Context);
286 if (ScopesCache[LocalScope]->isPointeeMutated(Variable))
289 diag(Variable->getBeginLoc(),
290 "pointee of variable %0 of type %1 can be declared 'const'")
292 if (IsNormalVariableInTemplate)
293 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
297 if (TransformPointersAsPointers) {
299 Qualifiers::Const, QualifierTarget::Pointee,
300 QualifierPolicy::Right);
306 if (VC == VariableCategory::Value && AnalyzeValues) {
310 if (VC == VariableCategory::Reference && AnalyzeReferences) {
311 if (VT->getPointeeType()->isPointerType() && !WarnPointersAsValues)
316 if (VC == VariableCategory::Pointer && AnalyzePointers) {
317 if (WarnPointersAsValues && !VT.isConstQualified())
319 if (WarnPointersAsPointers) {
320 if (
const auto *PT = dyn_cast<PointerType>(VT)) {
321 if (!PT->getPointeeType().isConstQualified() &&
322 !PT->getPointeeType()->isFunctionType())
325 if (
const auto *AT = dyn_cast<ArrayType>(VT)) {
326 assert(AT->getElementType()->isPointerType());
327 if (!AT->getElementType()->getPointeeType().isConstQualified())
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.