Skip to content

[SourceKit] For clang enums imported as error domains, make sure to use unique USRs. #3834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion lib/AST/USRGeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/PreprocessingRecord.h"
#include "clang/Lex/Preprocessor.h"
Expand Down Expand Up @@ -55,7 +56,39 @@ bool ide::printDeclUSR(const ValueDecl *D, raw_ostream &OS) {

ValueDecl *VD = const_cast<ValueDecl *>(D);

if (ClangNode ClangN = VD->getClangNode()) {
auto interpretAsClangNode = [](const ValueDecl *D)->ClangNode {
ClangNode ClangN = D->getClangNode();
if (auto ClangD = ClangN.getAsDecl()) {
// NSErrorDomain causes the clang enum to be imported like this:
//
// struct MyError {
// enum Code : Int32 {
// case errFirst
// case errSecond
// }
// static var errFirst: MyError.Code { get }
// static var errSecond: MyError.Code { get }
// }
//
// The clang enum and enum constants are associated with both the
// struct/nested enum, and the static vars/enum cases.
// But we want unique USRs for the above symbols, so use the clang USR
// for the enum and enum cases, and the Swift USR for the struct and vars.
//
if (isa<clang::EnumDecl>(ClangD)) {
if (ClangD->hasAttr<clang::NSErrorDomainAttr>() && isa<StructDecl>(D))
return ClangNode();
} else if (auto *ClangEnumConst = dyn_cast<clang::EnumConstantDecl>(ClangD)) {
if (auto *ClangEnum = dyn_cast<clang::EnumDecl>(ClangEnumConst->getDeclContext())) {
if (ClangEnum->hasAttr<clang::NSErrorDomainAttr>() && isa<VarDecl>(D))
return ClangNode();
}
}
}
return ClangN;
};

if (ClangNode ClangN = interpretAsClangNode(D)) {
llvm::SmallString<128> Buf;
if (auto ClangD = ClangN.getAsDecl()) {
bool Ignore = clang::index::generateUSRForDecl(ClangD, Buf);
Expand Down
14 changes: 14 additions & 0 deletions test/SourceKit/DocSupport/Inputs/MyError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@import Foundation;

#define NS_ERROR_ENUM(_type, _name, _domain) \
enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type

@class NSString;
extern const NSString *const MyErrorDomain;
/// This is my cool error code.
typedef NS_ERROR_ENUM(int, MyErrorCode, MyErrorDomain) {
/// This is first error.
MyErrFirst,
/// This is second error.
MyErrSecond,
};
4 changes: 4 additions & 0 deletions test/SourceKit/DocSupport/Inputs/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module "MyError" {
header "MyError.h"
export *
}
32 changes: 32 additions & 0 deletions test/SourceKit/DocSupport/doc_error_domain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// REQUIRES: OS=macosx
// RUN: %sourcekitd-test -req=doc-info -module MyError -- -I %S/Inputs \
// RUN: %mcp_opt -sdk %sdk | %sed_clean > %t.response
// RUN: FileCheck -input-file=%t.response %s

// CHECK: struct MyError {
// CHECK: enum Code : Int32 {
// CHECK: case errFirst
// CHECK: case errSecond
// CHECK: }
// CHECK: static var errFirst: MyError.Code { get }
// CHECK: static var errSecond: MyError.Code { get }

// CHECK: key.kind: source.lang.swift.decl.struct,
// CHECK-NEXT: key.name: "MyError",
// CHECK-NEXT: key.usr: "s:VSC7MyError",
// CHECK-NEXT: This is my cool error code.

// CHECK: key.kind: source.lang.swift.decl.enum,
// CHECK-NEXT: key.name: "Code",
// CHECK-NEXT: key.usr: "c:@E@MyErrorCode",
// CHECK-NEXT: This is my cool error code.

// CHECK: key.kind: source.lang.swift.decl.enumelement,
// CHECK-NEXT: key.name: "errFirst",
// CHECK-NEXT: key.usr: "c:@E@MyErrorCode@MyErrFirst",
// CHECK-NEXT: This is first error.

// CHECK: key.kind: source.lang.swift.decl.var.static,
// CHECK-NEXT: key.name: "errFirst",
// CHECK-NEXT: key.usr: "s:ZvVSC7MyError8errFirstOS_4Code",
// CHECK-NEXT: This is first error.