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 "";
|
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) {
|
SymbolID getSymbolID(const Decl *D) {
|
||||||
llvm::SmallString<128> USR;
|
llvm::SmallString<128> USR;
|
||||||
if (index::generateUSRForDecl(D, USR))
|
if (index::generateUSRForDecl(D, USR))
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "index/SymbolID.h"
|
#include "index/SymbolID.h"
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
|
#include "clang/AST/DeclObjC.h"
|
||||||
#include "clang/AST/NestedNameSpecifier.h"
|
#include "clang/AST/NestedNameSpecifier.h"
|
||||||
#include "clang/Basic/SourceLocation.h"
|
#include "clang/Basic/SourceLocation.h"
|
||||||
#include "clang/Lex/MacroInfo.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.
|
/// string if decl is not a template specialization.
|
||||||
std::string printTemplateSpecializationArgs(const NamedDecl &ND);
|
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.
|
/// Gets the symbol ID for a declaration. Returned SymbolID might be null.
|
||||||
SymbolID getSymbolID(const Decl *D);
|
SymbolID getSymbolID(const Decl *D);
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclBase.h"
|
#include "clang/AST/DeclBase.h"
|
||||||
#include "clang/AST/DeclCXX.h"
|
#include "clang/AST/DeclCXX.h"
|
||||||
|
#include "clang/AST/DeclObjC.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
|
@ -64,6 +65,15 @@ PrintingPolicy getPrintingPolicy(PrintingPolicy Base) {
|
||||||
std::string getLocalScope(const Decl *D) {
|
std::string getLocalScope(const Decl *D) {
|
||||||
std::vector<std::string> Scopes;
|
std::vector<std::string> Scopes;
|
||||||
const DeclContext *DC = D->getDeclContext();
|
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) {
|
auto GetName = [](const TypeDecl *D) {
|
||||||
if (!D->getDeclName().isEmpty()) {
|
if (!D->getDeclName().isEmpty()) {
|
||||||
PrintingPolicy Policy = D->getASTContext().getPrintingPolicy();
|
PrintingPolicy Policy = D->getASTContext().getPrintingPolicy();
|
||||||
|
@ -90,6 +100,11 @@ std::string getLocalScope(const Decl *D) {
|
||||||
std::string getNamespaceScope(const Decl *D) {
|
std::string getNamespaceScope(const Decl *D) {
|
||||||
const DeclContext *DC = D->getDeclContext();
|
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))
|
if (const TagDecl *TD = dyn_cast<TagDecl>(DC))
|
||||||
return getNamespaceScope(TD);
|
return getNamespaceScope(TD);
|
||||||
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
|
||||||
|
|
|
@ -2167,7 +2167,8 @@ TEST(Hover, All) {
|
||||||
HI.Name = "data";
|
HI.Name = "data";
|
||||||
HI.Type = "char";
|
HI.Type = "char";
|
||||||
HI.Kind = index::SymbolKind::Field;
|
HI.Kind = index::SymbolKind::Field;
|
||||||
HI.NamespaceScope = "ObjC::"; // FIXME: fix it
|
HI.LocalScope = "ObjC::";
|
||||||
|
HI.NamespaceScope = "";
|
||||||
HI.Definition = "char data";
|
HI.Definition = "char data";
|
||||||
}},
|
}},
|
||||||
{
|
{
|
||||||
|
@ -2260,6 +2261,86 @@ TEST(Hover, All) {
|
||||||
HI.Name = "this";
|
HI.Name = "this";
|
||||||
HI.Definition = "const Foo<int, F> *";
|
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.
|
// Create a tiny index, so tests above can verify documentation is fetched.
|
||||||
|
|
Loading…
Reference in New Issue