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