summaryrefslogtreecommitdiffstats
path: root/tools/CIndex/CIndexUSRs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/CIndex/CIndexUSRs.cpp')
-rw-r--r--tools/CIndex/CIndexUSRs.cpp211
1 files changed, 176 insertions, 35 deletions
diff --git a/tools/CIndex/CIndexUSRs.cpp b/tools/CIndex/CIndexUSRs.cpp
index 379320c..58870b9 100644
--- a/tools/CIndex/CIndexUSRs.cpp
+++ b/tools/CIndex/CIndexUSRs.cpp
@@ -14,9 +14,12 @@
#include "CIndexer.h"
#include "CXCursor.h"
#include "clang/AST/DeclVisitor.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Lex/PreprocessingRecord.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+using namespace clang;
using namespace clang::cxstring;
//===----------------------------------------------------------------------===//
@@ -27,8 +30,10 @@ namespace {
class USRGenerator : public DeclVisitor<USRGenerator> {
llvm::raw_ostream &Out;
bool IgnoreResults;
+ ASTUnit *AU;
public:
- USRGenerator(llvm::raw_ostream &out) : Out(out), IgnoreResults(false) {}
+ USRGenerator(ASTUnit *au, llvm::raw_ostream &out)
+ : Out(out), IgnoreResults(false), AU(au) {}
bool ignoreResults() const { return IgnoreResults; }
@@ -39,12 +44,19 @@ public:
void VisitFunctionDecl(FunctionDecl *D);
void VisitNamedDecl(NamedDecl *D);
void VisitNamespaceDecl(NamespaceDecl *D);
+ void VisitObjCClassDecl(ObjCClassDecl *CD);
void VisitObjCContainerDecl(ObjCContainerDecl *CD);
+ void VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *P);
void VisitObjCMethodDecl(ObjCMethodDecl *MD);
void VisitObjCPropertyDecl(ObjCPropertyDecl *D);
+ void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D);
void VisitTagDecl(TagDecl *D);
void VisitTypedefDecl(TypedefDecl *D);
+ void VisitVarDecl(VarDecl *D);
+ /// Generate the string component containing the location of the
+ /// declaration.
+ void GenLoc(const Decl *D);
/// String generation methods used both by the visitation methods
/// and from other clients that want to directly generate USRs. These
@@ -78,10 +90,10 @@ private:
llvm::raw_svector_ostream Out;
USRGenerator UG;
public:
- StringUSRGenerator()
- : Out(StrBuf), UG(Out) {
+ StringUSRGenerator(const CXCursor *C = 0)
+ : Out(StrBuf), UG(C ? cxcursor::getCursorASTUnit(*C) : 0, Out) {
// Add the USR space prefix.
- Out << "c:";
+ Out << "c:";
}
llvm::StringRef str() {
@@ -106,7 +118,7 @@ public:
void USRGenerator::VisitBlockDecl(BlockDecl *D) {
VisitDeclContext(D->getDeclContext());
// FIXME: Better support for anonymous blocks.
- Out << "@B^anon";
+ Out << "@B@anon";
}
void USRGenerator::VisitDeclContext(DeclContext *DC) {
@@ -122,24 +134,48 @@ void USRGenerator::VisitFieldDecl(FieldDecl *D) {
return;
}
VisitDeclContext(D->getDeclContext());
- Out << "@^FI^" << s;
+ Out << (isa<ObjCIvarDecl>(D) ? "@" : "@FI@") << s;
}
void USRGenerator::VisitFunctionDecl(FunctionDecl *D) {
VisitDeclContext(D->getDeclContext());
- Out << "@F^" << D->getNameAsString();
+ Out << "@F@" << D;
}
void USRGenerator::VisitNamedDecl(NamedDecl *D) {
VisitDeclContext(D->getDeclContext());
const std::string &s = D->getNameAsString();
- // assert(!s.empty());
- GenNamedDecl(s);
+ // The string can be empty if the declaration has no name; e.g., it is
+ // the ParmDecl with no name for declaration of a function pointer type, e.g.:
+ // void (*f)(void *);
+ // In this case, don't generate a USR.
+ if (s.empty())
+ IgnoreResults = true;
+ else
+ GenNamedDecl(s);
+}
+
+void USRGenerator::VisitVarDecl(VarDecl *D) {
+ // VarDecls can be declared 'extern' within a function or method body,
+ // but their enclosing DeclContext is the function, not the TU. We need
+ // to check the storage class to correctly generate the USR.
+ if (!D->hasExternalStorage())
+ VisitDeclContext(D->getDeclContext());
+
+ const std::string &s = D->getNameAsString();
+ // The string can be empty if the declaration has no name; e.g., it is
+ // the ParmDecl with no name for declaration of a function pointer type, e.g.:
+ // void (*f)(void *);
+ // In this case, don't generate a USR.
+ if (s.empty())
+ IgnoreResults = true;
+ else
+ GenNamedDecl(s);
}
void USRGenerator::VisitNamespaceDecl(NamespaceDecl *D) {
VisitDeclContext(D->getDeclContext());
- Out << "@N^" << D->getNameAsString();
+ Out << "@N@" << D;
}
void USRGenerator::VisitObjCMethodDecl(ObjCMethodDecl *D) {
@@ -148,6 +184,18 @@ void USRGenerator::VisitObjCMethodDecl(ObjCMethodDecl *D) {
D->isInstanceMethod());
}
+void USRGenerator::VisitObjCClassDecl(ObjCClassDecl *D) {
+ // FIXME: @class declarations can refer to multiple classes. We need
+ // to be able to traverse these.
+ IgnoreResults = true;
+}
+
+void USRGenerator::VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *D) {
+ // FIXME: @protocol declarations can refer to multiple protocols. We need
+ // to be able to traverse these.
+ IgnoreResults = true;
+}
+
void USRGenerator::VisitObjCContainerDecl(ObjCContainerDecl *D) {
switch (D->getKind()) {
default:
@@ -195,32 +243,85 @@ void USRGenerator::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
GenObjCProperty(D->getName());
}
+void USRGenerator::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
+ if (ObjCPropertyDecl *PD = D->getPropertyDecl()) {
+ VisitObjCPropertyDecl(PD);
+ return;
+ }
+
+ IgnoreResults = true;
+}
+
void USRGenerator::VisitTagDecl(TagDecl *D) {
+ D = D->getCanonicalDecl();
VisitDeclContext(D->getDeclContext());
switch (D->getTagKind()) {
- case TagDecl::TK_struct: Out << "@S^"; break;
- case TagDecl::TK_class: Out << "@C^"; break;
- case TagDecl::TK_union: Out << "@U^"; break;
- case TagDecl::TK_enum: Out << "@E^"; break;
+ case TagDecl::TK_struct: Out << "@S"; break;
+ case TagDecl::TK_class: Out << "@C"; break;
+ case TagDecl::TK_union: Out << "@U"; break;
+ case TagDecl::TK_enum: Out << "@E"; break;
}
- // FIXME: Better support for anonymous structures and enums.
const std::string &s = D->getNameAsString();
+ const TypedefDecl *TD = 0;
if (s.empty()) {
- if (TypedefDecl *TD = D->getTypedefForAnonDecl())
- Out << "^anontd^" << TD->getNameAsString();
- else
- Out << "^anon";
+ TD = D->getTypedefForAnonDecl();
+ Out << (TD ? 'A' : 'a');
+ }
+
+ // Add the location of the tag decl to handle resolution across
+ // translation units.
+ if (D->getLinkage() == NoLinkage) {
+ Out << '@';
+ GenLoc(D);
+ if (IgnoreResults)
+ return;
+ }
+
+ if (s.empty()) {
+ if (TD)
+ Out << '@' << TD;
}
else
- Out << s;
+ Out << '@' << s;
}
void USRGenerator::VisitTypedefDecl(TypedefDecl *D) {
DeclContext *DC = D->getDeclContext();
if (NamedDecl *DCN = dyn_cast<NamedDecl>(DC))
Visit(DCN);
- Out << "typedef@" << D->getName();
+ Out << "@T@";
+ if (D->getLinkage() == NoLinkage) {
+ GenLoc(D);
+ if (IgnoreResults)
+ return;
+ Out << '@';
+ }
+ Out << D->getName();
+}
+
+void USRGenerator::GenLoc(const Decl *D) {
+ const SourceManager &SM = AU->getSourceManager();
+ SourceLocation L = D->getLocStart();
+ if (L.isInvalid()) {
+ IgnoreResults = true;
+ return;
+ }
+ L = SM.getInstantiationLoc(L);
+ const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(L);
+ const FileEntry *FE = SM.getFileEntryForID(Decomposed.first);
+ if (FE) {
+ llvm::sys::Path P(FE->getName());
+ Out << P.getLast();
+ }
+ else {
+ // This case really isn't interesting.
+ IgnoreResults = true;
+ return;
+ }
+ Out << '@'
+ << SM.getLineNumber(Decomposed.first, Decomposed.second) << ':'
+ << SM.getColumnNumber(Decomposed.first, Decomposed.second);
}
//===----------------------------------------------------------------------===//
@@ -228,7 +329,7 @@ void USRGenerator::VisitTypedefDecl(TypedefDecl *D) {
//===----------------------------------------------------------------------===//
void USRGenerator::GenNamedDecl(llvm::StringRef name) {
- Out << "@^" << name;
+ Out << "@" << name;
}
void USRGenerator::GenObjCClass(llvm::StringRef cls) {
@@ -236,7 +337,7 @@ void USRGenerator::GenObjCClass(llvm::StringRef cls) {
}
void USRGenerator::GenObjCCategory(llvm::StringRef cls, llvm::StringRef cat) {
- Out << "objc(cy)" << cls << '^' << cat;
+ Out << "objc(cy)" << cls << '@' << cat;
}
void USRGenerator::GenObjCIvar(llvm::StringRef ivar) {
@@ -259,29 +360,69 @@ void USRGenerator::GenObjCProtocol(llvm::StringRef prot) {
// API hooks.
//===----------------------------------------------------------------------===//
-extern "C" {
+static inline llvm::StringRef extractUSRSuffix(llvm::StringRef s) {
+ return s.startswith("c:") ? s.substr(2) : "";
+}
-CXString clang_getCursorUSR(CXCursor C) {
+static CXString getDeclCursorUSR(const CXCursor &C) {
Decl *D = cxcursor::getCursorDecl(C);
- if (!D)
- return createCXString(NULL);
- StringUSRGenerator SUG;
- SUG->Visit(static_cast<Decl*>(D));
+ // Don't generate USRs for things with invalid locations.
+ if (!D || D->getLocStart().isInvalid())
+ return createCXString("");
+
+ // Check if the cursor has 'NoLinkage'.
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
+ switch (ND->getLinkage()) {
+ case ExternalLinkage:
+ // Generate USRs for all entities with external linkage.
+ break;
+ case NoLinkage:
+ // We allow enums, typedefs, and structs that have no linkage to
+ // have USRs that are anchored to the file they were defined in
+ // (e.g., the header). This is a little gross, but in principal
+ // enums/anonymous structs/etc. defined in a common header file
+ // are referred to across multiple translation units.
+ if (isa<TagDecl>(ND) || isa<TypedefDecl>(ND) ||
+ isa<EnumConstantDecl>(ND) || isa<FieldDecl>(ND))
+ break;
+ // Fall-through.
+ case InternalLinkage:
+ case UniqueExternalLinkage:
+ return createCXString("");
+ }
+
+ StringUSRGenerator SUG(&C);
+ SUG->Visit(D);
if (SUG->ignoreResults())
return createCXString("");
- // Return a copy of the string that must be disposed by the caller.
+ // For development testing.
+ // assert(SUG.str().size() > 2);
+
+ // Return a copy of the string that must be disposed by the caller.
return createCXString(SUG.str(), true);
}
-static inline llvm::StringRef extractUSRSuffix(llvm::StringRef s) {
- if (!(s.size() >= 2 && s[0] == 'c' && s[1] == ':'))
- return "";
- return s.substr(2);
+extern "C" {
+
+CXString clang_getCursorUSR(CXCursor C) {
+ const CXCursorKind &K = clang_getCursorKind(C);
+
+ if (clang_isDeclaration(K))
+ return getDeclCursorUSR(C);
+
+ if (K == CXCursor_MacroDefinition) {
+ StringUSRGenerator SUG(&C);
+ SUG << "macro@"
+ << cxcursor::getCursorMacroDefinition(C)->getName()->getNameStart();
+ return createCXString(SUG.str(), true);
+ }
+
+ return createCXString("");
}
-
+
CXString clang_constructUSR_ObjCIvar(const char *name, CXString classUSR) {
StringUSRGenerator SUG;
SUG << extractUSRSuffix(clang_getCString(classUSR));
OpenPOWER on IntegriCloud