clang-tools  14.0.0git
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) &&
39  "Invalid Qualifier");
40 
41  SourceLocation BeginLoc = Decl->getQualifierLoc().getBeginLoc();
42  if (BeginLoc.isInvalid())
43  BeginLoc = Decl->getBeginLoc();
44  SourceLocation EndLoc = Decl->getLocation();
45 
46  CharSourceRange FileRange = Lexer::makeFileCharRange(
47  CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,
48  Result.Context->getLangOpts());
49 
50  if (FileRange.isInvalid())
51  return llvm::None;
52 
53  tok::TokenKind Tok =
54  Qual == Qualifier::Const
55  ? tok::kw_const
56  : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
57 
58  return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context,
59  *Result.SourceManager);
60 }
61 
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())));
70 
71  if (TypeSpecifier.getBegin().isMacroID() ||
72  TypeSpecifier.getEnd().isMacroID())
73  return llvm::None;
74  return TypeSpecifier;
75 }
76 
77 llvm::Optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
78  const Token &ConstToken) {
79  if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
80  TypeSpecifier.setBegin(ConstToken.getLocation());
81  return llvm::None;
82  }
83  if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
84  TypeSpecifier.setEnd(ConstToken.getEndLoc());
85  return llvm::None;
86  }
87  return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
88 }
89 
90 bool isPointerConst(QualType QType) {
91  QualType Pointee = QType->getPointeeType();
92  assert(!Pointee.isNull() && "can't have a null Pointee");
93  return Pointee.isConstQualified();
94 }
95 
96 bool isAutoPointerConst(QualType QType) {
97  QualType Pointee =
98  cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
99  assert(!Pointee.isNull() && "can't have a null Pointee");
100  return Pointee.isConstQualified();
101 }
102 
103 } // namespace
104 
105 void QualifiedAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
106  Options.store(Opts, "AddConstToQualified", AddConstToQualified);
107 }
108 
109 void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
110  auto ExplicitSingleVarDecl =
111  [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
112  llvm::StringRef ID) {
113  return declStmt(
114  unless(isInTemplateInstantiation()),
115  hasSingleDecl(
116  varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
117  };
118  auto ExplicitSingleVarDeclInTemplate =
119  [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
120  llvm::StringRef ID) {
121  return declStmt(
122  isInTemplateInstantiation(),
123  hasSingleDecl(
124  varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
125  };
126 
127  auto IsBoundToType = refersToType(equalsBoundNode("type"));
128  auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));
129  auto IsAutoDeducedToPointer = [](const auto &...InnerMatchers) {
130  return autoType(hasDeducedType(
131  hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...)))));
132  };
133 
134  Finder->addMatcher(
135  ExplicitSingleVarDecl(hasType(IsAutoDeducedToPointer(UnlessFunctionType)),
136  "auto"),
137  this);
138 
139  Finder->addMatcher(
140  ExplicitSingleVarDeclInTemplate(
141  allOf(hasType(IsAutoDeducedToPointer(
142  hasUnqualifiedType(qualType().bind("type")),
143  UnlessFunctionType)),
144  anyOf(hasAncestor(
145  functionDecl(hasAnyTemplateArgument(IsBoundToType))),
146  hasAncestor(classTemplateSpecializationDecl(
147  hasAnyTemplateArgument(IsBoundToType))))),
148  "auto"),
149  this);
150  if (!AddConstToQualified)
151  return;
152  Finder->addMatcher(ExplicitSingleVarDecl(
153  hasType(pointerType(pointee(autoType()))), "auto_ptr"),
154  this);
155  Finder->addMatcher(
156  ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),
157  "auto_ref"),
158  this);
159 }
160 
161 void QualifiedAutoCheck::check(const MatchFinder::MatchResult &Result) {
162  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto")) {
163  SourceRange TypeSpecifier;
164  if (llvm::Optional<SourceRange> TypeSpec =
165  getTypeSpecifierLocation(Var, Result)) {
166  TypeSpecifier = *TypeSpec;
167  } else
168  return;
169 
170  llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
171  auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) {
172  if (IsPresent) {
173  llvm::Optional<Token> Token = findQualToken(Var, Qual, Result);
174  if (!Token || Token->getLocation().isMacroID())
175  return true; // Disregard this VarDecl.
176  if (llvm::Optional<SourceRange> Result =
177  mergeReplacementRange(TypeSpecifier, *Token))
178  RemoveQualifiersRange.push_back(*Result);
179  }
180  return false;
181  };
182 
183  bool IsLocalConst = Var->getType().isLocalConstQualified();
184  bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
185  bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
186 
187  if (CheckQualifier(IsLocalConst, Qualifier::Const) ||
188  CheckQualifier(IsLocalVolatile, Qualifier::Volatile) ||
189  CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
190  return;
191 
192  // Check for bridging the gap between the asterisk and name.
193  if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))
194  TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));
195 
196  CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier);
197  if (FixItRange.isInvalid())
198  return;
199 
200  SourceLocation FixitLoc = FixItRange.getBegin();
201  for (SourceRange &Range : RemoveQualifiersRange) {
202  if (Range.getBegin() < FixitLoc)
203  FixitLoc = Range.getBegin();
204  }
205 
206  std::string ReplStr = [&] {
207  llvm::StringRef PtrConst = isPointerConst(Var->getType()) ? "const " : "";
208  llvm::StringRef LocalConst = IsLocalConst ? "const " : "";
209  llvm::StringRef LocalVol = IsLocalVolatile ? "volatile " : "";
210  llvm::StringRef LocalRestrict = IsLocalRestrict ? "__restrict " : "";
211  return (PtrConst + "auto *" + LocalConst + LocalVol + LocalRestrict)
212  .str();
213  }();
214 
215  DiagnosticBuilder Diag =
216  diag(FixitLoc,
217  "'%select{|const }0%select{|volatile }1%select{|__restrict }2auto "
218  "%3' can be declared as '%4%3'")
219  << IsLocalConst << IsLocalVolatile << IsLocalRestrict << Var->getName()
220  << ReplStr;
221 
222  for (SourceRange &Range : RemoveQualifiersRange) {
223  Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(Range));
224  }
225 
226  Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
227  return;
228  }
229  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ptr")) {
230  if (!isPointerConst(Var->getType()))
231  return; // Pointer isn't const, no need to add const qualifier.
232  if (!isAutoPointerConst(Var->getType()))
233  return; // Const isn't wrapped in the auto type, so must be declared
234  // explicitly.
235 
236  if (Var->getType().isLocalConstQualified()) {
237  llvm::Optional<Token> Token =
238  findQualToken(Var, Qualifier::Const, Result);
239  if (!Token || Token->getLocation().isMacroID())
240  return;
241  }
242  if (Var->getType().isLocalVolatileQualified()) {
243  llvm::Optional<Token> Token =
244  findQualToken(Var, Qualifier::Volatile, Result);
245  if (!Token || Token->getLocation().isMacroID())
246  return;
247  }
248  if (Var->getType().isLocalRestrictQualified()) {
249  llvm::Optional<Token> Token =
250  findQualToken(Var, Qualifier::Restrict, Result);
251  if (!Token || Token->getLocation().isMacroID())
252  return;
253  }
254 
255  if (llvm::Optional<SourceRange> TypeSpec =
256  getTypeSpecifierLocation(Var, Result)) {
257  if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
258  TypeSpec->getEnd().isMacroID())
259  return;
260  SourceLocation InsertPos = TypeSpec->getBegin();
261  diag(InsertPos,
262  "'auto *%select{|const }0%select{|volatile }1%2' can be declared as "
263  "'const auto *%select{|const }0%select{|volatile }1%2'")
264  << Var->getType().isLocalConstQualified()
265  << Var->getType().isLocalVolatileQualified() << Var->getName()
266  << FixItHint::CreateInsertion(InsertPos, "const ");
267  }
268  return;
269  }
270  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ref")) {
271  if (!isPointerConst(Var->getType()))
272  return; // Pointer isn't const, no need to add const qualifier.
273  if (!isAutoPointerConst(Var->getType()))
274  // Const isn't wrapped in the auto type, so must be declared explicitly.
275  return;
276 
277  if (llvm::Optional<SourceRange> TypeSpec =
278  getTypeSpecifierLocation(Var, Result)) {
279  if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
280  TypeSpec->getEnd().isMacroID())
281  return;
282  SourceLocation InsertPos = TypeSpec->getBegin();
283  diag(InsertPos, "'auto &%0' can be declared as 'const auto &%0'")
284  << Var->getName() << FixItHint::CreateInsertion(InsertPos, "const ");
285  }
286  return;
287  }
288 }
289 
290 } // namespace readability
291 } // namespace tidy
292 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
clang::tidy::utils::fixit::QualifierTarget::Pointee
@ Pointee
QualifiedAutoCheck.h
clang::ast_matchers
Definition: AbseilMatcher.h:14
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:258
clang::tidy::readability::Const
@ Const
Definition: MakeMemberFunctionConstCheck.cpp:52
ID
static char ID
Definition: Logger.cpp:74
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
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:114
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
Definition: EasilySwappableParametersCheck.cpp:1888