10#include "../utils/LexerUtils.h"
11#include "clang/ASTMatchers/ASTMatchers.h"
12#include "llvm/ADT/SmallVector.h"
23 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
24 return InnerMatcher.matches(
Node.getUnqualifiedType(), Finder,
Builder);
27enum class Qualifier {
Const, Volatile, Restrict };
29std::optional<Token> findQualToken(
const VarDecl *
Decl, Qualifier Qual,
30 const MatchFinder::MatchResult &Result) {
35 assert((Qual == Qualifier::Const || Qual == Qualifier::Volatile ||
36 Qual == Qualifier::Restrict) &&
39 SourceLocation BeginLoc =
Decl->getQualifierLoc().getBeginLoc();
40 if (BeginLoc.isInvalid())
41 BeginLoc =
Decl->getBeginLoc();
42 SourceLocation EndLoc =
Decl->getLocation();
44 CharSourceRange FileRange = Lexer::makeFileCharRange(
45 CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,
46 Result.Context->getLangOpts());
48 if (FileRange.isInvalid())
52 Qual == Qualifier::Const
54 : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
57 *Result.SourceManager);
60std::optional<SourceRange>
61getTypeSpecifierLocation(
const VarDecl *Var,
62 const MatchFinder::MatchResult &Result) {
63 SourceRange TypeSpecifier(
64 Var->getTypeSpecStartLoc(),
65 Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength(
66 Var->getTypeSpecEndLoc(), *Result.SourceManager,
67 Result.Context->getLangOpts())));
69 if (TypeSpecifier.getBegin().isMacroID() ||
70 TypeSpecifier.getEnd().isMacroID())
75std::optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
76 const Token &ConstToken) {
77 if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
78 TypeSpecifier.setBegin(ConstToken.getLocation());
81 if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
82 TypeSpecifier.setEnd(ConstToken.getEndLoc());
85 return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
88bool isPointerConst(QualType QType) {
89 QualType
Pointee = QType->getPointeeType();
90 assert(!
Pointee.isNull() &&
"can't have a null Pointee");
91 return Pointee.isConstQualified();
94bool isAutoPointerConst(QualType QType) {
96 cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
97 assert(!
Pointee.isNull() &&
"can't have a null Pointee");
98 return Pointee.isConstQualified();
104 Options.
store(Opts,
"AddConstToQualified", AddConstToQualified);
108 auto ExplicitSingleVarDecl =
109 [](
const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
110 llvm::StringRef
ID) {
112 unless(isInTemplateInstantiation()),
114 varDecl(unless(isImplicit()), InnerMatcher).bind(
ID)));
116 auto ExplicitSingleVarDeclInTemplate =
117 [](
const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
118 llvm::StringRef
ID) {
120 isInTemplateInstantiation(),
122 varDecl(unless(isImplicit()), InnerMatcher).bind(
ID)));
125 auto IsBoundToType = refersToType(equalsBoundNode(
"type"));
126 auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));
127 auto IsAutoDeducedToPointer = [](
const auto &...InnerMatchers) {
128 return autoType(hasDeducedType(
129 hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...)))));
133 ExplicitSingleVarDecl(hasType(IsAutoDeducedToPointer(UnlessFunctionType)),
138 ExplicitSingleVarDeclInTemplate(
139 allOf(hasType(IsAutoDeducedToPointer(
140 hasUnqualifiedType(qualType().bind(
"type")),
141 UnlessFunctionType)),
143 functionDecl(hasAnyTemplateArgument(IsBoundToType))),
144 hasAncestor(classTemplateSpecializationDecl(
145 hasAnyTemplateArgument(IsBoundToType))))),
148 if (!AddConstToQualified)
150 Finder->addMatcher(ExplicitSingleVarDecl(
151 hasType(pointerType(pointee(autoType()))),
"auto_ptr"),
154 ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),
160 if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"auto")) {
161 SourceRange TypeSpecifier;
162 if (std::optional<SourceRange> TypeSpec =
163 getTypeSpecifierLocation(Var, Result)) {
164 TypeSpecifier = *TypeSpec;
168 llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
169 auto CheckQualifier = [&](
bool IsPresent, Qualifier Qual) {
171 std::optional<Token> Token = findQualToken(Var, Qual, Result);
172 if (!Token || Token->getLocation().isMacroID())
174 if (std::optional<SourceRange> Result =
175 mergeReplacementRange(TypeSpecifier, *Token))
176 RemoveQualifiersRange.push_back(*Result);
181 bool IsLocalConst = Var->getType().isLocalConstQualified();
182 bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
183 bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
185 if (CheckQualifier(IsLocalConst, Qualifier::Const) ||
186 CheckQualifier(IsLocalVolatile, Qualifier::Volatile) ||
187 CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
191 if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))
192 TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));
194 CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier);
195 if (FixItRange.isInvalid())
198 SourceLocation FixitLoc = FixItRange.getBegin();
199 for (SourceRange &
Range : RemoveQualifiersRange) {
200 if (
Range.getBegin() < FixitLoc)
201 FixitLoc =
Range.getBegin();
204 std::string ReplStr = [&] {
205 llvm::StringRef PtrConst = isPointerConst(Var->getType()) ?
"const " :
"";
206 llvm::StringRef LocalConst = IsLocalConst ?
"const " :
"";
207 llvm::StringRef LocalVol = IsLocalVolatile ?
"volatile " :
"";
208 llvm::StringRef LocalRestrict = IsLocalRestrict ?
"__restrict " :
"";
209 return (PtrConst +
"auto *" + LocalConst + LocalVol + LocalRestrict)
213 DiagnosticBuilder Diag =
215 "'%select{|const }0%select{|volatile }1%select{|__restrict }2auto "
216 "%3' can be declared as '%4%3'")
217 << IsLocalConst << IsLocalVolatile << IsLocalRestrict << Var->getName()
220 for (SourceRange &
Range : RemoveQualifiersRange) {
221 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
Range));
224 Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
227 if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"auto_ptr")) {
228 if (!isPointerConst(Var->getType()))
230 if (!isAutoPointerConst(Var->getType()))
234 if (Var->getType().isLocalConstQualified()) {
235 std::optional<Token> Token = findQualToken(Var, Qualifier::Const, Result);
236 if (!Token || Token->getLocation().isMacroID())
239 if (Var->getType().isLocalVolatileQualified()) {
240 std::optional<Token> Token =
241 findQualToken(Var, Qualifier::Volatile, Result);
242 if (!Token || Token->getLocation().isMacroID())
245 if (Var->getType().isLocalRestrictQualified()) {
246 std::optional<Token> Token =
247 findQualToken(Var, Qualifier::Restrict, Result);
248 if (!Token || Token->getLocation().isMacroID())
252 if (std::optional<SourceRange> TypeSpec =
253 getTypeSpecifierLocation(Var, Result)) {
254 if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
255 TypeSpec->getEnd().isMacroID())
257 SourceLocation InsertPos = TypeSpec->getBegin();
259 "'auto *%select{|const }0%select{|volatile }1%2' can be declared as "
260 "'const auto *%select{|const }0%select{|volatile }1%2'")
261 << Var->getType().isLocalConstQualified()
262 << Var->getType().isLocalVolatileQualified() << Var->getName()
263 << FixItHint::CreateInsertion(InsertPos,
"const ");
267 if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"auto_ref")) {
268 if (!isPointerConst(Var->getType()))
270 if (!isAutoPointerConst(Var->getType()))
274 if (std::optional<SourceRange> TypeSpec =
275 getTypeSpecifierLocation(Var, Result)) {
276 if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
277 TypeSpec->getEnd().isMacroID())
279 SourceLocation InsertPos = TypeSpec->getBegin();
280 diag(InsertPos,
"'auto &%0' can be declared as 'const auto &%0'")
281 << Var->getName() << FixItHint::CreateInsertion(InsertPos,
"const ");
const FunctionDecl * Decl
CodeCompletionBuilder Builder
CharSourceRange Range
SourceRange for the file name.
::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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
std::optional< Token > getQualifyingToken(tok::TokenKind TK, CharSourceRange Range, const ASTContext &Context, const SourceManager &SM)
Assuming that Range spans a CVR-qualified type, returns the token in Range that is responsible for th...
llvm::StringMap< ClangTidyValue > OptionMap