clang-tools  17.0.0git
UseTrailingReturnTypeCheck.cpp
Go to the documentation of this file.
1 //===--- UseTrailingReturnTypeCheck.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 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "clang/Tooling/FixIt.h"
15 #include "llvm/ADT/StringExtras.h"
16 
17 #include <cctype>
18 #include <optional>
19 
20 using namespace clang::ast_matchers;
21 
22 namespace clang::tidy::modernize {
23 namespace {
24 struct UnqualNameVisitor : public RecursiveASTVisitor<UnqualNameVisitor> {
25 public:
26  UnqualNameVisitor(const FunctionDecl &F) : F(F) {}
27 
28  bool Collision = false;
29 
30  bool shouldWalkTypesOfTypeLocs() const { return false; }
31 
32  bool visitUnqualName(StringRef UnqualName) {
33  // Check for collisions with function arguments.
34  for (ParmVarDecl *Param : F.parameters())
35  if (const IdentifierInfo *Ident = Param->getIdentifier())
36  if (Ident->getName() == UnqualName) {
37  Collision = true;
38  return true;
39  }
40  return false;
41  }
42 
43  bool TraverseTypeLoc(TypeLoc TL, bool Elaborated = false) {
44  if (TL.isNull())
45  return true;
46 
47  if (!Elaborated) {
48  switch (TL.getTypeLocClass()) {
49  case TypeLoc::Record:
50  if (visitUnqualName(
51  TL.getAs<RecordTypeLoc>().getTypePtr()->getDecl()->getName()))
52  return false;
53  break;
54  case TypeLoc::Enum:
55  if (visitUnqualName(
56  TL.getAs<EnumTypeLoc>().getTypePtr()->getDecl()->getName()))
57  return false;
58  break;
59  case TypeLoc::TemplateSpecialization:
60  if (visitUnqualName(TL.getAs<TemplateSpecializationTypeLoc>()
61  .getTypePtr()
62  ->getTemplateName()
63  .getAsTemplateDecl()
64  ->getName()))
65  return false;
66  break;
67  case TypeLoc::Typedef:
68  if (visitUnqualName(
69  TL.getAs<TypedefTypeLoc>().getTypePtr()->getDecl()->getName()))
70  return false;
71  break;
72  case TypeLoc::Using:
73  if (visitUnqualName(TL.getAs<UsingTypeLoc>()
74  .getTypePtr()
75  ->getFoundDecl()
76  ->getName()))
77  return false;
78  break;
79  default:
80  break;
81  }
82  }
83 
84  return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc(TL);
85  }
86 
87  // Replace the base method in order to call our own
88  // TraverseTypeLoc().
89  bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) {
90  return TraverseTypeLoc(TL.getUnqualifiedLoc());
91  }
92 
93  // Replace the base version to inform TraverseTypeLoc that the type is
94  // elaborated.
95  bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc TL) {
96  if (TL.getQualifierLoc() &&
97  !TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()))
98  return false;
99  const auto *T = TL.getTypePtr();
100  return TraverseTypeLoc(TL.getNamedTypeLoc(),
101  T->getKeyword() != ETK_None || T->getQualifier());
102  }
103 
104  bool VisitDeclRefExpr(DeclRefExpr *S) {
105  DeclarationName Name = S->getNameInfo().getName();
106  return S->getQualifierLoc() || !Name.isIdentifier() ||
107  !visitUnqualName(Name.getAsIdentifierInfo()->getName());
108  }
109 
110 private:
111  const FunctionDecl &F;
112 };
113 } // namespace
114 
115 constexpr llvm::StringLiteral Message =
116  "use a trailing return type for this function";
117 
118 static SourceLocation expandIfMacroId(SourceLocation Loc,
119  const SourceManager &SM) {
120  if (Loc.isMacroID())
121  Loc = expandIfMacroId(SM.getImmediateExpansionRange(Loc).getBegin(), SM);
122  assert(!Loc.isMacroID() &&
123  "SourceLocation must not be a macro ID after recursive expansion");
124  return Loc;
125 }
126 
127 SourceLocation UseTrailingReturnTypeCheck::findTrailingReturnTypeSourceLocation(
128  const FunctionDecl &F, const FunctionTypeLoc &FTL, const ASTContext &Ctx,
129  const SourceManager &SM, const LangOptions &LangOpts) {
130  // We start with the location of the closing parenthesis.
131  SourceRange ExceptionSpecRange = F.getExceptionSpecSourceRange();
132  if (ExceptionSpecRange.isValid())
133  return Lexer::getLocForEndOfToken(ExceptionSpecRange.getEnd(), 0, SM,
134  LangOpts);
135 
136  // If the function argument list ends inside of a macro, it is dangerous to
137  // start lexing from here - bail out.
138  SourceLocation ClosingParen = FTL.getRParenLoc();
139  if (ClosingParen.isMacroID())
140  return {};
141 
142  SourceLocation Result =
143  Lexer::getLocForEndOfToken(ClosingParen, 0, SM, LangOpts);
144 
145  // Skip subsequent CV and ref qualifiers.
146  std::pair<FileID, unsigned> Loc = SM.getDecomposedLoc(Result);
147  StringRef File = SM.getBufferData(Loc.first);
148  const char *TokenBegin = File.data() + Loc.second;
149  Lexer Lexer(SM.getLocForStartOfFile(Loc.first), LangOpts, File.begin(),
150  TokenBegin, File.end());
151  Token T;
152  while (!Lexer.LexFromRawLexer(T)) {
153  if (T.is(tok::raw_identifier)) {
154  IdentifierInfo &Info = Ctx.Idents.get(
155  StringRef(SM.getCharacterData(T.getLocation()), T.getLength()));
156  T.setIdentifierInfo(&Info);
157  T.setKind(Info.getTokenID());
158  }
159 
160  if (T.isOneOf(tok::amp, tok::ampamp, tok::kw_const, tok::kw_volatile,
161  tok::kw_restrict)) {
162  Result = T.getEndLoc();
163  continue;
164  }
165  break;
166  }
167  return Result;
168 }
169 
170 static bool isCvr(Token T) {
171  return T.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict);
172 }
173 
174 static bool isSpecifier(Token T) {
175  return T.isOneOf(tok::kw_constexpr, tok::kw_inline, tok::kw_extern,
176  tok::kw_static, tok::kw_friend, tok::kw_virtual);
177 }
178 
179 static std::optional<ClassifiedToken>
180 classifyToken(const FunctionDecl &F, Preprocessor &PP, Token Tok) {
181  ClassifiedToken CT;
182  CT.T = Tok;
183  CT.IsQualifier = true;
184  CT.IsSpecifier = true;
185  bool ContainsQualifiers = false;
186  bool ContainsSpecifiers = false;
187  bool ContainsSomethingElse = false;
188 
189  Token End;
190  End.startToken();
191  End.setKind(tok::eof);
192  SmallVector<Token, 2> Stream{Tok, End};
193 
194  // FIXME: do not report these token to Preprocessor.TokenWatcher.
195  PP.EnterTokenStream(Stream, false, /*IsReinject=*/false);
196  while (true) {
197  Token T;
198  PP.Lex(T);
199  if (T.is(tok::eof))
200  break;
201 
202  bool Qual = isCvr(T);
203  bool Spec = isSpecifier(T);
204  CT.IsQualifier &= Qual;
205  CT.IsSpecifier &= Spec;
206  ContainsQualifiers |= Qual;
207  ContainsSpecifiers |= Spec;
208  ContainsSomethingElse |= !Qual && !Spec;
209  }
210 
211  // If the Token/Macro contains more than one type of tokens, we would need
212  // to split the macro in order to move parts to the trailing return type.
213  if (ContainsQualifiers + ContainsSpecifiers + ContainsSomethingElse > 1)
214  return std::nullopt;
215 
216  return CT;
217 }
218 
219 std::optional<SmallVector<ClassifiedToken, 8>>
220 UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(
221  const FunctionDecl &F, const ASTContext &Ctx, const SourceManager &SM,
222  const LangOptions &LangOpts) {
223  SourceLocation BeginF = expandIfMacroId(F.getBeginLoc(), SM);
224  SourceLocation BeginNameF = expandIfMacroId(F.getLocation(), SM);
225 
226  // Create tokens for everything before the name of the function.
227  std::pair<FileID, unsigned> Loc = SM.getDecomposedLoc(BeginF);
228  StringRef File = SM.getBufferData(Loc.first);
229  const char *TokenBegin = File.data() + Loc.second;
230  Lexer Lexer(SM.getLocForStartOfFile(Loc.first), LangOpts, File.begin(),
231  TokenBegin, File.end());
232  Token T;
233  SmallVector<ClassifiedToken, 8> ClassifiedTokens;
234  while (!Lexer.LexFromRawLexer(T) &&
235  SM.isBeforeInTranslationUnit(T.getLocation(), BeginNameF)) {
236  if (T.is(tok::raw_identifier)) {
237  IdentifierInfo &Info = Ctx.Idents.get(
238  StringRef(SM.getCharacterData(T.getLocation()), T.getLength()));
239 
240  if (Info.hasMacroDefinition()) {
241  const MacroInfo *MI = PP->getMacroInfo(&Info);
242  if (!MI || MI->isFunctionLike()) {
243  // Cannot handle function style macros.
244  diag(F.getLocation(), Message);
245  return std::nullopt;
246  }
247  }
248 
249  T.setIdentifierInfo(&Info);
250  T.setKind(Info.getTokenID());
251  }
252 
253  if (std::optional<ClassifiedToken> CT = classifyToken(F, *PP, T))
254  ClassifiedTokens.push_back(*CT);
255  else {
256  diag(F.getLocation(), Message);
257  return std::nullopt;
258  }
259  }
260 
261  return ClassifiedTokens;
262 }
263 
264 static bool hasAnyNestedLocalQualifiers(QualType Type) {
265  bool Result = Type.hasLocalQualifiers();
266  if (Type->isPointerType())
267  Result = Result || hasAnyNestedLocalQualifiers(
268  Type->castAs<PointerType>()->getPointeeType());
269  if (Type->isReferenceType())
270  Result = Result || hasAnyNestedLocalQualifiers(
271  Type->castAs<ReferenceType>()->getPointeeType());
272  return Result;
273 }
274 
275 SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
276  const FunctionDecl &F, const TypeLoc &ReturnLoc, const ASTContext &Ctx,
277  const SourceManager &SM, const LangOptions &LangOpts) {
278 
279  // We start with the range of the return type and expand to neighboring
280  // qualifiers (const, volatile and restrict).
281  SourceRange ReturnTypeRange = F.getReturnTypeSourceRange();
282  if (ReturnTypeRange.isInvalid()) {
283  // Happens if e.g. clang cannot resolve all includes and the return type is
284  // unknown.
285  diag(F.getLocation(), Message);
286  return {};
287  }
288 
289 
290  // If the return type has no local qualifiers, it's source range is accurate.
291  if (!hasAnyNestedLocalQualifiers(F.getReturnType()))
292  return ReturnTypeRange;
293 
294  // Include qualifiers to the left and right of the return type.
295  std::optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
296  classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
297  if (!MaybeTokens)
298  return {};
299  const SmallVector<ClassifiedToken, 8> &Tokens = *MaybeTokens;
300 
301  ReturnTypeRange.setBegin(expandIfMacroId(ReturnTypeRange.getBegin(), SM));
302  ReturnTypeRange.setEnd(expandIfMacroId(ReturnTypeRange.getEnd(), SM));
303 
304  bool ExtendedLeft = false;
305  for (size_t I = 0; I < Tokens.size(); I++) {
306  // If we found the beginning of the return type, include left qualifiers.
307  if (!SM.isBeforeInTranslationUnit(Tokens[I].T.getLocation(),
308  ReturnTypeRange.getBegin()) &&
309  !ExtendedLeft) {
310  assert(I <= size_t(std::numeric_limits<int>::max()) &&
311  "Integer overflow detected");
312  for (int J = static_cast<int>(I) - 1; J >= 0 && Tokens[J].IsQualifier;
313  J--)
314  ReturnTypeRange.setBegin(Tokens[J].T.getLocation());
315  ExtendedLeft = true;
316  }
317  // If we found the end of the return type, include right qualifiers.
318  if (SM.isBeforeInTranslationUnit(ReturnTypeRange.getEnd(),
319  Tokens[I].T.getLocation())) {
320  for (size_t J = I; J < Tokens.size() && Tokens[J].IsQualifier; J++)
321  ReturnTypeRange.setEnd(Tokens[J].T.getLocation());
322  break;
323  }
324  }
325 
326  assert(!ReturnTypeRange.getBegin().isMacroID() &&
327  "Return type source range begin must not be a macro");
328  assert(!ReturnTypeRange.getEnd().isMacroID() &&
329  "Return type source range end must not be a macro");
330  return ReturnTypeRange;
331 }
332 
333 void UseTrailingReturnTypeCheck::keepSpecifiers(
334  std::string &ReturnType, std::string &Auto, SourceRange ReturnTypeCVRange,
335  const FunctionDecl &F, const FriendDecl *Fr, const ASTContext &Ctx,
336  const SourceManager &SM, const LangOptions &LangOpts) {
337  // Check if there are specifiers inside the return type. E.g. unsigned
338  // inline int.
339  const auto *M = dyn_cast<CXXMethodDecl>(&F);
340  if (!F.isConstexpr() && !F.isInlineSpecified() &&
341  F.getStorageClass() != SC_Extern && F.getStorageClass() != SC_Static &&
342  !Fr && !(M && M->isVirtualAsWritten()))
343  return;
344 
345  // Tokenize return type. If it contains macros which contain a mix of
346  // qualifiers, specifiers and types, give up.
347  std::optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
348  classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
349  if (!MaybeTokens)
350  return;
351 
352  // Find specifiers, remove them from the return type, add them to 'auto'.
353  unsigned int ReturnTypeBeginOffset =
354  SM.getDecomposedLoc(ReturnTypeCVRange.getBegin()).second;
355  size_t InitialAutoLength = Auto.size();
356  unsigned int DeletedChars = 0;
357  for (ClassifiedToken CT : *MaybeTokens) {
358  if (SM.isBeforeInTranslationUnit(CT.T.getLocation(),
359  ReturnTypeCVRange.getBegin()) ||
360  SM.isBeforeInTranslationUnit(ReturnTypeCVRange.getEnd(),
361  CT.T.getLocation()))
362  continue;
363  if (!CT.IsSpecifier)
364  continue;
365 
366  // Add the token to 'auto' and remove it from the return type, including
367  // any whitespace following the token.
368  unsigned int TOffset = SM.getDecomposedLoc(CT.T.getLocation()).second;
369  assert(TOffset >= ReturnTypeBeginOffset &&
370  "Token location must be after the beginning of the return type");
371  unsigned int TOffsetInRT = TOffset - ReturnTypeBeginOffset - DeletedChars;
372  unsigned int TLengthWithWS = CT.T.getLength();
373  while (TOffsetInRT + TLengthWithWS < ReturnType.size() &&
374  llvm::isSpace(ReturnType[TOffsetInRT + TLengthWithWS]))
375  TLengthWithWS++;
376  std::string Specifier = ReturnType.substr(TOffsetInRT, TLengthWithWS);
377  if (!llvm::isSpace(Specifier.back()))
378  Specifier.push_back(' ');
379  Auto.insert(Auto.size() - InitialAutoLength, Specifier);
380  ReturnType.erase(TOffsetInRT, TLengthWithWS);
381  DeletedChars += TLengthWithWS;
382  }
383 }
384 
385 void UseTrailingReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
386  auto F = functionDecl(
387  unless(anyOf(hasTrailingReturn(), returns(voidType()),
388  cxxConversionDecl(), cxxMethodDecl(isImplicit()))))
389  .bind("Func");
390 
391  Finder->addMatcher(F, this);
392  Finder->addMatcher(friendDecl(hasDescendant(F)).bind("Friend"), this);
393 }
394 
395 void UseTrailingReturnTypeCheck::registerPPCallbacks(
396  const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
397  this->PP = PP;
398 }
399 
400 void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
401  assert(PP && "Expected registerPPCallbacks() to have been called before so "
402  "preprocessor is available");
403 
404  const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("Func");
405  const auto *Fr = Result.Nodes.getNodeAs<FriendDecl>("Friend");
406  assert(F && "Matcher is expected to find only FunctionDecls");
407 
408  // Three-way comparison operator<=> is syntactic sugar and generates implicit
409  // nodes for all other operators.
410  if (F->getLocation().isInvalid() || F->isImplicit())
411  return;
412 
413  // Skip functions which return 'auto' and defaulted operators.
414  const auto *AT = F->getDeclaredReturnType()->getAs<AutoType>();
415  if (AT != nullptr &&
416  ((!AT->isConstrained() && AT->getKeyword() == AutoTypeKeyword::Auto &&
417  !hasAnyNestedLocalQualifiers(F->getDeclaredReturnType())) ||
418  F->isDefaulted()))
419  return;
420 
421  // TODO: implement those
422  if (F->getDeclaredReturnType()->isFunctionPointerType() ||
423  F->getDeclaredReturnType()->isMemberFunctionPointerType() ||
424  F->getDeclaredReturnType()->isMemberPointerType()) {
425  diag(F->getLocation(), Message);
426  return;
427  }
428 
429  const ASTContext &Ctx = *Result.Context;
430  const SourceManager &SM = *Result.SourceManager;
431  const LangOptions &LangOpts = getLangOpts();
432 
433  const TypeSourceInfo *TSI = F->getTypeSourceInfo();
434  if (!TSI)
435  return;
436 
437  FunctionTypeLoc FTL =
438  TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
439  if (!FTL) {
440  // FIXME: This may happen if we have __attribute__((...)) on the function.
441  // We abort for now. Remove this when the function type location gets
442  // available in clang.
443  diag(F->getLocation(), Message);
444  return;
445  }
446 
447  SourceLocation InsertionLoc =
448  findTrailingReturnTypeSourceLocation(*F, FTL, Ctx, SM, LangOpts);
449  if (InsertionLoc.isInvalid()) {
450  diag(F->getLocation(), Message);
451  return;
452  }
453 
454  // Using the declared return type via F->getDeclaredReturnType().getAsString()
455  // discards user formatting and order of const, volatile, type, whitespace,
456  // space before & ... .
457  SourceRange ReturnTypeCVRange =
458  findReturnTypeAndCVSourceRange(*F, FTL.getReturnLoc(), Ctx, SM, LangOpts);
459  if (ReturnTypeCVRange.isInvalid())
460  return;
461 
462  // Check if unqualified names in the return type conflict with other entities
463  // after the rewrite.
464  // FIXME: this could be done better, by performing a lookup of all
465  // unqualified names in the return type in the scope of the function. If the
466  // lookup finds a different entity than the original entity identified by the
467  // name, then we can either not perform a rewrite or explicitly qualify the
468  // entity. Such entities could be function parameter names, (inherited) class
469  // members, template parameters, etc.
470  UnqualNameVisitor UNV{*F};
471  UNV.TraverseTypeLoc(FTL.getReturnLoc());
472  if (UNV.Collision) {
473  diag(F->getLocation(), Message);
474  return;
475  }
476 
477  SourceLocation ReturnTypeEnd =
478  Lexer::getLocForEndOfToken(ReturnTypeCVRange.getEnd(), 0, SM, LangOpts);
479  StringRef CharAfterReturnType = Lexer::getSourceText(
480  CharSourceRange::getCharRange(ReturnTypeEnd,
481  ReturnTypeEnd.getLocWithOffset(1)),
482  SM, LangOpts);
483  bool NeedSpaceAfterAuto =
484  CharAfterReturnType.empty() || !llvm::isSpace(CharAfterReturnType[0]);
485 
486  std::string Auto = NeedSpaceAfterAuto ? "auto " : "auto";
487  std::string ReturnType =
488  std::string(tooling::fixit::getText(ReturnTypeCVRange, Ctx));
489  keepSpecifiers(ReturnType, Auto, ReturnTypeCVRange, *F, Fr, Ctx, SM,
490  LangOpts);
491 
492  diag(F->getLocation(), Message)
493  << FixItHint::CreateReplacement(ReturnTypeCVRange, Auto)
494  << FixItHint::CreateInsertion(InsertionLoc, " -> " + ReturnType);
495 }
496 
497 } // namespace clang::tidy::modernize
clang::tidy::modernize::ClassifiedToken
Definition: UseTrailingReturnTypeCheck.h:18
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:43
clang::tidy::modernize::classifyToken
static std::optional< ClassifiedToken > classifyToken(const FunctionDecl &F, Preprocessor &PP, Token Tok)
Definition: UseTrailingReturnTypeCheck.cpp:180
clang::tidy::modernize::expandIfMacroId
static SourceLocation expandIfMacroId(SourceLocation Loc, const SourceManager &SM)
Definition: UseTrailingReturnTypeCheck.cpp:118
Type
NodeType Type
Definition: HTMLGenerator.cpp:75
Collision
bool Collision
Definition: UseTrailingReturnTypeCheck.cpp:28
clang::tidy::modernize::ClassifiedToken::IsSpecifier
bool IsSpecifier
Definition: UseTrailingReturnTypeCheck.h:21
clang::clangd::WantDiagnostics::Auto
@ Auto
Diagnostics must not be generated for this snapshot.
clang::tidy::modernize::Message
constexpr llvm::StringLiteral Message
Definition: UseTrailingReturnTypeCheck.cpp:115
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:115
Ctx
Context Ctx
Definition: TUScheduler.cpp:552
clang::tidy::modernize::getText
static StringRef getText(const Token &Tok, const SourceManager &Sources)
Definition: UseOverrideCheck.cpp:76
clang::ast_matchers
Definition: AbseilMatcher.h:13
clang::tidy::modernize
Definition: AvoidBindCheck.cpp:32
M
const google::protobuf::Message & M
Definition: Server.cpp:309
clang::tidy::modernize::ClassifiedToken::T
Token T
Definition: UseTrailingReturnTypeCheck.h:19
UseTrailingReturnTypeCheck.h
clang::tidy::modernize::ClassifiedToken::IsQualifier
bool IsQualifier
Definition: UseTrailingReturnTypeCheck.h:20
clang::tidy::modernize::isCvr
static bool isCvr(Token T)
Definition: UseTrailingReturnTypeCheck.cpp:170
Name
Token Name
Definition: MacroToEnumCheck.cpp:87
clang::tidy::modernize::isSpecifier
static bool isSpecifier(Token T)
Definition: UseTrailingReturnTypeCheck.cpp:174
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:28
Info
FunctionInfo Info
Definition: FunctionSizeCheck.cpp:119
clang::clangd::check
bool check(llvm::StringRef File, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
Definition: Check.cpp:418
LangOpts
const LangOptions * LangOpts
Definition: ExtractFunction.cpp:374
clang::doc::Record
llvm::SmallVector< uint64_t, 1024 > Record
Definition: BitcodeReader.cpp:18
clang::tidy::modernize::hasAnyNestedLocalQualifiers
static bool hasAnyNestedLocalQualifiers(QualType Type)
Definition: UseTrailingReturnTypeCheck.cpp:264
ReturnType
std::string ReturnType
Definition: CodeComplete.cpp:486