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:
David Goldman 2020-12-04 15:04:25 -05:00
parent 33e731e62d
commit 07c5a800dc
4 changed files with 152 additions and 1 deletions

View File

@ -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))

View File

@ -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);

View File

@ -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))

View File

@ -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.