1 //===--- Lookup.h - Classes for name lookup ---------------------*- C++ -*-===// 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 // This file defines the LookupResult class, which is integral to 11 // Sema's name-lookup subsystem. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_CLANG_SEMA_LOOKUP_H 16 #define LLVM_CLANG_SEMA_LOOKUP_H 17 18 #include "clang/AST/DeclCXX.h" 19 #include "clang/Sema/Sema.h" 20 21 namespace clang { 22 23 /// @brief Represents the results of name lookup. 24 /// 25 /// An instance of the LookupResult class captures the results of a 26 /// single name lookup, which can return no result (nothing found), 27 /// a single declaration, a set of overloaded functions, or an 28 /// ambiguity. Use the getKind() method to determine which of these 29 /// results occurred for a given lookup. 30 class LookupResult { 31 public: 32 enum LookupResultKind { 33 /// @brief No entity found met the criteria. 34 NotFound = 0, 35 36 /// @brief No entity found met the criteria within the current 37 /// instantiation,, but there were dependent base classes of the 38 /// current instantiation that could not be searched. 39 NotFoundInCurrentInstantiation, 40 41 /// @brief Name lookup found a single declaration that met the 42 /// criteria. getFoundDecl() will return this declaration. 43 Found, 44 45 /// @brief Name lookup found a set of overloaded functions that 46 /// met the criteria. 47 FoundOverloaded, 48 49 /// @brief Name lookup found an unresolvable value declaration 50 /// and cannot yet complete. This only happens in C++ dependent 51 /// contexts with dependent using declarations. 52 FoundUnresolvedValue, 53 54 /// @brief Name lookup results in an ambiguity; use 55 /// getAmbiguityKind to figure out what kind of ambiguity 56 /// we have. 57 Ambiguous 58 }; 59 60 enum AmbiguityKind { 61 /// Name lookup results in an ambiguity because multiple 62 /// entities that meet the lookup criteria were found in 63 /// subobjects of different types. For example: 64 /// @code 65 /// struct A { void f(int); } 66 /// struct B { void f(double); } 67 /// struct C : A, B { }; 68 /// void test(C c) { 69 /// c.f(0); // error: A::f and B::f come from subobjects of different 70 /// // types. overload resolution is not performed. 71 /// } 72 /// @endcode 73 AmbiguousBaseSubobjectTypes, 74 75 /// Name lookup results in an ambiguity because multiple 76 /// nonstatic entities that meet the lookup criteria were found 77 /// in different subobjects of the same type. For example: 78 /// @code 79 /// struct A { int x; }; 80 /// struct B : A { }; 81 /// struct C : A { }; 82 /// struct D : B, C { }; 83 /// int test(D d) { 84 /// return d.x; // error: 'x' is found in two A subobjects (of B and C) 85 /// } 86 /// @endcode 87 AmbiguousBaseSubobjects, 88 89 /// Name lookup results in an ambiguity because multiple definitions 90 /// of entity that meet the lookup criteria were found in different 91 /// declaration contexts. 92 /// @code 93 /// namespace A { 94 /// int i; 95 /// namespace B { int i; } 96 /// int test() { 97 /// using namespace B; 98 /// return i; // error 'i' is found in namespace A and A::B 99 /// } 100 /// } 101 /// @endcode 102 AmbiguousReference, 103 104 /// Name lookup results in an ambiguity because an entity with a 105 /// tag name was hidden by an entity with an ordinary name from 106 /// a different context. 107 /// @code 108 /// namespace A { struct Foo {}; } 109 /// namespace B { void Foo(); } 110 /// namespace C { 111 /// using namespace A; 112 /// using namespace B; 113 /// } 114 /// void test() { 115 /// C::Foo(); // error: tag 'A::Foo' is hidden by an object in a 116 /// // different namespace 117 /// } 118 /// @endcode 119 AmbiguousTagHiding 120 }; 121 122 /// A little identifier for flagging temporary lookup results. 123 enum TemporaryToken { 124 Temporary 125 }; 126 127 typedef UnresolvedSetImpl::iterator iterator; 128 129 LookupResult(Sema &SemaRef, const DeclarationNameInfo &NameInfo, 130 Sema::LookupNameKind LookupKind, 131 Sema::RedeclarationKind Redecl = Sema::NotForRedeclaration) ResultKind(NotFound)132 : ResultKind(NotFound), 133 Paths(0), 134 NamingClass(0), 135 SemaRef(SemaRef), 136 NameInfo(NameInfo), 137 LookupKind(LookupKind), 138 IDNS(0), 139 Redecl(Redecl != Sema::NotForRedeclaration), 140 HideTags(true), 141 Diagnose(Redecl == Sema::NotForRedeclaration), 142 AllowHidden(Redecl == Sema::ForRedeclaration) 143 { 144 configure(); 145 } 146 147 // TODO: consider whether this constructor should be restricted to take 148 // as input a const IndentifierInfo* (instead of Name), 149 // forcing other cases towards the constructor taking a DNInfo. 150 LookupResult(Sema &SemaRef, DeclarationName Name, 151 SourceLocation NameLoc, Sema::LookupNameKind LookupKind, 152 Sema::RedeclarationKind Redecl = Sema::NotForRedeclaration) ResultKind(NotFound)153 : ResultKind(NotFound), 154 Paths(0), 155 NamingClass(0), 156 SemaRef(SemaRef), 157 NameInfo(Name, NameLoc), 158 LookupKind(LookupKind), 159 IDNS(0), 160 Redecl(Redecl != Sema::NotForRedeclaration), 161 HideTags(true), 162 Diagnose(Redecl == Sema::NotForRedeclaration), 163 AllowHidden(Redecl == Sema::ForRedeclaration) 164 { 165 configure(); 166 } 167 168 /// Creates a temporary lookup result, initializing its core data 169 /// using the information from another result. Diagnostics are always 170 /// disabled. LookupResult(TemporaryToken _,const LookupResult & Other)171 LookupResult(TemporaryToken _, const LookupResult &Other) 172 : ResultKind(NotFound), 173 Paths(0), 174 NamingClass(0), 175 SemaRef(Other.SemaRef), 176 NameInfo(Other.NameInfo), 177 LookupKind(Other.LookupKind), 178 IDNS(Other.IDNS), 179 Redecl(Other.Redecl), 180 HideTags(Other.HideTags), 181 Diagnose(false), 182 AllowHidden(Other.AllowHidden) 183 {} 184 ~LookupResult()185 ~LookupResult() { 186 if (Diagnose) diagnose(); 187 if (Paths) deletePaths(Paths); 188 } 189 190 /// Gets the name info to look up. getLookupNameInfo()191 const DeclarationNameInfo &getLookupNameInfo() const { 192 return NameInfo; 193 } 194 195 /// \brief Sets the name info to look up. setLookupNameInfo(const DeclarationNameInfo & NameInfo)196 void setLookupNameInfo(const DeclarationNameInfo &NameInfo) { 197 this->NameInfo = NameInfo; 198 } 199 200 /// Gets the name to look up. getLookupName()201 DeclarationName getLookupName() const { 202 return NameInfo.getName(); 203 } 204 205 /// \brief Sets the name to look up. setLookupName(DeclarationName Name)206 void setLookupName(DeclarationName Name) { 207 NameInfo.setName(Name); 208 } 209 210 /// Gets the kind of lookup to perform. getLookupKind()211 Sema::LookupNameKind getLookupKind() const { 212 return LookupKind; 213 } 214 215 /// True if this lookup is just looking for an existing declaration. isForRedeclaration()216 bool isForRedeclaration() const { 217 return Redecl; 218 } 219 220 /// \brief Specify whether hidden declarations are visible, e.g., 221 /// for recovery reasons. setAllowHidden(bool AH)222 void setAllowHidden(bool AH) { 223 AllowHidden = AH; 224 } 225 226 /// \brief Determine whether this lookup is permitted to see hidden 227 /// declarations, such as those in modules that have not yet been imported. isHiddenDeclarationVisible()228 bool isHiddenDeclarationVisible() const { 229 return AllowHidden || LookupKind == Sema::LookupTagName; 230 } 231 232 /// Sets whether tag declarations should be hidden by non-tag 233 /// declarations during resolution. The default is true. setHideTags(bool Hide)234 void setHideTags(bool Hide) { 235 HideTags = Hide; 236 } 237 isAmbiguous()238 bool isAmbiguous() const { 239 return getResultKind() == Ambiguous; 240 } 241 242 /// Determines if this names a single result which is not an 243 /// unresolved value using decl. If so, it is safe to call 244 /// getFoundDecl(). isSingleResult()245 bool isSingleResult() const { 246 return getResultKind() == Found; 247 } 248 249 /// Determines if the results are overloaded. isOverloadedResult()250 bool isOverloadedResult() const { 251 return getResultKind() == FoundOverloaded; 252 } 253 isUnresolvableResult()254 bool isUnresolvableResult() const { 255 return getResultKind() == FoundUnresolvedValue; 256 } 257 getResultKind()258 LookupResultKind getResultKind() const { 259 sanity(); 260 return ResultKind; 261 } 262 getAmbiguityKind()263 AmbiguityKind getAmbiguityKind() const { 264 assert(isAmbiguous()); 265 return Ambiguity; 266 } 267 asUnresolvedSet()268 const UnresolvedSetImpl &asUnresolvedSet() const { 269 return Decls; 270 } 271 begin()272 iterator begin() const { return iterator(Decls.begin()); } end()273 iterator end() const { return iterator(Decls.end()); } 274 275 /// \brief Return true if no decls were found empty()276 bool empty() const { return Decls.empty(); } 277 278 /// \brief Return the base paths structure that's associated with 279 /// these results, or null if none is. getBasePaths()280 CXXBasePaths *getBasePaths() const { 281 return Paths; 282 } 283 284 /// \brief Determine whether the given declaration is visible to the 285 /// program. isVisible(NamedDecl * D)286 static bool isVisible(NamedDecl *D) { 287 // If this declaration is not hidden, it's visible. 288 if (!D->isHidden()) 289 return true; 290 291 // FIXME: We should be allowed to refer to a module-private name from 292 // within the same module, e.g., during template instantiation. 293 // This requires us know which module a particular declaration came from. 294 return false; 295 } 296 297 /// \brief Retrieve the accepted (re)declaration of the given declaration, 298 /// if there is one. getAcceptableDecl(NamedDecl * D)299 NamedDecl *getAcceptableDecl(NamedDecl *D) const { 300 if (!D->isInIdentifierNamespace(IDNS)) 301 return 0; 302 303 if (isHiddenDeclarationVisible() || isVisible(D)) 304 return D; 305 306 return getAcceptableDeclSlow(D); 307 } 308 309 private: 310 NamedDecl *getAcceptableDeclSlow(NamedDecl *D) const; 311 public: 312 313 /// \brief Returns the identifier namespace mask for this lookup. getIdentifierNamespace()314 unsigned getIdentifierNamespace() const { 315 return IDNS; 316 } 317 318 /// \brief Returns whether these results arose from performing a 319 /// lookup into a class. isClassLookup()320 bool isClassLookup() const { 321 return NamingClass != 0; 322 } 323 324 /// \brief Returns the 'naming class' for this lookup, i.e. the 325 /// class which was looked into to find these results. 326 /// 327 /// C++0x [class.access.base]p5: 328 /// The access to a member is affected by the class in which the 329 /// member is named. This naming class is the class in which the 330 /// member name was looked up and found. [Note: this class can be 331 /// explicit, e.g., when a qualified-id is used, or implicit, 332 /// e.g., when a class member access operator (5.2.5) is used 333 /// (including cases where an implicit "this->" is added). If both 334 /// a class member access operator and a qualified-id are used to 335 /// name the member (as in p->T::m), the class naming the member 336 /// is the class named by the nested-name-specifier of the 337 /// qualified-id (that is, T). -- end note ] 338 /// 339 /// This is set by the lookup routines when they find results in a class. getNamingClass()340 CXXRecordDecl *getNamingClass() const { 341 return NamingClass; 342 } 343 344 /// \brief Sets the 'naming class' for this lookup. setNamingClass(CXXRecordDecl * Record)345 void setNamingClass(CXXRecordDecl *Record) { 346 NamingClass = Record; 347 } 348 349 /// \brief Returns the base object type associated with this lookup; 350 /// important for [class.protected]. Most lookups do not have an 351 /// associated base object. getBaseObjectType()352 QualType getBaseObjectType() const { 353 return BaseObjectType; 354 } 355 356 /// \brief Sets the base object type for this lookup. setBaseObjectType(QualType T)357 void setBaseObjectType(QualType T) { 358 BaseObjectType = T; 359 } 360 361 /// \brief Add a declaration to these results with its natural access. 362 /// Does not test the acceptance criteria. addDecl(NamedDecl * D)363 void addDecl(NamedDecl *D) { 364 addDecl(D, D->getAccess()); 365 } 366 367 /// \brief Add a declaration to these results with the given access. 368 /// Does not test the acceptance criteria. addDecl(NamedDecl * D,AccessSpecifier AS)369 void addDecl(NamedDecl *D, AccessSpecifier AS) { 370 Decls.addDecl(D, AS); 371 ResultKind = Found; 372 } 373 374 /// \brief Add all the declarations from another set of lookup 375 /// results. addAllDecls(const LookupResult & Other)376 void addAllDecls(const LookupResult &Other) { 377 Decls.append(Other.Decls.begin(), Other.Decls.end()); 378 ResultKind = Found; 379 } 380 381 /// \brief Determine whether no result was found because we could not 382 /// search into dependent base classes of the current instantiation. wasNotFoundInCurrentInstantiation()383 bool wasNotFoundInCurrentInstantiation() const { 384 return ResultKind == NotFoundInCurrentInstantiation; 385 } 386 387 /// \brief Note that while no result was found in the current instantiation, 388 /// there were dependent base classes that could not be searched. setNotFoundInCurrentInstantiation()389 void setNotFoundInCurrentInstantiation() { 390 assert(ResultKind == NotFound && Decls.empty()); 391 ResultKind = NotFoundInCurrentInstantiation; 392 } 393 394 /// \brief Resolves the result kind of the lookup, possibly hiding 395 /// decls. 396 /// 397 /// This should be called in any environment where lookup might 398 /// generate multiple lookup results. 399 void resolveKind(); 400 401 /// \brief Re-resolves the result kind of the lookup after a set of 402 /// removals has been performed. resolveKindAfterFilter()403 void resolveKindAfterFilter() { 404 if (Decls.empty()) { 405 if (ResultKind != NotFoundInCurrentInstantiation) 406 ResultKind = NotFound; 407 408 if (Paths) { 409 deletePaths(Paths); 410 Paths = 0; 411 } 412 } else { 413 AmbiguityKind SavedAK = Ambiguity; 414 ResultKind = Found; 415 resolveKind(); 416 417 // If we didn't make the lookup unambiguous, restore the old 418 // ambiguity kind. 419 if (ResultKind == Ambiguous) { 420 Ambiguity = SavedAK; 421 } else if (Paths) { 422 deletePaths(Paths); 423 Paths = 0; 424 } 425 } 426 } 427 428 template <class DeclClass> getAsSingle()429 DeclClass *getAsSingle() const { 430 if (getResultKind() != Found) return 0; 431 return dyn_cast<DeclClass>(getFoundDecl()); 432 } 433 434 /// \brief Fetch the unique decl found by this lookup. Asserts 435 /// that one was found. 436 /// 437 /// This is intended for users who have examined the result kind 438 /// and are certain that there is only one result. getFoundDecl()439 NamedDecl *getFoundDecl() const { 440 assert(getResultKind() == Found 441 && "getFoundDecl called on non-unique result"); 442 return (*begin())->getUnderlyingDecl(); 443 } 444 445 /// Fetches a representative decl. Useful for lazy diagnostics. getRepresentativeDecl()446 NamedDecl *getRepresentativeDecl() const { 447 assert(!Decls.empty() && "cannot get representative of empty set"); 448 return *begin(); 449 } 450 451 /// \brief Asks if the result is a single tag decl. isSingleTagDecl()452 bool isSingleTagDecl() const { 453 return getResultKind() == Found && isa<TagDecl>(getFoundDecl()); 454 } 455 456 /// \brief Make these results show that the name was found in 457 /// base classes of different types. 458 /// 459 /// The given paths object is copied and invalidated. 460 void setAmbiguousBaseSubobjectTypes(CXXBasePaths &P); 461 462 /// \brief Make these results show that the name was found in 463 /// distinct base classes of the same type. 464 /// 465 /// The given paths object is copied and invalidated. 466 void setAmbiguousBaseSubobjects(CXXBasePaths &P); 467 468 /// \brief Make these results show that the name was found in 469 /// different contexts and a tag decl was hidden by an ordinary 470 /// decl in a different context. setAmbiguousQualifiedTagHiding()471 void setAmbiguousQualifiedTagHiding() { 472 setAmbiguous(AmbiguousTagHiding); 473 } 474 475 /// \brief Clears out any current state. clear()476 void clear() { 477 ResultKind = NotFound; 478 Decls.clear(); 479 if (Paths) deletePaths(Paths); 480 Paths = NULL; 481 NamingClass = 0; 482 } 483 484 /// \brief Clears out any current state and re-initializes for a 485 /// different kind of lookup. clear(Sema::LookupNameKind Kind)486 void clear(Sema::LookupNameKind Kind) { 487 clear(); 488 LookupKind = Kind; 489 configure(); 490 } 491 492 /// \brief Change this lookup's redeclaration kind. setRedeclarationKind(Sema::RedeclarationKind RK)493 void setRedeclarationKind(Sema::RedeclarationKind RK) { 494 Redecl = RK; 495 AllowHidden = (RK == Sema::ForRedeclaration); 496 configure(); 497 } 498 499 void print(raw_ostream &); 500 501 /// Suppress the diagnostics that would normally fire because of this 502 /// lookup. This happens during (e.g.) redeclaration lookups. suppressDiagnostics()503 void suppressDiagnostics() { 504 Diagnose = false; 505 } 506 507 /// Determines whether this lookup is suppressing diagnostics. isSuppressingDiagnostics()508 bool isSuppressingDiagnostics() const { 509 return !Diagnose; 510 } 511 512 /// Sets a 'context' source range. setContextRange(SourceRange SR)513 void setContextRange(SourceRange SR) { 514 NameContextRange = SR; 515 } 516 517 /// Gets the source range of the context of this name; for C++ 518 /// qualified lookups, this is the source range of the scope 519 /// specifier. getContextRange()520 SourceRange getContextRange() const { 521 return NameContextRange; 522 } 523 524 /// Gets the location of the identifier. This isn't always defined: 525 /// sometimes we're doing lookups on synthesized names. getNameLoc()526 SourceLocation getNameLoc() const { 527 return NameInfo.getLoc(); 528 } 529 530 /// \brief Get the Sema object that this lookup result is searching 531 /// with. getSema()532 Sema &getSema() const { return SemaRef; } 533 534 /// A class for iterating through a result set and possibly 535 /// filtering out results. The results returned are possibly 536 /// sugared. 537 class Filter { 538 LookupResult &Results; 539 LookupResult::iterator I; 540 bool Changed; 541 bool CalledDone; 542 543 friend class LookupResult; Filter(LookupResult & Results)544 Filter(LookupResult &Results) 545 : Results(Results), I(Results.begin()), Changed(false), CalledDone(false) 546 {} 547 548 public: ~Filter()549 ~Filter() { 550 assert(CalledDone && 551 "LookupResult::Filter destroyed without done() call"); 552 } 553 hasNext()554 bool hasNext() const { 555 return I != Results.end(); 556 } 557 next()558 NamedDecl *next() { 559 assert(I != Results.end() && "next() called on empty filter"); 560 return *I++; 561 } 562 563 /// Restart the iteration. restart()564 void restart() { 565 I = Results.begin(); 566 } 567 568 /// Erase the last element returned from this iterator. erase()569 void erase() { 570 Results.Decls.erase(--I); 571 Changed = true; 572 } 573 574 /// Replaces the current entry with the given one, preserving the 575 /// access bits. replace(NamedDecl * D)576 void replace(NamedDecl *D) { 577 Results.Decls.replace(I-1, D); 578 Changed = true; 579 } 580 581 /// Replaces the current entry with the given one. replace(NamedDecl * D,AccessSpecifier AS)582 void replace(NamedDecl *D, AccessSpecifier AS) { 583 Results.Decls.replace(I-1, D, AS); 584 Changed = true; 585 } 586 done()587 void done() { 588 assert(!CalledDone && "done() called twice"); 589 CalledDone = true; 590 591 if (Changed) 592 Results.resolveKindAfterFilter(); 593 } 594 }; 595 596 /// Create a filter for this result set. makeFilter()597 Filter makeFilter() { 598 return Filter(*this); 599 } 600 601 private: diagnose()602 void diagnose() { 603 if (isAmbiguous()) 604 SemaRef.DiagnoseAmbiguousLookup(*this); 605 else if (isClassLookup() && SemaRef.getLangOpts().AccessControl) 606 SemaRef.CheckLookupAccess(*this); 607 } 608 setAmbiguous(AmbiguityKind AK)609 void setAmbiguous(AmbiguityKind AK) { 610 ResultKind = Ambiguous; 611 Ambiguity = AK; 612 } 613 614 void addDeclsFromBasePaths(const CXXBasePaths &P); 615 void configure(); 616 617 // Sanity checks. 618 void sanityImpl() const; 619 sanity()620 void sanity() const { 621 #ifndef NDEBUG 622 sanityImpl(); 623 #endif 624 } 625 sanityCheckUnresolved()626 bool sanityCheckUnresolved() const { 627 for (iterator I = begin(), E = end(); I != E; ++I) 628 if (isa<UnresolvedUsingValueDecl>((*I)->getUnderlyingDecl())) 629 return true; 630 return false; 631 } 632 633 static void deletePaths(CXXBasePaths *); 634 635 // Results. 636 LookupResultKind ResultKind; 637 AmbiguityKind Ambiguity; // ill-defined unless ambiguous 638 UnresolvedSet<8> Decls; 639 CXXBasePaths *Paths; 640 CXXRecordDecl *NamingClass; 641 QualType BaseObjectType; 642 643 // Parameters. 644 Sema &SemaRef; 645 DeclarationNameInfo NameInfo; 646 SourceRange NameContextRange; 647 Sema::LookupNameKind LookupKind; 648 unsigned IDNS; // set by configure() 649 650 bool Redecl; 651 652 /// \brief True if tag declarations should be hidden if non-tags 653 /// are present 654 bool HideTags; 655 656 bool Diagnose; 657 658 /// \brief True if we should allow hidden declarations to be 'visible'. 659 bool AllowHidden; 660 }; 661 662 /// \brief Consumes visible declarations found when searching for 663 /// all visible names within a given scope or context. 664 /// 665 /// This abstract class is meant to be subclassed by clients of \c 666 /// Sema::LookupVisibleDecls(), each of which should override the \c 667 /// FoundDecl() function to process declarations as they are found. 668 class VisibleDeclConsumer { 669 public: 670 /// \brief Destroys the visible declaration consumer. 671 virtual ~VisibleDeclConsumer(); 672 673 /// \brief Invoked each time \p Sema::LookupVisibleDecls() finds a 674 /// declaration visible from the current scope or context. 675 /// 676 /// \param ND the declaration found. 677 /// 678 /// \param Hiding a declaration that hides the declaration \p ND, 679 /// or NULL if no such declaration exists. 680 /// 681 /// \param Ctx the original context from which the lookup started. 682 /// 683 /// \param InBaseClass whether this declaration was found in base 684 /// class of the context we searched. 685 virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx, 686 bool InBaseClass) = 0; 687 }; 688 689 /// \brief A class for storing results from argument-dependent lookup. 690 class ADLResult { 691 private: 692 /// A map from canonical decls to the 'most recent' decl. 693 llvm::DenseMap<NamedDecl*, NamedDecl*> Decls; 694 695 public: 696 /// Adds a new ADL candidate to this map. 697 void insert(NamedDecl *D); 698 699 /// Removes any data associated with a given decl. erase(NamedDecl * D)700 void erase(NamedDecl *D) { 701 Decls.erase(cast<NamedDecl>(D->getCanonicalDecl())); 702 } 703 704 class iterator { 705 typedef llvm::DenseMap<NamedDecl*,NamedDecl*>::iterator inner_iterator; 706 inner_iterator iter; 707 708 friend class ADLResult; iterator(const inner_iterator & iter)709 iterator(const inner_iterator &iter) : iter(iter) {} 710 public: iterator()711 iterator() {} 712 713 iterator &operator++() { ++iter; return *this; } 714 iterator operator++(int) { return iterator(iter++); } 715 716 NamedDecl *operator*() const { return iter->second; } 717 718 bool operator==(const iterator &other) const { return iter == other.iter; } 719 bool operator!=(const iterator &other) const { return iter != other.iter; } 720 }; 721 begin()722 iterator begin() { return iterator(Decls.begin()); } end()723 iterator end() { return iterator(Decls.end()); } 724 }; 725 726 } 727 728 #endif 729