forked from OSchip/llvm-project
Improve hover scopes for Objective-C code
- Instead of `AppDelegate::application:didFinishLaunchingWithOptions:` you will now see `-[AppDelegate application:didFinishLaunchingWithOptions:]` - Also include categories in the name when printing the scopes, e.g. `Class(Category)` and `-[Class(Category) method]` Differential Revision: https://reviews.llvm.org/D68590
This commit is contained in:
parent
33e731e62d
commit
07c5a800dc
|
@ -283,6 +283,52 @@ std::string printNamespaceScope(const DeclContext &DC) {
|
|||
return "";
|
||||
}
|
||||
|
||||
static llvm::StringRef
|
||||
getNameOrErrForObjCInterface(const ObjCInterfaceDecl *ID) {
|
||||
return ID ? ID->getName() : "<<error-type>>";
|
||||
}
|
||||
|
||||
std::string printObjCMethod(const ObjCMethodDecl &Method) {
|
||||
std::string Name;
|
||||
llvm::raw_string_ostream OS(Name);
|
||||
|
||||
OS << (Method.isInstanceMethod() ? '-' : '+') << '[';
|
||||
|
||||
// Should always be true.
|
||||
if (const ObjCContainerDecl *C =
|
||||
dyn_cast<ObjCContainerDecl>(Method.getDeclContext()))
|
||||
OS << printObjCContainer(*C);
|
||||
|
||||
Method.getSelector().print(OS << ' ');
|
||||
if (Method.isVariadic())
|
||||
OS << ", ...";
|
||||
|
||||
OS << ']';
|
||||
OS.flush();
|
||||
return Name;
|
||||
}
|
||||
|
||||
std::string printObjCContainer(const ObjCContainerDecl &C) {
|
||||
if (const ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(&C)) {
|
||||
std::string Name;
|
||||
llvm::raw_string_ostream OS(Name);
|
||||
const ObjCInterfaceDecl *Class = Category->getClassInterface();
|
||||
OS << getNameOrErrForObjCInterface(Class) << '(' << Category->getName()
|
||||
<< ')';
|
||||
OS.flush();
|
||||
return Name;
|
||||
}
|
||||
if (const ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(&C)) {
|
||||
std::string Name;
|
||||
llvm::raw_string_ostream OS(Name);
|
||||
const ObjCInterfaceDecl *Class = CID->getClassInterface();
|
||||
OS << getNameOrErrForObjCInterface(Class) << '(' << CID->getName() << ')';
|
||||
OS.flush();
|
||||
return Name;
|
||||
}
|
||||
return C.getNameAsString();
|
||||
}
|
||||
|
||||
SymbolID getSymbolID(const Decl *D) {
|
||||
llvm::SmallString<128> USR;
|
||||
if (index::generateUSRForDecl(D, USR))
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "index/SymbolID.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Lex/MacroInfo.h"
|
||||
|
@ -64,6 +65,14 @@ std::string printName(const ASTContext &Ctx, const NamedDecl &ND);
|
|||
/// string if decl is not a template specialization.
|
||||
std::string printTemplateSpecializationArgs(const NamedDecl &ND);
|
||||
|
||||
/// Print the Objective-C method name, including the full container name, e.g.
|
||||
/// `-[MyClass(Category) method:]`
|
||||
std::string printObjCMethod(const ObjCMethodDecl &Method);
|
||||
|
||||
/// Print the Objective-C container name including categories, e.g. `MyClass`,
|
||||
// `MyClass()`, `MyClass(Category)`, and `MyProtocol`.
|
||||
std::string printObjCContainer(const ObjCContainerDecl &C);
|
||||
|
||||
/// Gets the symbol ID for a declaration. Returned SymbolID might be null.
|
||||
SymbolID getSymbolID(const Decl *D);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
|
@ -64,6 +65,15 @@ PrintingPolicy getPrintingPolicy(PrintingPolicy Base) {
|
|||
std::string getLocalScope(const Decl *D) {
|
||||
std::vector<std::string> Scopes;
|
||||
const DeclContext *DC = D->getDeclContext();
|
||||
|
||||
// ObjC scopes won't have multiple components for us to join, instead:
|
||||
// - Methods: "-[Class methodParam1:methodParam2]"
|
||||
// - Classes, categories, and protocols: "MyClass(Category)"
|
||||
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC))
|
||||
return printObjCMethod(*MD);
|
||||
else if (const ObjCContainerDecl *CD = dyn_cast<ObjCContainerDecl>(DC))
|
||||
return printObjCContainer(*CD);
|
||||
|
||||
auto GetName = [](const TypeDecl *D) {
|
||||
if (!D->getDeclName().isEmpty()) {
|
||||
PrintingPolicy Policy = D->getASTContext().getPrintingPolicy();
|
||||
|
@ -90,6 +100,11 @@ std::string getLocalScope(const Decl *D) {
|
|||
std::string getNamespaceScope(const Decl *D) {
|
||||
const DeclContext *DC = D->getDeclContext();
|
||||
|
||||
// ObjC does not have the concept of namespaces, so instead we support
|
||||
// local scopes.
|
||||
if (isa<ObjCMethodDecl, ObjCContainerDecl>(DC))
|
||||
return "";
|
||||
|
||||
if (const TagDecl *TD = dyn_cast<TagDecl>(DC))
|
||||
return getNamespaceScope(TD);
|
||||
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
|
||||
|
|
|
@ -2167,7 +2167,8 @@ TEST(Hover, All) {
|
|||
HI.Name = "data";
|
||||
HI.Type = "char";
|
||||
HI.Kind = index::SymbolKind::Field;
|
||||
HI.NamespaceScope = "ObjC::"; // FIXME: fix it
|
||||
HI.LocalScope = "ObjC::";
|
||||
HI.NamespaceScope = "";
|
||||
HI.Definition = "char data";
|
||||
}},
|
||||
{
|
||||
|
@ -2260,6 +2261,86 @@ TEST(Hover, All) {
|
|||
HI.Name = "this";
|
||||
HI.Definition = "const Foo<int, F> *";
|
||||
}},
|
||||
{
|
||||
R"cpp(
|
||||
@interface MYObject
|
||||
@end
|
||||
@interface MYObject (Private)
|
||||
@property(nonatomic, assign) int privateField;
|
||||
@end
|
||||
|
||||
int someFunction() {
|
||||
MYObject *obj = [MYObject sharedInstance];
|
||||
return obj.[[private^Field]];
|
||||
}
|
||||
)cpp",
|
||||
[](HoverInfo &HI) {
|
||||
HI.Name = "privateField";
|
||||
HI.Kind = index::SymbolKind::InstanceProperty;
|
||||
HI.LocalScope = "MYObject(Private)::";
|
||||
HI.NamespaceScope = "";
|
||||
HI.Definition = "@property(nonatomic, assign, unsafe_unretained, "
|
||||
"readwrite) int privateField;";
|
||||
}},
|
||||
{
|
||||
R"cpp(
|
||||
@protocol MYProtocol
|
||||
@property(nonatomic, assign) int prop1;
|
||||
@end
|
||||
|
||||
int someFunction() {
|
||||
id<MYProtocol> obj = 0;
|
||||
return obj.[[pro^p1]];
|
||||
}
|
||||
)cpp",
|
||||
[](HoverInfo &HI) {
|
||||
HI.Name = "prop1";
|
||||
HI.Kind = index::SymbolKind::InstanceProperty;
|
||||
HI.LocalScope = "MYProtocol::";
|
||||
HI.NamespaceScope = "";
|
||||
HI.Definition = "@property(nonatomic, assign, unsafe_unretained, "
|
||||
"readwrite) int prop1;";
|
||||
}},
|
||||
{R"objc(
|
||||
@interface Foo
|
||||
@end
|
||||
|
||||
@implementation Foo(Private)
|
||||
+ (int)somePrivateMethod {
|
||||
int [[res^ult]] = 2;
|
||||
return result;
|
||||
}
|
||||
@end
|
||||
)objc",
|
||||
[](HoverInfo &HI) {
|
||||
HI.Name = "result";
|
||||
HI.Definition = "int result = 2";
|
||||
HI.Kind = index::SymbolKind::Variable;
|
||||
HI.Type = "int";
|
||||
HI.LocalScope = "+[Foo(Private) somePrivateMethod]::";
|
||||
HI.NamespaceScope = "";
|
||||
HI.Value = "2";
|
||||
}},
|
||||
{R"objc(
|
||||
@interface Foo
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
- (int)variadicArgMethod:(id)first, ... {
|
||||
int [[res^ult]] = 0;
|
||||
return result;
|
||||
}
|
||||
@end
|
||||
)objc",
|
||||
[](HoverInfo &HI) {
|
||||
HI.Name = "result";
|
||||
HI.Definition = "int result = 0";
|
||||
HI.Kind = index::SymbolKind::Variable;
|
||||
HI.Type = "int";
|
||||
HI.LocalScope = "-[Foo variadicArgMethod:, ...]::";
|
||||
HI.NamespaceScope = "";
|
||||
HI.Value = "0";
|
||||
}},
|
||||
};
|
||||
|
||||
// Create a tiny index, so tests above can verify documentation is fetched.
|
||||
|
|
Loading…
Reference in New Issue