1//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
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/// \file
10/// Methods for finding all instances of a USR. Our strategy is very
11/// simple; we just compare the USR at every relevant AST node with the one
12/// provided.
13///
14//===----------------------------------------------------------------------===//
15
16#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
17#include "clang/AST/ASTContext.h"
18#include "clang/AST/RecursiveASTVisitor.h"
19#include "clang/Basic/LLVM.h"
20#include "clang/Basic/SourceLocation.h"
21#include "clang/Basic/SourceManager.h"
22#include "clang/Lex/Lexer.h"
23#include "clang/Tooling/Core/Lookup.h"
24#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
25#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
26#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
27#include "llvm/ADT/StringRef.h"
28#include "llvm/Support/Casting.h"
29#include <cstddef>
30#include <set>
31#include <string>
32#include <vector>
33
34using namespace llvm;
35
36namespace clang {
37namespace tooling {
38
39namespace {
40
41// Returns true if the given Loc is valid for edit. We don't edit the
42// SourceLocations that are valid or in temporary buffer.
43bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) {
44  if (Loc.isInvalid())
45    return false;
46  const clang::FullSourceLoc FullLoc(Loc, SM);
47  std::pair<clang::FileID, unsigned> FileIdAndOffset =
48      FullLoc.getSpellingLoc().getDecomposedLoc();
49  return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr;
50}
51
52// This visitor recursively searches for all instances of a USR in a
53// translation unit and stores them for later usage.
54class USRLocFindingASTVisitor
55    : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> {
56public:
57  explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
58                                   StringRef PrevName,
59                                   const ASTContext &Context)
60      : RecursiveSymbolVisitor(Context.getSourceManager(),
61                               Context.getLangOpts()),
62        USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
63  }
64
65  bool visitSymbolOccurrence(const NamedDecl *ND,
66                             ArrayRef<SourceRange> NameRanges) {
67    if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
68      assert(NameRanges.size() == 1 &&
69             "Multiple name pieces are not supported yet!");
70      SourceLocation Loc = NameRanges[0].getBegin();
71      const SourceManager &SM = Context.getSourceManager();
72      // TODO: Deal with macro occurrences correctly.
73      if (Loc.isMacroID())
74        Loc = SM.getSpellingLoc(Loc);
75      checkAndAddLocation(Loc);
76    }
77    return true;
78  }
79
80  // Non-visitors:
81
82  /// Returns a set of unique symbol occurrences. Duplicate or
83  /// overlapping occurrences are erroneous and should be reported!
84  SymbolOccurrences takeOccurrences() { return std::move(Occurrences); }
85
86private:
87  void checkAndAddLocation(SourceLocation Loc) {
88    const SourceLocation BeginLoc = Loc;
89    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
90        BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
91    StringRef TokenName =
92        Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
93                             Context.getSourceManager(), Context.getLangOpts());
94    size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
95
96    // The token of the source location we find actually has the old
97    // name.
98    if (Offset != StringRef::npos)
99      Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol,
100                               BeginLoc.getLocWithOffset(Offset));
101  }
102
103  const std::set<std::string> USRSet;
104  const SymbolName PrevName;
105  SymbolOccurrences Occurrences;
106  const ASTContext &Context;
107};
108
109SourceLocation StartLocationForType(TypeLoc TL) {
110  // For elaborated types (e.g. `struct a::A`) we want the portion after the
111  // `struct` but including the namespace qualifier, `a::`.
112  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
113    NestedNameSpecifierLoc NestedNameSpecifier =
114        ElaboratedTypeLoc.getQualifierLoc();
115    if (NestedNameSpecifier.getNestedNameSpecifier())
116      return NestedNameSpecifier.getBeginLoc();
117    TL = TL.getNextTypeLoc();
118  }
119  return TL.getBeginLoc();
120}
121
122SourceLocation EndLocationForType(TypeLoc TL) {
123  // Dig past any namespace or keyword qualifications.
124  while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
125         TL.getTypeLocClass() == TypeLoc::Qualified)
126    TL = TL.getNextTypeLoc();
127
128  // The location for template specializations (e.g. Foo<int>) includes the
129  // templated types in its location range.  We want to restrict this to just
130  // before the `<` character.
131  if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
132    return TL.castAs<TemplateSpecializationTypeLoc>()
133        .getLAngleLoc()
134        .getLocWithOffset(-1);
135  }
136  return TL.getEndLoc();
137}
138
139NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
140  // Dig past any keyword qualifications.
141  while (TL.getTypeLocClass() == TypeLoc::Qualified)
142    TL = TL.getNextTypeLoc();
143
144  // For elaborated types (e.g. `struct a::A`) we want the portion after the
145  // `struct` but including the namespace qualifier, `a::`.
146  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
147    return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
148  return nullptr;
149}
150
151// Find all locations identified by the given USRs for rename.
152//
153// This class will traverse the AST and find every AST node whose USR is in the
154// given USRs' set.
155class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
156public:
157  RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
158      : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
159
160  // A structure records all information of a symbol reference being renamed.
161  // We try to add as few prefix qualifiers as possible.
162  struct RenameInfo {
163    // The begin location of a symbol being renamed.
164    SourceLocation Begin;
165    // The end location of a symbol being renamed.
166    SourceLocation End;
167    // The declaration of a symbol being renamed (can be nullptr).
168    const NamedDecl *FromDecl;
169    // The declaration in which the nested name is contained (can be nullptr).
170    const Decl *Context;
171    // The nested name being replaced (can be nullptr).
172    const NestedNameSpecifier *Specifier;
173    // Determine whether the prefix qualifiers of the NewName should be ignored.
174    // Normally, we set it to true for the symbol declaration and definition to
175    // avoid adding prefix qualifiers.
176    // For example, if it is true and NewName is "a::b::foo", then the symbol
177    // occurrence which the RenameInfo points to will be renamed to "foo".
178    bool IgnorePrefixQualifers;
179  };
180
181  bool VisitNamedDecl(const NamedDecl *Decl) {
182    // UsingDecl has been handled in other place.
183    if (llvm::isa<UsingDecl>(Decl))
184      return true;
185
186    // DestructorDecl has been handled in Typeloc.
187    if (llvm::isa<CXXDestructorDecl>(Decl))
188      return true;
189
190    if (Decl->isImplicit())
191      return true;
192
193    if (isInUSRSet(Decl)) {
194      // For the case of renaming an alias template, we actually rename the
195      // underlying alias declaration of the template.
196      if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl))
197        Decl = TAT->getTemplatedDecl();
198
199      auto StartLoc = Decl->getLocation();
200      auto EndLoc = StartLoc;
201      if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
202        RenameInfo Info = {StartLoc,
203                           EndLoc,
204                           /*FromDecl=*/nullptr,
205                           /*Context=*/nullptr,
206                           /*Specifier=*/nullptr,
207                           /*IgnorePrefixQualifers=*/true};
208        RenameInfos.push_back(Info);
209      }
210    }
211    return true;
212  }
213
214  bool VisitMemberExpr(const MemberExpr *Expr) {
215    const NamedDecl *Decl = Expr->getFoundDecl();
216    auto StartLoc = Expr->getMemberLoc();
217    auto EndLoc = Expr->getMemberLoc();
218    if (isInUSRSet(Decl)) {
219      RenameInfos.push_back({StartLoc, EndLoc,
220                            /*FromDecl=*/nullptr,
221                            /*Context=*/nullptr,
222                            /*Specifier=*/nullptr,
223                            /*IgnorePrefixQualifiers=*/true});
224    }
225    return true;
226  }
227
228  bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
229    // Fix the constructor initializer when renaming class members.
230    for (const auto *Initializer : CD->inits()) {
231      // Ignore implicit initializers.
232      if (!Initializer->isWritten())
233        continue;
234
235      if (const FieldDecl *FD = Initializer->getMember()) {
236        if (isInUSRSet(FD)) {
237          auto Loc = Initializer->getSourceLocation();
238          RenameInfos.push_back({Loc, Loc,
239                                 /*FromDecl=*/nullptr,
240                                 /*Context=*/nullptr,
241                                 /*Specifier=*/nullptr,
242                                 /*IgnorePrefixQualifiers=*/true});
243        }
244      }
245    }
246    return true;
247  }
248
249  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
250    const NamedDecl *Decl = Expr->getFoundDecl();
251    // Get the underlying declaration of the shadow declaration introduced by a
252    // using declaration.
253    if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
254      Decl = UsingShadow->getTargetDecl();
255    }
256
257    auto StartLoc = Expr->getBeginLoc();
258    // For template function call expressions like `foo<int>()`, we want to
259    // restrict the end of location to just before the `<` character.
260    SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
261                                ? Expr->getLAngleLoc().getLocWithOffset(-1)
262                                : Expr->getEndLoc();
263
264    if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
265      if (isInUSRSet(MD)) {
266        // Handle renaming static template class methods, we only rename the
267        // name without prefix qualifiers and restrict the source range to the
268        // name.
269        RenameInfos.push_back({EndLoc, EndLoc,
270                               /*FromDecl=*/nullptr,
271                               /*Context=*/nullptr,
272                               /*Specifier=*/nullptr,
273                               /*IgnorePrefixQualifiers=*/true});
274        return true;
275      }
276    }
277
278    // In case of renaming an enum declaration, we have to explicitly handle
279    // unscoped enum constants referenced in expressions (e.g.
280    // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped
281    // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by
282    // TypeLoc.
283    if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) {
284      // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`)
285      // when renaming an unscoped enum declaration with a new namespace.
286      if (!Expr->hasQualifier())
287        return true;
288
289      if (const auto *ED =
290              llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) {
291        if (ED->isScoped())
292          return true;
293        Decl = ED;
294      }
295      // The current fix would qualify "ns1::ns2::Green" as
296      // "ns1::ns2::Color::Green".
297      //
298      // Get the EndLoc of the replacement by moving 1 character backward (
299      // to exclude the last '::').
300      //
301      //    ns1::ns2::Green;
302      //    ^      ^^
303      // BeginLoc  |EndLoc of the qualifier
304      //           new EndLoc
305      EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
306      assert(EndLoc.isValid() &&
307             "The enum constant should have prefix qualifers.");
308    }
309    if (isInUSRSet(Decl) &&
310        IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
311      RenameInfo Info = {StartLoc,
312                         EndLoc,
313                         Decl,
314                         getClosestAncestorDecl(*Expr),
315                         Expr->getQualifier(),
316                         /*IgnorePrefixQualifers=*/false};
317      RenameInfos.push_back(Info);
318    }
319
320    return true;
321  }
322
323  bool VisitUsingDecl(const UsingDecl *Using) {
324    for (const auto *UsingShadow : Using->shadows()) {
325      if (isInUSRSet(UsingShadow->getTargetDecl())) {
326        UsingDecls.push_back(Using);
327        break;
328      }
329    }
330    return true;
331  }
332
333  bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
334    if (!NestedLoc.getNestedNameSpecifier()->getAsType())
335      return true;
336
337    if (const auto *TargetDecl =
338            getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
339      if (isInUSRSet(TargetDecl)) {
340        RenameInfo Info = {NestedLoc.getBeginLoc(),
341                           EndLocationForType(NestedLoc.getTypeLoc()),
342                           TargetDecl,
343                           getClosestAncestorDecl(NestedLoc),
344                           NestedLoc.getNestedNameSpecifier()->getPrefix(),
345                           /*IgnorePrefixQualifers=*/false};
346        RenameInfos.push_back(Info);
347      }
348    }
349    return true;
350  }
351
352  bool VisitTypeLoc(TypeLoc Loc) {
353    auto Parents = Context.getParents(Loc);
354    TypeLoc ParentTypeLoc;
355    if (!Parents.empty()) {
356      // Handle cases of nested name specificier locations.
357      //
358      // The VisitNestedNameSpecifierLoc interface is not impelmented in
359      // RecursiveASTVisitor, we have to handle it explicitly.
360      if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
361        VisitNestedNameSpecifierLocations(*NSL);
362        return true;
363      }
364
365      if (const auto *TL = Parents[0].get<TypeLoc>())
366        ParentTypeLoc = *TL;
367    }
368
369    // Handle the outermost TypeLoc which is directly linked to the interesting
370    // declaration and don't handle nested name specifier locations.
371    if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
372      if (isInUSRSet(TargetDecl)) {
373        // Only handle the outermost typeLoc.
374        //
375        // For a type like "a::Foo", there will be two typeLocs for it.
376        // One ElaboratedType, the other is RecordType:
377        //
378        //   ElaboratedType 0x33b9390 'a::Foo' sugar
379        //   `-RecordType 0x338fef0 'class a::Foo'
380        //     `-CXXRecord 0x338fe58 'Foo'
381        //
382        // Skip if this is an inner typeLoc.
383        if (!ParentTypeLoc.isNull() &&
384            isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
385          return true;
386
387        auto StartLoc = StartLocationForType(Loc);
388        auto EndLoc = EndLocationForType(Loc);
389        if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
390          RenameInfo Info = {StartLoc,
391                             EndLoc,
392                             TargetDecl,
393                             getClosestAncestorDecl(Loc),
394                             GetNestedNameForType(Loc),
395                             /*IgnorePrefixQualifers=*/false};
396          RenameInfos.push_back(Info);
397        }
398        return true;
399      }
400    }
401
402    // Handle specific template class specialiation cases.
403    if (const auto *TemplateSpecType =
404            dyn_cast<TemplateSpecializationType>(Loc.getType())) {
405      TypeLoc TargetLoc = Loc;
406      if (!ParentTypeLoc.isNull()) {
407        if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
408          TargetLoc = ParentTypeLoc;
409      }
410
411      if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
412        TypeLoc TargetLoc = Loc;
413        // FIXME: Find a better way to handle this case.
414        // For the qualified template class specification type like
415        // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
416        // (ElaboratedType) of the TemplateSpecializationType in order to
417        // catch the prefix qualifiers "ns::".
418        if (!ParentTypeLoc.isNull() &&
419            llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
420          TargetLoc = ParentTypeLoc;
421
422        auto StartLoc = StartLocationForType(TargetLoc);
423        auto EndLoc = EndLocationForType(TargetLoc);
424        if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
425          RenameInfo Info = {
426              StartLoc,
427              EndLoc,
428              TemplateSpecType->getTemplateName().getAsTemplateDecl(),
429              getClosestAncestorDecl(
430                  ast_type_traits::DynTypedNode::create(TargetLoc)),
431              GetNestedNameForType(TargetLoc),
432              /*IgnorePrefixQualifers=*/false};
433          RenameInfos.push_back(Info);
434        }
435      }
436    }
437    return true;
438  }
439
440  // Returns a list of RenameInfo.
441  const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
442
443  // Returns a list of using declarations which are needed to update.
444  const std::vector<const UsingDecl *> &getUsingDecls() const {
445    return UsingDecls;
446  }
447
448private:
449  // Get the supported declaration from a given typeLoc. If the declaration type
450  // is not supported, returns nullptr.
451  const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
452    if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>())
453      return TT->getDecl();
454    if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
455      return RD;
456    if (const auto *ED =
457            llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl()))
458      return ED;
459    return nullptr;
460  }
461
462  // Get the closest ancester which is a declaration of a given AST node.
463  template <typename ASTNodeType>
464  const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
465    auto Parents = Context.getParents(Node);
466    // FIXME: figure out how to handle it when there are multiple parents.
467    if (Parents.size() != 1)
468      return nullptr;
469    if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
470            Parents[0].getNodeKind()))
471      return Parents[0].template get<Decl>();
472    return getClosestAncestorDecl(Parents[0]);
473  }
474
475  // Get the parent typeLoc of a given typeLoc. If there is no such parent,
476  // return nullptr.
477  const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
478    auto Parents = Context.getParents(Loc);
479    // FIXME: figure out how to handle it when there are multiple parents.
480    if (Parents.size() != 1)
481      return nullptr;
482    return Parents[0].get<TypeLoc>();
483  }
484
485  // Check whether the USR of a given Decl is in the USRSet.
486  bool isInUSRSet(const Decl *Decl) const {
487    auto USR = getUSRForDecl(Decl);
488    if (USR.empty())
489      return false;
490    return llvm::is_contained(USRSet, USR);
491  }
492
493  const std::set<std::string> USRSet;
494  ASTContext &Context;
495  std::vector<RenameInfo> RenameInfos;
496  // Record all interested using declarations which contains the using-shadow
497  // declarations of the symbol declarations being renamed.
498  std::vector<const UsingDecl *> UsingDecls;
499};
500
501} // namespace
502
503SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs,
504                                       StringRef PrevName, Decl *Decl) {
505  USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
506  Visitor.TraverseDecl(Decl);
507  return Visitor.takeOccurrences();
508}
509
510std::vector<tooling::AtomicChange>
511createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
512                          llvm::StringRef NewName, Decl *TranslationUnitDecl) {
513  RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
514  Finder.TraverseDecl(TranslationUnitDecl);
515
516  const SourceManager &SM =
517      TranslationUnitDecl->getASTContext().getSourceManager();
518
519  std::vector<tooling::AtomicChange> AtomicChanges;
520  auto Replace = [&](SourceLocation Start, SourceLocation End,
521                     llvm::StringRef Text) {
522    tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
523    llvm::Error Err = ReplaceChange.replace(
524        SM, CharSourceRange::getTokenRange(Start, End), Text);
525    if (Err) {
526      llvm::errs() << "Failed to add replacement to AtomicChange: "
527                   << llvm::toString(std::move(Err)) << "\n";
528      return;
529    }
530    AtomicChanges.push_back(std::move(ReplaceChange));
531  };
532
533  for (const auto &RenameInfo : Finder.getRenameInfos()) {
534    std::string ReplacedName = NewName.str();
535    if (RenameInfo.IgnorePrefixQualifers) {
536      // Get the name without prefix qualifiers from NewName.
537      size_t LastColonPos = NewName.find_last_of(':');
538      if (LastColonPos != std::string::npos)
539        ReplacedName = NewName.substr(LastColonPos + 1);
540    } else {
541      if (RenameInfo.FromDecl && RenameInfo.Context) {
542        if (!llvm::isa<clang::TranslationUnitDecl>(
543                RenameInfo.Context->getDeclContext())) {
544          ReplacedName = tooling::replaceNestedName(
545              RenameInfo.Specifier, RenameInfo.Begin,
546              RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
547              NewName.startswith("::") ? NewName.str()
548                                       : ("::" + NewName).str());
549        } else {
550          // This fixes the case where type `T` is a parameter inside a function
551          // type (e.g. `std::function<void(T)>`) and the DeclContext of `T`
552          // becomes the translation unit. As a workaround, we simply use
553          // fully-qualified name here for all references whose `DeclContext` is
554          // the translation unit and ignore the possible existence of
555          // using-decls (in the global scope) that can shorten the replaced
556          // name.
557          llvm::StringRef ActualName = Lexer::getSourceText(
558              CharSourceRange::getTokenRange(
559                  SourceRange(RenameInfo.Begin, RenameInfo.End)),
560              SM, TranslationUnitDecl->getASTContext().getLangOpts());
561          // Add the leading "::" back if the name written in the code contains
562          // it.
563          if (ActualName.startswith("::") && !NewName.startswith("::")) {
564            ReplacedName = "::" + NewName.str();
565          }
566        }
567      }
568      // If the NewName contains leading "::", add it back.
569      if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
570        ReplacedName = NewName.str();
571    }
572    Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
573  }
574
575  // Hanlde using declarations explicitly as "using a::Foo" don't trigger
576  // typeLoc for "a::Foo".
577  for (const auto *Using : Finder.getUsingDecls())
578    Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str());
579
580  return AtomicChanges;
581}
582
583} // end namespace tooling
584} // end namespace clang
585