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
const FunctionDecl * Decl
CodeCompletionBuilder Builder
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
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
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)