blob: fb3529d5405cf954f155ad8f6456999df397cdb9 [file] [log] [blame]
//===--- 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"
using namespace clang;
using namespace idx;
namespace {
//===----------------------------------------------------------------------===//
// DeclEntityAnalyzer Implementation
//===----------------------------------------------------------------------===//
class 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 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 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 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);
}