10 #include "../utils/LexerUtils.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "llvm/ADT/Optional.h"
13 #include "llvm/ADT/SmallVector.h"
19 namespace readability {
25 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
26 return InnerMatcher.matches(Node.getUnqualifiedType(), Finder,
Builder);
31 llvm::Optional<Token> findQualToken(
const VarDecl *
Decl, Qualifier Qual,
32 const MatchFinder::MatchResult &Result) {
38 Qual == Qualifier::Restrict) &&
41 SourceLocation BeginLoc =
Decl->getQualifierLoc().getBeginLoc();
42 if (BeginLoc.isInvalid())
43 BeginLoc =
Decl->getBeginLoc();
44 SourceLocation EndLoc =
Decl->getLocation();
46 CharSourceRange FileRange = Lexer::makeFileCharRange(
47 CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,
48 Result.Context->getLangOpts());
50 if (FileRange.isInvalid())
56 : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
59 *Result.SourceManager);
62 llvm::Optional<SourceRange>
63 getTypeSpecifierLocation(
const VarDecl *Var,
64 const MatchFinder::MatchResult &Result) {
65 SourceRange TypeSpecifier(
66 Var->getTypeSpecStartLoc(),
67 Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength(
68 Var->getTypeSpecEndLoc(), *Result.SourceManager,
69 Result.Context->getLangOpts())));
71 if (TypeSpecifier.getBegin().isMacroID() ||
72 TypeSpecifier.getEnd().isMacroID())
77 llvm::Optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
78 const Token &ConstToken) {
79 if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
80 TypeSpecifier.setBegin(ConstToken.getLocation());
83 if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
84 TypeSpecifier.setEnd(ConstToken.getEndLoc());
87 return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
90 bool isPointerConst(QualType QType) {
91 QualType
Pointee = QType->getPointeeType();
92 assert(!
Pointee.isNull() &&
"can't have a null Pointee");
93 return Pointee.isConstQualified();
96 bool isAutoPointerConst(QualType QType) {
98 cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
99 assert(!
Pointee.isNull() &&
"can't have a null Pointee");
100 return Pointee.isConstQualified();
106 Options.store(Opts,
"AddConstToQualified", AddConstToQualified);
109 void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
110 auto ExplicitSingleVarDecl =
111 [](
const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
112 llvm::StringRef ID) {
114 unless(isInTemplateInstantiation()),
116 varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
118 auto ExplicitSingleVarDeclInTemplate =
119 [](
const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
120 llvm::StringRef ID) {
122 isInTemplateInstantiation(),
124 varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
127 auto IsBoundToType = refersToType(equalsBoundNode(
"type"));
130 ExplicitSingleVarDecl(hasType(autoType(hasDeducedType(
131 pointerType(pointee(unless(functionType())))))),
136 ExplicitSingleVarDeclInTemplate(
137 allOf(hasType(autoType(hasDeducedType(pointerType(
138 pointee(hasUnqualifiedType(qualType().bind(
"type")),
139 unless(functionType())))))),
141 functionDecl(hasAnyTemplateArgument(IsBoundToType))),
142 hasAncestor(classTemplateSpecializationDecl(
143 hasAnyTemplateArgument(IsBoundToType))))),
146 if (!AddConstToQualified)
148 Finder->addMatcher(ExplicitSingleVarDecl(
149 hasType(pointerType(pointee(autoType()))),
"auto_ptr"),
152 ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),
157 void QualifiedAutoCheck::check(
const MatchFinder::MatchResult &Result) {
158 if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"auto")) {
159 SourceRange TypeSpecifier;
160 if (llvm::Optional<SourceRange> TypeSpec =
161 getTypeSpecifierLocation(Var, Result)) {
162 TypeSpecifier = *TypeSpec;
166 llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
167 auto CheckQualifier = [&](
bool IsPresent, Qualifier Qual) {
169 llvm::Optional<Token> Token = findQualToken(Var, Qual, Result);
170 if (!Token || Token->getLocation().isMacroID())
172 if (llvm::Optional<SourceRange> Result =
173 mergeReplacementRange(TypeSpecifier, *Token))
174 RemoveQualifiersRange.push_back(*Result);
179 bool IsLocalConst = Var->getType().isLocalConstQualified();
180 bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
181 bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
184 CheckQualifier(IsLocalVolatile, Qualifier::Volatile) ||
185 CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
189 if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))
190 TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));
192 CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier);
193 if (FixItRange.isInvalid())
196 SourceLocation FixitLoc = FixItRange.getBegin();
197 for (SourceRange &
Range : RemoveQualifiersRange) {
198 if (
Range.getBegin() < FixitLoc)
199 FixitLoc =
Range.getBegin();
202 std::string ReplStr = [&] {
203 llvm::StringRef PtrConst = isPointerConst(Var->getType()) ?
"const " :
"";
204 llvm::StringRef LocalConst = IsLocalConst ?
"const " :
"";
205 llvm::StringRef LocalVol = IsLocalVolatile ?
"volatile " :
"";
206 llvm::StringRef LocalRestrict = IsLocalRestrict ?
"__restrict " :
"";
207 return (PtrConst +
"auto *" + LocalConst + LocalVol + LocalRestrict)
211 DiagnosticBuilder Diag =
212 diag(FixitLoc,
"'%0%1%2auto %3' can be declared as '%4%3'")
213 << (IsLocalConst ?
"const " :
"")
214 << (IsLocalVolatile ?
"volatile " :
"")
215 << (IsLocalRestrict ?
"__restrict " :
"") << Var->getName() << ReplStr;
217 for (SourceRange &
Range : RemoveQualifiersRange) {
218 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
Range));
221 Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
224 if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"auto_ptr")) {
225 if (!isPointerConst(Var->getType()))
227 if (!isAutoPointerConst(Var->getType()))
231 if (Var->getType().isLocalConstQualified()) {
232 llvm::Optional<Token> Token =
234 if (!Token || Token->getLocation().isMacroID())
237 if (Var->getType().isLocalVolatileQualified()) {
238 llvm::Optional<Token> Token =
239 findQualToken(Var, Qualifier::Volatile, Result);
240 if (!Token || Token->getLocation().isMacroID())
243 if (Var->getType().isLocalRestrictQualified()) {
244 llvm::Optional<Token> Token =
245 findQualToken(Var, Qualifier::Restrict, Result);
246 if (!Token || Token->getLocation().isMacroID())
250 if (llvm::Optional<SourceRange> TypeSpec =
251 getTypeSpecifierLocation(Var, Result)) {
252 if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
253 TypeSpec->getEnd().isMacroID())
255 SourceLocation InsertPos = TypeSpec->getBegin();
256 diag(InsertPos,
"'auto *%0%1%2' can be declared as 'const auto *%0%1%2'")
257 << (Var->getType().isLocalConstQualified() ?
"const " :
"")
258 << (Var->getType().isLocalVolatileQualified() ?
"volatile " :
"")
259 << Var->getName() << FixItHint::CreateInsertion(InsertPos,
"const ");
263 if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"auto_ref")) {
264 if (!isPointerConst(Var->getType()))
266 if (!isAutoPointerConst(Var->getType()))
270 if (llvm::Optional<SourceRange> TypeSpec =
271 getTypeSpecifierLocation(Var, Result)) {
272 if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
273 TypeSpec->getEnd().isMacroID())
275 SourceLocation InsertPos = TypeSpec->getBegin();
276 diag(InsertPos,
"'auto &%0' can be declared as 'const auto &%0'")
277 << Var->getName() << FixItHint::CreateInsertion(InsertPos,
"const ");