| //===--- Analyzer.cpp - Analysis for indexing information -------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the Analyzer interface. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Index/Analyzer.h" |
| #include "clang/Index/Entity.h" |
| #include "clang/Index/TranslationUnit.h" |
| #include "clang/Index/Handlers.h" |
| #include "clang/Index/ASTLocation.h" |
| #include "clang/Index/GlobalSelector.h" |
| #include "clang/Index/DeclReferenceMap.h" |
| #include "clang/Index/SelectorMap.h" |
| #include "clang/Index/IndexProvider.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "llvm/ADT/SmallSet.h" |
| #include "llvm/Support/Compiler.h" |
| using namespace clang; |
| using namespace idx; |
| |
| namespace { |
| |
| //===----------------------------------------------------------------------===// |
| // DeclEntityAnalyzer Implementation |
| //===----------------------------------------------------------------------===// |
| |
| class VISIBILITY_HIDDEN DeclEntityAnalyzer : public TranslationUnitHandler { |
| Entity Ent; |
| TULocationHandler &TULocHandler; |
| |
| public: |
| DeclEntityAnalyzer(Entity ent, TULocationHandler &handler) |
| : Ent(ent), TULocHandler(handler) { } |
| |
| virtual void Handle(TranslationUnit *TU) { |
| assert(TU && "Passed null translation unit"); |
| |
| Decl *D = Ent.getDecl(TU->getASTContext()); |
| assert(D && "Couldn't resolve Entity"); |
| |
| for (Decl::redecl_iterator I = D->redecls_begin(), |
| E = D->redecls_end(); I != E; ++I) |
| TULocHandler.Handle(TULocation(TU, ASTLocation(*I))); |
| } |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // RefEntityAnalyzer Implementation |
| //===----------------------------------------------------------------------===// |
| |
| class VISIBILITY_HIDDEN RefEntityAnalyzer : public TranslationUnitHandler { |
| Entity Ent; |
| TULocationHandler &TULocHandler; |
| |
| public: |
| RefEntityAnalyzer(Entity ent, TULocationHandler &handler) |
| : Ent(ent), TULocHandler(handler) { } |
| |
| virtual void Handle(TranslationUnit *TU) { |
| assert(TU && "Passed null translation unit"); |
| |
| Decl *D = Ent.getDecl(TU->getASTContext()); |
| assert(D && "Couldn't resolve Entity"); |
| NamedDecl *ND = dyn_cast<NamedDecl>(D); |
| if (!ND) |
| return; |
| |
| DeclReferenceMap &RefMap = TU->getDeclReferenceMap(); |
| for (DeclReferenceMap::astlocation_iterator |
| I = RefMap.refs_begin(ND), E = RefMap.refs_end(ND); I != E; ++I) |
| TULocHandler.Handle(TULocation(TU, *I)); |
| } |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // RefSelectorAnalyzer Implementation |
| //===----------------------------------------------------------------------===// |
| |
| /// \brief Accepts an ObjC method and finds all message expressions that this |
| /// method may respond to. |
| class VISIBILITY_HIDDEN RefSelectorAnalyzer : public TranslationUnitHandler { |
| Program &Prog; |
| TULocationHandler &TULocHandler; |
| |
| // The original ObjCInterface associated with the method. |
| Entity IFaceEnt; |
| GlobalSelector GlobSel; |
| bool IsInstanceMethod; |
| |
| /// \brief Super classes of the ObjCInterface. |
| typedef llvm::SmallSet<Entity, 16> EntitiesSetTy; |
| EntitiesSetTy HierarchyEntities; |
| |
| public: |
| RefSelectorAnalyzer(ObjCMethodDecl *MD, |
| Program &prog, TULocationHandler &handler) |
| : Prog(prog), TULocHandler(handler) { |
| assert(MD); |
| |
| // FIXME: Protocol methods. |
| assert(!isa<ObjCProtocolDecl>(MD->getDeclContext()) && |
| "Protocol methods not supported yet"); |
| |
| ObjCInterfaceDecl *IFD = MD->getClassInterface(); |
| assert(IFD); |
| IFaceEnt = Entity::get(IFD, Prog); |
| GlobSel = GlobalSelector::get(MD->getSelector(), Prog); |
| IsInstanceMethod = MD->isInstanceMethod(); |
| |
| for (ObjCInterfaceDecl *Cls = IFD->getSuperClass(); |
| Cls; Cls = Cls->getSuperClass()) |
| HierarchyEntities.insert(Entity::get(Cls, Prog)); |
| } |
| |
| virtual void Handle(TranslationUnit *TU) { |
| assert(TU && "Passed null translation unit"); |
| |
| ASTContext &Ctx = TU->getASTContext(); |
| // Null means it doesn't exist in this translation unit. |
| ObjCInterfaceDecl *IFace = |
| cast_or_null<ObjCInterfaceDecl>(IFaceEnt.getDecl(Ctx)); |
| Selector Sel = GlobSel.getSelector(Ctx); |
| |
| SelectorMap &SelMap = TU->getSelectorMap(); |
| for (SelectorMap::astlocation_iterator |
| I = SelMap.refs_begin(Sel), E = SelMap.refs_end(Sel); I != E; ++I) { |
| if (ValidReference(*I, IFace)) |
| TULocHandler.Handle(TULocation(TU, *I)); |
| } |
| } |
| |
| /// \brief Determines whether the given message expression is likely to end |
| /// up at the given interface decl. |
| /// |
| /// It returns true "eagerly", meaning it will return false only if it can |
| /// "prove" statically that the interface cannot accept this message. |
| bool ValidReference(ASTLocation ASTLoc, ObjCInterfaceDecl *IFace) { |
| assert(ASTLoc.isStmt()); |
| |
| // FIXME: Finding @selector references should be through another Analyzer |
| // method, like FindSelectors. |
| if (isa<ObjCSelectorExpr>(ASTLoc.AsStmt())) |
| return false; |
| |
| ObjCInterfaceDecl *MsgD = 0; |
| ObjCMessageExpr *Msg = cast<ObjCMessageExpr>(ASTLoc.AsStmt()); |
| |
| if (Msg->getReceiver()) { |
| const ObjCObjectPointerType *OPT = |
| Msg->getReceiver()->getType()->getAsObjCInterfacePointerType(); |
| |
| // Can be anything! Accept it as a possibility.. |
| if (!OPT || OPT->isObjCIdType() || OPT->isObjCQualifiedIdType()) |
| return true; |
| |
| // Expecting class method. |
| if (OPT->isObjCClassType() || OPT->isObjCQualifiedClassType()) |
| return !IsInstanceMethod; |
| |
| MsgD = OPT->getInterfaceDecl(); |
| assert(MsgD); |
| |
| // Should be an instance method. |
| if (!IsInstanceMethod) |
| return false; |
| |
| } else { |
| // Expecting class method. |
| if (IsInstanceMethod) |
| return false; |
| |
| MsgD = Msg->getClassInfo().first; |
| // FIXME: Case when we only have an identifier. |
| assert(MsgD && "Identifier only"); |
| } |
| |
| assert(MsgD); |
| |
| // Same interface ? We have a winner! |
| if (MsgD == IFace) |
| return true; |
| |
| // If the message interface is a superclass of the original interface, |
| // accept this message as a possibility. |
| if (HierarchyEntities.count(Entity::get(MsgD, Prog))) |
| return true; |
| |
| // If the message interface is a subclass of the original interface, accept |
| // the message unless there is a subclass in the hierarchy that will |
| // "steal" the message (thus the message "will go" to the subclass and not |
| /// the original interface). |
| if (IFace) { |
| Selector Sel = Msg->getSelector(); |
| for (ObjCInterfaceDecl *Cls = MsgD; Cls; Cls = Cls->getSuperClass()) { |
| if (Cls == IFace) |
| return true; |
| if (Cls->getMethod(Sel, IsInstanceMethod)) |
| return false; |
| } |
| } |
| |
| // The interfaces are unrelated, don't accept the message. |
| return false; |
| } |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // MessageAnalyzer Implementation |
| //===----------------------------------------------------------------------===// |
| |
| /// \brief Accepts an ObjC message expression and finds all methods that may |
| /// respond to it. |
| class VISIBILITY_HIDDEN MessageAnalyzer : public TranslationUnitHandler { |
| Program &Prog; |
| TULocationHandler &TULocHandler; |
| |
| // The ObjCInterface associated with the message. Can be null/invalid. |
| Entity MsgIFaceEnt; |
| GlobalSelector GlobSel; |
| bool CanBeInstanceMethod; |
| bool CanBeClassMethod; |
| |
| /// \brief Super classes of the ObjCInterface. |
| typedef llvm::SmallSet<Entity, 16> EntitiesSetTy; |
| EntitiesSetTy HierarchyEntities; |
| |
| /// \brief The interface in the message interface hierarchy that "intercepts" |
| /// the selector. |
| Entity ReceiverIFaceEnt; |
| |
| public: |
| MessageAnalyzer(ObjCMessageExpr *Msg, |
| Program &prog, TULocationHandler &handler) |
| : Prog(prog), TULocHandler(handler), |
| CanBeInstanceMethod(false), |
| CanBeClassMethod(false) { |
| |
| assert(Msg); |
| |
| ObjCInterfaceDecl *MsgD = 0; |
| |
| while (true) { |
| if (Msg->getReceiver() == 0) { |
| CanBeClassMethod = true; |
| MsgD = Msg->getClassInfo().first; |
| // FIXME: Case when we only have an identifier. |
| assert(MsgD && "Identifier only"); |
| break; |
| } |
| |
| const ObjCObjectPointerType *OPT = |
| Msg->getReceiver()->getType()->getAsObjCInterfacePointerType(); |
| |
| if (!OPT || OPT->isObjCIdType() || OPT->isObjCQualifiedIdType()) { |
| CanBeInstanceMethod = CanBeClassMethod = true; |
| break; |
| } |
| |
| if (OPT->isObjCClassType() || OPT->isObjCQualifiedClassType()) { |
| CanBeClassMethod = true; |
| break; |
| } |
| |
| MsgD = OPT->getInterfaceDecl(); |
| assert(MsgD); |
| CanBeInstanceMethod = true; |
| break; |
| } |
| |
| assert(CanBeInstanceMethod || CanBeClassMethod); |
| |
| Selector sel = Msg->getSelector(); |
| assert(!sel.isNull()); |
| |
| MsgIFaceEnt = Entity::get(MsgD, Prog); |
| GlobSel = GlobalSelector::get(sel, Prog); |
| |
| if (MsgD) { |
| for (ObjCInterfaceDecl *Cls = MsgD->getSuperClass(); |
| Cls; Cls = Cls->getSuperClass()) |
| HierarchyEntities.insert(Entity::get(Cls, Prog)); |
| |
| // Find the interface in the hierarchy that "receives" the message. |
| for (ObjCInterfaceDecl *Cls = MsgD; Cls; Cls = Cls->getSuperClass()) { |
| bool isReceiver = false; |
| |
| ObjCInterfaceDecl::lookup_const_iterator Meth, MethEnd; |
| for (llvm::tie(Meth, MethEnd) = Cls->lookup(sel); |
| Meth != MethEnd; ++Meth) { |
| if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(*Meth)) |
| if ((MD->isInstanceMethod() && CanBeInstanceMethod) || |
| (MD->isClassMethod() && CanBeClassMethod)) { |
| isReceiver = true; |
| break; |
| } |
| } |
| |
| if (isReceiver) { |
| ReceiverIFaceEnt = Entity::get(Cls, Prog); |
| break; |
| } |
| } |
| } |
| } |
| |
| virtual void Handle(TranslationUnit *TU) { |
| assert(TU && "Passed null translation unit"); |
| ASTContext &Ctx = TU->getASTContext(); |
| |
| // Null means it doesn't exist in this translation unit or there was no |
| // interface that was determined to receive the original message. |
| ObjCInterfaceDecl *ReceiverIFace = |
| cast_or_null<ObjCInterfaceDecl>(ReceiverIFaceEnt.getDecl(Ctx)); |
| |
| // No subclass for the original receiver interface, so it remains the |
| // receiver. |
| if (ReceiverIFaceEnt.isValid() && ReceiverIFace == 0) |
| return; |
| |
| // Null means it doesn't exist in this translation unit or there was no |
| // interface associated with the message in the first place. |
| ObjCInterfaceDecl *MsgIFace = |
| cast_or_null<ObjCInterfaceDecl>(MsgIFaceEnt.getDecl(Ctx)); |
| |
| Selector Sel = GlobSel.getSelector(Ctx); |
| SelectorMap &SelMap = TU->getSelectorMap(); |
| for (SelectorMap::method_iterator |
| I = SelMap.methods_begin(Sel), E = SelMap.methods_end(Sel); |
| I != E; ++I) { |
| ObjCMethodDecl *D = *I; |
| if (ValidMethod(D, MsgIFace, ReceiverIFace)) { |
| for (ObjCMethodDecl::redecl_iterator |
| RI = D->redecls_begin(), RE = D->redecls_end(); RI != RE; ++RI) |
| TULocHandler.Handle(TULocation(TU, ASTLocation(*RI))); |
| } |
| } |
| } |
| |
| /// \brief Determines whether the given method is likely to accept the |
| /// original message. |
| /// |
| /// It returns true "eagerly", meaning it will return false only if it can |
| /// "prove" statically that the method cannot accept the original message. |
| bool ValidMethod(ObjCMethodDecl *D, ObjCInterfaceDecl *MsgIFace, |
| ObjCInterfaceDecl *ReceiverIFace) { |
| assert(D); |
| |
| // FIXME: Protocol methods ? |
| if (isa<ObjCProtocolDecl>(D->getDeclContext())) |
| return false; |
| |
| // No specific interface associated with the message. Can be anything. |
| if (MsgIFaceEnt.isInvalid()) |
| return true; |
| |
| if ((!CanBeInstanceMethod && D->isInstanceMethod()) || |
| (!CanBeClassMethod && D->isClassMethod())) |
| return false; |
| |
| ObjCInterfaceDecl *IFace = D->getClassInterface(); |
| assert(IFace); |
| |
| // If the original message interface is the same or a superclass of the |
| // given interface, accept the method as a possibility. |
| if (MsgIFace && MsgIFace->isSuperClassOf(IFace)) |
| return true; |
| |
| if (ReceiverIFace) { |
| // The given interface, "overrides" the receiver. |
| if (ReceiverIFace->isSuperClassOf(IFace)) |
| return true; |
| } else { |
| // No receiver was found for the original message. |
| assert(ReceiverIFaceEnt.isInvalid()); |
| |
| // If the original message interface is a subclass of the given interface, |
| // accept the message. |
| if (HierarchyEntities.count(Entity::get(IFace, Prog))) |
| return true; |
| } |
| |
| // The interfaces are unrelated, or the receiver interface wasn't |
| // "overriden". |
| return false; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| //===----------------------------------------------------------------------===// |
| // Analyzer Implementation |
| //===----------------------------------------------------------------------===// |
| |
| void Analyzer::FindDeclarations(Decl *D, TULocationHandler &Handler) { |
| assert(D && "Passed null declaration"); |
| Entity Ent = Entity::get(D, Prog); |
| if (Ent.isInvalid()) |
| return; |
| |
| DeclEntityAnalyzer DEA(Ent, Handler); |
| Idxer.GetTranslationUnitsFor(Ent, DEA); |
| } |
| |
| void Analyzer::FindReferences(Decl *D, TULocationHandler &Handler) { |
| assert(D && "Passed null declaration"); |
| if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { |
| RefSelectorAnalyzer RSA(MD, Prog, Handler); |
| GlobalSelector Sel = GlobalSelector::get(MD->getSelector(), Prog); |
| Idxer.GetTranslationUnitsFor(Sel, RSA); |
| return; |
| } |
| |
| Entity Ent = Entity::get(D, Prog); |
| if (Ent.isInvalid()) |
| return; |
| |
| RefEntityAnalyzer REA(Ent, Handler); |
| Idxer.GetTranslationUnitsFor(Ent, REA); |
| } |
| |
| /// \brief Find methods that may respond to the given message and pass them |
| /// to Handler. |
| void Analyzer::FindObjCMethods(ObjCMessageExpr *Msg, |
| TULocationHandler &Handler) { |
| assert(Msg); |
| MessageAnalyzer MsgAnalyz(Msg, Prog, Handler); |
| GlobalSelector GlobSel = GlobalSelector::get(Msg->getSelector(), Prog); |
| Idxer.GetTranslationUnitsFor(GlobSel, MsgAnalyz); |
| } |