10#include "../utils/FixItHintUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
24 ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
25 return ast_matchers::internal::matchesFirstInPointerRange(
26 InnerMatcher,
Node.decl_begin(),
Node.decl_end(), Finder,
30 return Node.isSpelledAsLValue();
38 AnalyzeValues(Options.get(
"AnalyzeValues", true)),
39 AnalyzeReferences(Options.get(
"AnalyzeReferences", true)),
40 WarnPointersAsValues(Options.get(
"WarnPointersAsValues", false)),
41 TransformValues(Options.get(
"TransformValues", true)),
42 TransformReferences(Options.get(
"TransformReferences", true)),
43 TransformPointersAsValues(
44 Options.get(
"TransformPointersAsValues", false)) {
45 if (AnalyzeValues ==
false && AnalyzeReferences ==
false)
47 "The check 'misc-const-correctness' will not "
48 "perform any analysis because both 'AnalyzeValues' and "
49 "'AnalyzeReferences' are false.");
54 Options.
store(Opts,
"AnalyzeReferences", AnalyzeReferences);
55 Options.
store(Opts,
"WarnPointersAsValues", WarnPointersAsValues);
58 Options.
store(Opts,
"TransformReferences", TransformReferences);
59 Options.
store(Opts,
"TransformPointersAsValues", TransformPointersAsValues);
63 const auto ConstType = hasType(isConstQualified());
64 const auto ConstReference = hasType(references(isConstQualified()));
65 const auto RValueReference = hasType(
66 referenceType(anyOf(rValueReferenceType(), unless(isSpelledAsLValue()))));
68 const auto TemplateType = anyOf(
69 hasType(hasCanonicalType(templateTypeParmType())),
70 hasType(substTemplateTypeParmType()), hasType(isDependentType()),
73 hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
74 hasType(referenceType(pointee(substTemplateTypeParmType()))));
76 const auto AutoTemplateType = varDecl(
77 anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),
78 hasType(pointerType(pointee(autoType())))));
80 const auto FunctionPointerRef =
81 hasType(hasCanonicalType(referenceType(pointee(functionType()))));
85 const auto LocalValDecl = varDecl(
86 isLocal(), hasInitializer(anything()),
87 unless(anyOf(ConstType, ConstReference, TemplateType,
88 hasInitializer(isInstantiationDependent()), AutoTemplateType,
89 RValueReference, FunctionPointerRef,
90 hasType(cxxRecordDecl(isLambda())), isImplicit())));
94 const auto FunctionScope =
96 hasBody(stmt(forEachDescendant(
97 declStmt(containsAnyDeclaration(
98 LocalValDecl.bind(
"local-value")),
99 unless(has(decompositionDecl())))
102 .bind(
"function-decl");
104 Finder->addMatcher(FunctionScope,
this);
111 const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>(
"scope");
112 const auto *Variable = Result.Nodes.getNodeAs<VarDecl>(
"local-value");
113 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(
"function-decl");
120 bool IsNormalVariableInTemplate = Function->isTemplateInstantiation();
121 if (IsNormalVariableInTemplate &&
122 TemplateDiagnosticsCache.contains(Variable->getBeginLoc()))
126 if (Variable->getType()->isReferenceType())
128 if (Variable->getType()->isPointerType())
130 if (Variable->getType()->isArrayType()) {
131 if (
const auto *ArrayT = dyn_cast<ArrayType>(Variable->getType())) {
132 if (ArrayT->getElementType()->isPointerType())
143 Variable->getType()->getPointeeType()->isPointerType() &&
144 !WarnPointersAsValues)
154 registerScope(LocalScope, Result.Context);
157 if (ScopesCache[LocalScope]->isMutated(Variable))
160 auto Diag =
diag(Variable->getBeginLoc(),
161 "variable %0 of type %1 can be declared 'const'")
162 << Variable << Variable->getType();
163 if (IsNormalVariableInTemplate)
164 TemplateDiagnosticsCache.insert(Variable->getBeginLoc());
166 const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>(
"decl-stmt");
170 if (VarDeclStmt ==
nullptr || !VarDeclStmt->isSingleDecl())
173 using namespace utils::fixit;
175 Diag << addQualifierToVarDecl(*Variable, *Result.Context, Qualifiers::Const,
176 QualifierTarget::Value,
177 QualifierPolicy::Right);
184 Diag << addQualifierToVarDecl(*Variable, *Result.Context, Qualifiers::Const,
185 QualifierTarget::Value,
186 QualifierPolicy::Right);
191 if (WarnPointersAsValues && TransformPointersAsValues) {
192 Diag << addQualifierToVarDecl(*Variable, *Result.Context,
193 Qualifiers::Const, QualifierTarget::Value,
194 QualifierPolicy::Right);
200void ConstCorrectnessCheck::registerScope(
const Stmt *LocalScope,
201 ASTContext *Context) {
202 auto &Analyzer = ScopesCache[LocalScope];
204 Analyzer = std::make_unique<ExprMutationAnalyzer>(*LocalScope, *Context);
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
ConstCorrectnessCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
AST_MATCHER(Expr, isMacroID)
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
VariableCategory
Classify for a variable in what the Const-Check is interested.
llvm::StringMap< ClangTidyValue > OptionMap