clang-tools 10.0.0

QualifiedAutoCheck.cpp
Go to the documentation of this file.
1 //===--- QualifiedAutoCheck.cpp - clang-tidy ------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "QualifiedAutoCheck.h"
10 #include "../utils/LexerUtils.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "llvm/ADT/Optional.h"
13 #include "llvm/ADT/SmallVector.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace readability {
20
21 namespace {
22
23 // FIXME move to ASTMatchers
24 AST_MATCHER_P(QualType, hasUnqualifiedType,
25 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
26 return InnerMatcher.matches(Node.getUnqualifiedType(), Finder, Builder);
27 }
28
29 enum class Qualifier { Const, Volatile, Restrict };
30
31 llvm::Optional<Token> findQualToken(const VarDecl *Decl, Qualifier Qual,
32 const MatchFinder::MatchResult &Result) {
33 // Since either of the locs can be in a macro, use `makeFileCharRange` to be
34 // sure that we have a consistent `CharSourceRange`, located entirely in the
35 // source file.
36
37 assert(Qual == Qualifier::Const || Qual == Qualifier::Volatile ||
38 Qual == Qualifier::Restrict && "Invalid Qualifier");
39
40 SourceLocation BeginLoc = Decl->getQualifierLoc().getBeginLoc();
41 if (BeginLoc.isInvalid())
42 BeginLoc = Decl->getBeginLoc();
43 SourceLocation EndLoc = Decl->getLocation();
44
45 CharSourceRange FileRange = Lexer::makeFileCharRange(
46 CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,
47 Result.Context->getLangOpts());
48
49 if (FileRange.isInvalid())
50 return llvm::None;
51
52 tok::TokenKind Tok =
53 Qual == Qualifier::Const
54 ? tok::kw_const
55 : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
56
57 return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context,
58 *Result.SourceManager);
59 }
60
61 llvm::Optional<SourceRange>
62 getTypeSpecifierLocation(const VarDecl *Var,
63 const MatchFinder::MatchResult &Result) {
64 SourceRange TypeSpecifier(
65 Var->getTypeSpecStartLoc(),
66 Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength(
67 Var->getTypeSpecEndLoc(), *Result.SourceManager,
68 Result.Context->getLangOpts())));
69
70 if (TypeSpecifier.getBegin().isMacroID() ||
71 TypeSpecifier.getEnd().isMacroID())
72 return llvm::None;
73 return TypeSpecifier;
74 }
75
76 llvm::Optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
77 const Token &ConstToken) {
78 if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
79 TypeSpecifier.setBegin(ConstToken.getLocation());
80 return llvm::None;
81 }
82 if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
83 TypeSpecifier.setEnd(ConstToken.getEndLoc());
84 return llvm::None;
85 }
86 return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
87 }
88
89 bool isPointerConst(QualType QType) {
90 QualType Pointee = QType->getPointeeType();
91 assert(!Pointee.isNull() && "can't have a null Pointee");
92 return Pointee.isConstQualified();
93 }
94
95 bool isAutoPointerConst(QualType QType) {
96 QualType Pointee =
97 cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
98 assert(!Pointee.isNull() && "can't have a null Pointee");
99 return Pointee.isConstQualified();
100 }
101
102 } // namespace
103
104 void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
105 if (!getLangOpts().CPlusPlus11)
106 return; // Auto deduction not used in 'C or C++03 and earlier', so don't
107 // register Matchers.
108 auto ExplicitSingleVarDecl =
109 [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
110 llvm::StringRef ID) {
111 return declStmt(
112 unless(isInTemplateInstantiation()),
113 hasSingleDecl(
114 varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
115 };
116 auto ExplicitSingleVarDeclInTemplate =
117 [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
118 llvm::StringRef ID) {
119 return declStmt(
120 isInTemplateInstantiation(),
121 hasSingleDecl(
122 varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
123 };
124
125 auto IsBoundToType = refersToType(equalsBoundNode("type"));
126
127 Finder->addMatcher(
128 ExplicitSingleVarDecl(hasType(autoType(hasDeducedType(
129 pointerType(pointee(unless(functionType())))))),
130 "auto"),
131 this);
132
133 Finder->addMatcher(
134 ExplicitSingleVarDeclInTemplate(
135 allOf(hasType(autoType(hasDeducedType(pointerType(
136 pointee(hasUnqualifiedType(qualType().bind("type")),
137 unless(functionType())))))),
138 anyOf(hasAncestor(
139 functionDecl(hasAnyTemplateArgument(IsBoundToType))),
140 hasAncestor(classTemplateSpecializationDecl(
141 hasAnyTemplateArgument(IsBoundToType))))),
142 "auto"),
143 this);
144 Finder->addMatcher(ExplicitSingleVarDecl(
145 hasType(pointerType(pointee(autoType()))), "auto_ptr"),
146 this);
147 Finder->addMatcher(
148 ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),
149 "auto_ref"),
150 this);
151 }
152
153 void QualifiedAutoCheck::check(const MatchFinder::MatchResult &Result) {
154 if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto")) {
155 SourceRange TypeSpecifier;
156 if (llvm::Optional<SourceRange> TypeSpec =
157 getTypeSpecifierLocation(Var, Result)) {
158 TypeSpecifier = *TypeSpec;
159 } else
160 return;
161
162 llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
163 auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) {
164 if (IsPresent) {
165 llvm::Optional<Token> Token = findQualToken(Var, Qual, Result);
166 if (!Token || Token->getLocation().isMacroID())
167 return true; // Disregard this VarDecl.
168 if (llvm::Optional<SourceRange> Result =
169 mergeReplacementRange(TypeSpecifier, *Token))
170 RemoveQualifiersRange.push_back(*Result);
171 }
172 return false;
173 };
174
175 bool IsLocalConst = Var->getType().isLocalConstQualified();
176 bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
177 bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
178
179 if (CheckQualifier(IsLocalConst, Qualifier::Const))
180 return;
181 if (CheckQualifier(IsLocalVolatile, Qualifier::Volatile))
182 return;
183 if (CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
184 return;
185
186 // Check for bridging the gap between the asterisk and name.
187 if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))
188 TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));
189
190 CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier);
191 if (FixItRange.isInvalid())
192 return;
193
194 SourceLocation FixitLoc = FixItRange.getBegin();
195 for (SourceRange &Range : RemoveQualifiersRange) {
196 if (Range.getBegin() < FixitLoc)
197 FixitLoc = Range.getBegin();
198 }
199
200 std::string ReplStr = [&] {
201 llvm::StringRef PtrConst = isPointerConst(Var->getType()) ? "const " : "";
202 llvm::StringRef LocalConst = IsLocalConst ? "const " : "";
203 llvm::StringRef LocalVol = IsLocalVolatile ? "volatile " : "";
204 llvm::StringRef LocalRestrict = IsLocalRestrict ? "__restrict " : "";
205 return (PtrConst + "auto *" + LocalConst + LocalVol + LocalRestrict)
206 .str();
207 }();
208
209 DiagnosticBuilder Diag =
210 diag(FixitLoc, "'%0%1%2auto %3' can be declared as '%4%3'")
211 << (IsLocalConst ? "const " : "")
212 << (IsLocalVolatile ? "volatile " : "")
213 << (IsLocalRestrict ? "__restrict " : "") << Var->getName() << ReplStr;
214
215 for (SourceRange &Range : RemoveQualifiersRange) {
216 Diag << FixItHint::CreateRemoval(
217 CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()));
218 }
219
220 Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
221 return;
222 }
223 if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ptr")) {
224 if (!isPointerConst(Var->getType()))
225 return; // Pointer isn't const, no need to add const qualifier.
226 if (!isAutoPointerConst(Var->getType()))
227 return; // Const isnt wrapped in the auto type, so must be declared
228 // explicitly.
229
230 if (Var->getType().isLocalConstQualified()) {
231 llvm::Optional<Token> Token =
232 findQualToken(Var, Qualifier::Const, Result);
233 if (!Token || Token->getLocation().isMacroID())
234 return;
235 }
236 if (Var->getType().isLocalVolatileQualified()) {
237 llvm::Optional<Token> Token =
238 findQualToken(Var, Qualifier::Volatile, Result);
239 if (!Token || Token->getLocation().isMacroID())
240 return;
241 }
242 if (Var->getType().isLocalRestrictQualified()) {
243 llvm::Optional<Token> Token =
244 findQualToken(Var, Qualifier::Restrict, Result);
245 if (!Token || Token->getLocation().isMacroID())
246 return;
247 }
248
249 CharSourceRange FixItRange;
250 if (llvm::Optional<SourceRange> TypeSpec =
251 getTypeSpecifierLocation(Var, Result)) {
252 FixItRange = CharSourceRange::getCharRange(*TypeSpec);
253 if (FixItRange.isInvalid())
254 return;
255 } else
256 return;
257
258 DiagnosticBuilder Diag =
259 diag(FixItRange.getBegin(),
260 "'auto *%0%1%2' can be declared as 'const auto *%0%1%2'")
261 << (Var->getType().isLocalConstQualified() ? "const " : "")
262 << (Var->getType().isLocalVolatileQualified() ? "volatile " : "")
263 << Var->getName();
264 Diag << FixItHint::CreateReplacement(FixItRange, "const auto *");
265 return;
266 }
267 if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ref")) {
268 if (!isPointerConst(Var->getType()))
269 return; // Pointer isn't const, no need to add const qualifier.
270 if (!isAutoPointerConst(Var->getType()))
271 // Const isnt wrapped in the auto type, so must be declared explicitly.
272 return;
273
274 CharSourceRange FixItRange;
275 if (llvm::Optional<SourceRange> TypeSpec =
276 getTypeSpecifierLocation(Var, Result)) {
277 FixItRange = CharSourceRange::getCharRange(*TypeSpec);
278 if (FixItRange.isInvalid())
279 return;
280 } else
281 return;
282
283 DiagnosticBuilder Diag =
284 diag(FixItRange.getBegin(),
285 "'auto &%0' can be declared as 'const auto &%0'")
286 << Var->getName();
287 Diag << FixItHint::CreateReplacement(FixItRange, "const auto &");
288 return;
289 }
290 }
291
292 } // namespace readability
293 } // namespace tidy
294 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:93
QualifiedAutoCheck.h
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:35
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::readability::Const
Definition: MakeMemberFunctionConstCheck.cpp:50
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::utils::lexer::getQualifyingToken
llvm::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...
Definition: LexerUtils.cpp:112
clang::tidy::readability::@556::Qualifier
Qualifier
Definition: QualifiedAutoCheck.cpp:29
clang::tidy::readability::AST_MATCHER_P
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
Definition: ConvertMemberFunctionsToStatic.cpp:52