1 //===- unittest/Tooling/RecursiveASTVisitorTest.cpp -----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "TestVisitor.h" 11 12 #include <stack> 13 14 namespace clang { 15 16 class TypeLocVisitor : public ExpectedLocationVisitor<TypeLocVisitor> { 17 public: 18 bool VisitTypeLoc(TypeLoc TypeLocation) { 19 Match(TypeLocation.getType().getAsString(), TypeLocation.getBeginLoc()); 20 return true; 21 } 22 }; 23 24 class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> { 25 public: 26 bool VisitDeclRefExpr(DeclRefExpr *Reference) { 27 Match(Reference->getNameInfo().getAsString(), Reference->getLocation()); 28 return true; 29 } 30 }; 31 32 class VarDeclVisitor : public ExpectedLocationVisitor<VarDeclVisitor> { 33 public: 34 bool VisitVarDecl(VarDecl *Variable) { 35 Match(Variable->getNameAsString(), Variable->getLocStart()); 36 return true; 37 } 38 }; 39 40 class ParmVarDeclVisitorForImplicitCode : 41 public ExpectedLocationVisitor<ParmVarDeclVisitorForImplicitCode> { 42 public: 43 bool shouldVisitImplicitCode() const { return true; } 44 45 bool VisitParmVarDecl(ParmVarDecl *ParamVar) { 46 Match(ParamVar->getNameAsString(), ParamVar->getLocStart()); 47 return true; 48 } 49 }; 50 51 class CXXMemberCallVisitor 52 : public ExpectedLocationVisitor<CXXMemberCallVisitor> { 53 public: 54 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *Call) { 55 Match(Call->getMethodDecl()->getQualifiedNameAsString(), 56 Call->getLocStart()); 57 return true; 58 } 59 }; 60 61 class NamedDeclVisitor 62 : public ExpectedLocationVisitor<NamedDeclVisitor> { 63 public: 64 bool VisitNamedDecl(NamedDecl *Decl) { 65 std::string NameWithTemplateArgs; 66 llvm::raw_string_ostream OS(NameWithTemplateArgs); 67 Decl->getNameForDiagnostic(OS, 68 Decl->getASTContext().getPrintingPolicy(), 69 true); 70 Match(OS.str(), Decl->getLocation()); 71 return true; 72 } 73 }; 74 75 class CXXOperatorCallExprTraverser 76 : public ExpectedLocationVisitor<CXXOperatorCallExprTraverser> { 77 public: 78 // Use Traverse, not Visit, to check that data recursion optimization isn't 79 // bypassing the call of this function. 80 bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *CE) { 81 Match(getOperatorSpelling(CE->getOperator()), CE->getExprLoc()); 82 return ExpectedLocationVisitor<CXXOperatorCallExprTraverser>:: 83 TraverseCXXOperatorCallExpr(CE); 84 } 85 }; 86 87 class ParenExprVisitor : public ExpectedLocationVisitor<ParenExprVisitor> { 88 public: 89 bool VisitParenExpr(ParenExpr *Parens) { 90 Match("", Parens->getExprLoc()); 91 return true; 92 } 93 }; 94 95 class LambdaExprVisitor : public ExpectedLocationVisitor<LambdaExprVisitor> { 96 public: 97 bool VisitLambdaExpr(LambdaExpr *Lambda) { 98 PendingBodies.push(Lambda); 99 Match("", Lambda->getIntroducerRange().getBegin()); 100 return true; 101 } 102 /// For each call to VisitLambdaExpr, we expect a subsequent call (with 103 /// proper nesting) to TraverseLambdaBody. 104 bool TraverseLambdaBody(LambdaExpr *Lambda) { 105 EXPECT_FALSE(PendingBodies.empty()); 106 EXPECT_EQ(PendingBodies.top(), Lambda); 107 PendingBodies.pop(); 108 return TraverseStmt(Lambda->getBody()); 109 } 110 /// Determine whether TraverseLambdaBody has been called for every call to 111 /// VisitLambdaExpr. 112 bool allBodiesHaveBeenTraversed() const { 113 return PendingBodies.empty(); 114 } 115 private: 116 std::stack<LambdaExpr *> PendingBodies; 117 }; 118 119 // Matches the (optional) capture-default of a lambda-introducer. 120 class LambdaDefaultCaptureVisitor 121 : public ExpectedLocationVisitor<LambdaDefaultCaptureVisitor> { 122 public: 123 bool VisitLambdaExpr(LambdaExpr *Lambda) { 124 if (Lambda->getCaptureDefault() != LCD_None) { 125 Match("", Lambda->getCaptureDefaultLoc()); 126 } 127 return true; 128 } 129 }; 130 131 class TemplateArgumentLocTraverser 132 : public ExpectedLocationVisitor<TemplateArgumentLocTraverser> { 133 public: 134 bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) { 135 std::string ArgStr; 136 llvm::raw_string_ostream Stream(ArgStr); 137 const TemplateArgument &Arg = ArgLoc.getArgument(); 138 139 Arg.print(Context->getPrintingPolicy(), Stream); 140 Match(Stream.str(), ArgLoc.getLocation()); 141 return ExpectedLocationVisitor<TemplateArgumentLocTraverser>:: 142 TraverseTemplateArgumentLoc(ArgLoc); 143 } 144 }; 145 146 class CXXBoolLiteralExprVisitor 147 : public ExpectedLocationVisitor<CXXBoolLiteralExprVisitor> { 148 public: 149 bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) { 150 if (BE->getValue()) 151 Match("true", BE->getLocation()); 152 else 153 Match("false", BE->getLocation()); 154 return true; 155 } 156 }; 157 158 // Test RAV visits parameter variable declaration of the implicit 159 // copy assignment operator and implicit copy constructor. 160 TEST(RecursiveASTVisitor, VisitsParmVarDeclForImplicitCode) { 161 ParmVarDeclVisitorForImplicitCode Visitor; 162 // Match parameter variable name of implicit copy assignment operator and 163 // implicit copy constructor. 164 // This parameter name does not have a valid IdentifierInfo, and shares 165 // same SourceLocation with its class declaration, so we match an empty name 166 // with the class' source location. 167 Visitor.ExpectMatch("", 1, 7); 168 Visitor.ExpectMatch("", 3, 7); 169 EXPECT_TRUE(Visitor.runOver( 170 "class X {};\n" 171 "void foo(X a, X b) {a = b;}\n" 172 "class Y {};\n" 173 "void bar(Y a) {Y b = a;}")); 174 } 175 176 TEST(RecursiveASTVisitor, VisitsBaseClassDeclarations) { 177 TypeLocVisitor Visitor; 178 Visitor.ExpectMatch("class X", 1, 30); 179 EXPECT_TRUE(Visitor.runOver("class X {}; class Y : public X {};")); 180 } 181 182 TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) { 183 TypeLocVisitor Visitor; 184 Visitor.ExpectMatch("class X", 3, 18); 185 EXPECT_TRUE(Visitor.runOver( 186 "class Y;\n" 187 "class X {};\n" 188 "class Y : public X {};")); 189 } 190 191 TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) { 192 TypeLocVisitor Visitor; 193 Visitor.ExpectMatch("class X", 2, 18); 194 EXPECT_TRUE(Visitor.runOver( 195 "class X {};\n" 196 "class Y : public X { class Z; };")); 197 } 198 199 TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) { 200 TypeLocVisitor Visitor; 201 Visitor.ExpectMatch("X<class Y>", 2, 18); 202 EXPECT_TRUE(Visitor.runOver( 203 "template<typename T> class X {};\n" 204 "class Y : public X<Y> {};")); 205 } 206 207 TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) { 208 DeclRefExprVisitor Visitor; 209 Visitor.ExpectMatch("x", 2, 3); 210 EXPECT_TRUE(Visitor.runOver( 211 "void x(); template <void (*T)()> class X {};\nX<x> y;")); 212 } 213 214 TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtRange) { 215 DeclRefExprVisitor Visitor; 216 Visitor.ExpectMatch("x", 2, 25); 217 Visitor.ExpectMatch("x", 2, 30); 218 EXPECT_TRUE(Visitor.runOver( 219 "int x[5];\n" 220 "void f() { for (int i : x) { x[0] = 1; } }", 221 DeclRefExprVisitor::Lang_CXX11)); 222 } 223 224 TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) { 225 VarDeclVisitor Visitor; 226 Visitor.ExpectMatch("i", 2, 17); 227 EXPECT_TRUE(Visitor.runOver( 228 "int x[5];\n" 229 "void f() { for (int i : x) {} }", 230 VarDeclVisitor::Lang_CXX11)); 231 } 232 233 TEST(RecursiveASTVisitor, VisitsCallExpr) { 234 DeclRefExprVisitor Visitor; 235 Visitor.ExpectMatch("x", 1, 22); 236 EXPECT_TRUE(Visitor.runOver( 237 "void x(); void y() { x(); }")); 238 } 239 240 TEST(RecursiveASTVisitor, VisitsCallInTemplateInstantiation) { 241 CXXMemberCallVisitor Visitor; 242 Visitor.ExpectMatch("Y::x", 3, 3); 243 EXPECT_TRUE(Visitor.runOver( 244 "struct Y { void x(); };\n" 245 "template<typename T> void y(T t) {\n" 246 " t.x();\n" 247 "}\n" 248 "void foo() { y<Y>(Y()); }")); 249 } 250 251 TEST(RecursiveASTVisitor, VisitsCallInNestedFunctionTemplateInstantiation) { 252 CXXMemberCallVisitor Visitor; 253 Visitor.ExpectMatch("Y::x", 4, 5); 254 EXPECT_TRUE(Visitor.runOver( 255 "struct Y { void x(); };\n" 256 "template<typename T> struct Z {\n" 257 " template<typename U> static void f() {\n" 258 " T().x();\n" 259 " }\n" 260 "};\n" 261 "void foo() { Z<Y>::f<int>(); }")); 262 } 263 264 TEST(RecursiveASTVisitor, VisitsCallInNestedClassTemplateInstantiation) { 265 CXXMemberCallVisitor Visitor; 266 Visitor.ExpectMatch("A::x", 5, 7); 267 EXPECT_TRUE(Visitor.runOver( 268 "template <typename T1> struct X {\n" 269 " template <typename T2> struct Y {\n" 270 " void f() {\n" 271 " T2 y;\n" 272 " y.x();\n" 273 " }\n" 274 " };\n" 275 "};\n" 276 "struct A { void x(); };\n" 277 "int main() {\n" 278 " (new X<A>::Y<A>())->f();\n" 279 "}")); 280 } 281 282 /* FIXME: According to Richard Smith this is a bug in the AST. 283 TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) { 284 DeclRefExprVisitor Visitor; 285 Visitor.ExpectMatch("x", 3, 43); 286 EXPECT_TRUE(Visitor.runOver( 287 "template <typename T> void x();\n" 288 "template <void (*T)()> class X {};\n" 289 "template <typename T> class Y : public X< x<T> > {};\n" 290 "Y<int> y;")); 291 } 292 */ 293 294 TEST(RecursiveASTVisitor, VisitsCallInPartialTemplateSpecialization) { 295 CXXMemberCallVisitor Visitor; 296 Visitor.ExpectMatch("A::x", 6, 20); 297 EXPECT_TRUE(Visitor.runOver( 298 "template <typename T1> struct X {\n" 299 " template <typename T2, bool B> struct Y { void g(); };\n" 300 "};\n" 301 "template <typename T1> template <typename T2>\n" 302 "struct X<T1>::Y<T2, true> {\n" 303 " void f() { T2 y; y.x(); }\n" 304 "};\n" 305 "struct A { void x(); };\n" 306 "int main() {\n" 307 " (new X<A>::Y<A, true>())->f();\n" 308 "}\n")); 309 } 310 311 TEST(RecursiveASTVisitor, VisitsExplicitTemplateSpecialization) { 312 CXXMemberCallVisitor Visitor; 313 Visitor.ExpectMatch("A::f", 4, 5); 314 EXPECT_TRUE(Visitor.runOver( 315 "struct A {\n" 316 " void f() const {}\n" 317 " template<class T> void g(const T& t) const {\n" 318 " t.f();\n" 319 " }\n" 320 "};\n" 321 "template void A::g(const A& a) const;\n")); 322 } 323 324 TEST(RecursiveASTVisitor, VisitsPartialTemplateSpecialization) { 325 // From cfe-commits/Week-of-Mon-20100830/033998.html 326 // Contrary to the approach suggested in that email, we visit all 327 // specializations when we visit the primary template. Visiting them when we 328 // visit the associated specialization is problematic for specializations of 329 // template members of class templates. 330 NamedDeclVisitor Visitor; 331 Visitor.ExpectMatch("A<bool>", 1, 26); 332 Visitor.ExpectMatch("A<char *>", 2, 26); 333 EXPECT_TRUE(Visitor.runOver( 334 "template <class T> class A {};\n" 335 "template <class T> class A<T*> {};\n" 336 "A<bool> ab;\n" 337 "A<char*> acp;\n")); 338 } 339 340 TEST(RecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) { 341 NamedDeclVisitor Visitor; 342 Visitor.ExpectMatch("A<int>", 1, 29); 343 EXPECT_TRUE(Visitor.runOver( 344 "template<typename T> struct A;\n" 345 "A<int> *p;\n")); 346 } 347 348 TEST(RecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) { 349 NamedDeclVisitor Visitor; 350 Visitor.ExpectMatch("A<int>::B<char>", 2, 31); 351 EXPECT_TRUE(Visitor.runOver( 352 "template<typename T> struct A {\n" 353 " template<typename U> struct B;\n" 354 "};\n" 355 "A<int>::B<char> *p;\n")); 356 } 357 358 TEST(RecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) { 359 NamedDeclVisitor Visitor; 360 Visitor.ExpectMatch("A<int>", 1, 26); 361 EXPECT_TRUE(Visitor.runOver( 362 "template<typename T> int A();\n" 363 "int k = A<int>();\n")); 364 } 365 366 TEST(RecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) { 367 NamedDeclVisitor Visitor; 368 Visitor.ExpectMatch("A<int>::B<char>", 2, 35); 369 EXPECT_TRUE(Visitor.runOver( 370 "template<typename T> struct A {\n" 371 " template<typename U> static int B();\n" 372 "};\n" 373 "int k = A<int>::B<char>();\n")); 374 } 375 376 TEST(RecursiveASTVisitor, NoRecursionInSelfFriend) { 377 // From cfe-commits/Week-of-Mon-20100830/033977.html 378 NamedDeclVisitor Visitor; 379 Visitor.ExpectMatch("vector_iterator<int>", 2, 7); 380 EXPECT_TRUE(Visitor.runOver( 381 "template<typename Container>\n" 382 "class vector_iterator {\n" 383 " template <typename C> friend class vector_iterator;\n" 384 "};\n" 385 "vector_iterator<int> it_int;\n")); 386 } 387 388 TEST(RecursiveASTVisitor, TraversesOverloadedOperator) { 389 CXXOperatorCallExprTraverser Visitor; 390 Visitor.ExpectMatch("()", 4, 9); 391 EXPECT_TRUE(Visitor.runOver( 392 "struct A {\n" 393 " int operator()();\n" 394 "} a;\n" 395 "int k = a();\n")); 396 } 397 398 TEST(RecursiveASTVisitor, VisitsParensDuringDataRecursion) { 399 ParenExprVisitor Visitor; 400 Visitor.ExpectMatch("", 1, 9); 401 EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n")); 402 } 403 404 TEST(RecursiveASTVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) { 405 CXXBoolLiteralExprVisitor Visitor; 406 Visitor.ExpectMatch("true", 2, 19); 407 EXPECT_TRUE(Visitor.runOver( 408 "template<bool B> class X;\n" 409 "template<bool B = true> class Y;\n" 410 "template<bool B> class Y {};\n")); 411 } 412 413 TEST(RecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) { 414 TypeLocVisitor Visitor; 415 Visitor.ExpectMatch("class X", 2, 23); 416 EXPECT_TRUE(Visitor.runOver( 417 "class X;\n" 418 "template<typename T = X> class Y;\n" 419 "template<typename T> class Y {};\n")); 420 } 421 422 TEST(RecursiveASTVisitor, VisitsClassTemplateTemplateParmDefaultArgument) { 423 TemplateArgumentLocTraverser Visitor; 424 Visitor.ExpectMatch("X", 2, 40); 425 EXPECT_TRUE(Visitor.runOver( 426 "template<typename T> class X;\n" 427 "template<template <typename> class T = X> class Y;\n" 428 "template<template <typename> class T> class Y {};\n")); 429 } 430 431 // A visitor that visits implicit declarations and matches constructors. 432 class ImplicitCtorVisitor 433 : public ExpectedLocationVisitor<ImplicitCtorVisitor> { 434 public: 435 bool shouldVisitImplicitCode() const { return true; } 436 437 bool VisitCXXConstructorDecl(CXXConstructorDecl* Ctor) { 438 if (Ctor->isImplicit()) { // Was not written in source code 439 if (const CXXRecordDecl* Class = Ctor->getParent()) { 440 Match(Class->getName(), Ctor->getLocation()); 441 } 442 } 443 return true; 444 } 445 }; 446 447 TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) { 448 ImplicitCtorVisitor Visitor; 449 Visitor.ExpectMatch("Simple", 2, 8); 450 // Note: Clang lazily instantiates implicit declarations, so we need 451 // to use them in order to force them to appear in the AST. 452 EXPECT_TRUE(Visitor.runOver( 453 "struct WithCtor { WithCtor(); }; \n" 454 "struct Simple { Simple(); WithCtor w; }; \n" 455 "int main() { Simple s; Simple t(s); }\n")); 456 } 457 458 /// \brief A visitor that optionally includes implicit code and matches 459 /// CXXConstructExpr. 460 /// 461 /// The name recorded for the match is the name of the class whose constructor 462 /// is invoked by the CXXConstructExpr, not the name of the class whose 463 /// constructor the CXXConstructExpr is contained in. 464 class ConstructExprVisitor 465 : public ExpectedLocationVisitor<ConstructExprVisitor> { 466 public: 467 ConstructExprVisitor() : ShouldVisitImplicitCode(false) {} 468 469 bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; } 470 471 void setShouldVisitImplicitCode(bool NewValue) { 472 ShouldVisitImplicitCode = NewValue; 473 } 474 475 bool VisitCXXConstructExpr(CXXConstructExpr* Expr) { 476 if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) { 477 if (const CXXRecordDecl* Class = Ctor->getParent()) { 478 Match(Class->getName(), Expr->getLocation()); 479 } 480 } 481 return true; 482 } 483 484 private: 485 bool ShouldVisitImplicitCode; 486 }; 487 488 TEST(RecursiveASTVisitor, CanVisitImplicitMemberInitializations) { 489 ConstructExprVisitor Visitor; 490 Visitor.setShouldVisitImplicitCode(true); 491 Visitor.ExpectMatch("WithCtor", 2, 8); 492 // Simple has a constructor that implicitly initializes 'w'. Test 493 // that a visitor that visits implicit code visits that initialization. 494 // Note: Clang lazily instantiates implicit declarations, so we need 495 // to use them in order to force them to appear in the AST. 496 EXPECT_TRUE(Visitor.runOver( 497 "struct WithCtor { WithCtor(); }; \n" 498 "struct Simple { WithCtor w; }; \n" 499 "int main() { Simple s; }\n")); 500 } 501 502 // The same as CanVisitImplicitMemberInitializations, but checking that the 503 // visits are omitted when the visitor does not include implicit code. 504 TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) { 505 ConstructExprVisitor Visitor; 506 Visitor.setShouldVisitImplicitCode(false); 507 Visitor.DisallowMatch("WithCtor", 2, 8); 508 // Simple has a constructor that implicitly initializes 'w'. Test 509 // that a visitor that skips implicit code skips that initialization. 510 // Note: Clang lazily instantiates implicit declarations, so we need 511 // to use them in order to force them to appear in the AST. 512 EXPECT_TRUE(Visitor.runOver( 513 "struct WithCtor { WithCtor(); }; \n" 514 "struct Simple { WithCtor w; }; \n" 515 "int main() { Simple s; }\n")); 516 } 517 518 TEST(RecursiveASTVisitor, VisitsExtension) { 519 DeclRefExprVisitor Visitor; 520 Visitor.ExpectMatch("s", 1, 24); 521 EXPECT_TRUE(Visitor.runOver( 522 "int s = __extension__ (s);\n")); 523 } 524 525 TEST(RecursiveASTVisitor, VisitsCompoundLiteralType) { 526 TypeLocVisitor Visitor; 527 Visitor.ExpectMatch("struct S", 1, 26); 528 EXPECT_TRUE(Visitor.runOver( 529 "int f() { return (struct S { int a; }){.a = 0}.a; }", 530 TypeLocVisitor::Lang_C)); 531 } 532 533 TEST(RecursiveASTVisitor, VisitsLambdaExpr) { 534 LambdaExprVisitor Visitor; 535 Visitor.ExpectMatch("", 1, 12); 536 EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }", 537 LambdaExprVisitor::Lang_CXX11)); 538 } 539 540 TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) { 541 LambdaExprVisitor Visitor; 542 EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }", 543 LambdaExprVisitor::Lang_CXX11)); 544 EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed()); 545 } 546 547 TEST(RecursiveASTVisitor, HasCaptureDefaultLoc) { 548 LambdaDefaultCaptureVisitor Visitor; 549 Visitor.ExpectMatch("", 1, 20); 550 EXPECT_TRUE(Visitor.runOver("void f() { int a; [=]{a;}; }", 551 LambdaDefaultCaptureVisitor::Lang_CXX11)); 552 } 553 554 // Checks for lambda classes that are not marked as implicitly-generated. 555 // (There should be none.) 556 class ClassVisitor : public ExpectedLocationVisitor<ClassVisitor> { 557 public: 558 ClassVisitor() : SawNonImplicitLambdaClass(false) {} 559 bool VisitCXXRecordDecl(CXXRecordDecl* record) { 560 if (record->isLambda() && !record->isImplicit()) 561 SawNonImplicitLambdaClass = true; 562 return true; 563 } 564 565 bool sawOnlyImplicitLambdaClasses() const { 566 return !SawNonImplicitLambdaClass; 567 } 568 569 private: 570 bool SawNonImplicitLambdaClass; 571 }; 572 573 TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) { 574 ClassVisitor Visitor; 575 EXPECT_TRUE(Visitor.runOver("auto lambda = []{};", 576 ClassVisitor::Lang_CXX11)); 577 EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses()); 578 } 579 580 } // end namespace clang 581