Skip to content

Commit 62f30d5

Browse files
committed
[SourceKit] For clang enums imported as error domains, make sure to use unique USRs.
Error domain enums are imported with synthesizing something 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. rdar://27550967
1 parent f8f6d61 commit 62f30d5

File tree

4 files changed

+84
-1
lines changed

4 files changed

+84
-1
lines changed

lib/AST/USRGeneration.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/ADT/StringRef.h"
1919
#include "llvm/Support/raw_ostream.h"
2020
#include "clang/AST/ASTContext.h"
21+
#include "clang/AST/Attr.h"
2122
#include "clang/Index/USRGeneration.h"
2223
#include "clang/Lex/PreprocessingRecord.h"
2324
#include "clang/Lex/Preprocessor.h"
@@ -55,7 +56,39 @@ bool ide::printDeclUSR(const ValueDecl *D, raw_ostream &OS) {
5556

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

58-
if (ClangNode ClangN = VD->getClangNode()) {
59+
auto interpretAsClangNode = [](const ValueDecl *D)->ClangNode {
60+
ClangNode ClangN = D->getClangNode();
61+
if (auto ClangD = ClangN.getAsDecl()) {
62+
// NSErrorDomain causes the clang enum to be imported like this:
63+
//
64+
// struct MyError {
65+
// enum Code : Int32 {
66+
// case errFirst
67+
// case errSecond
68+
// }
69+
// static var errFirst: MyError.Code { get }
70+
// static var errSecond: MyError.Code { get }
71+
// }
72+
//
73+
// The clang enum and enum constants are associated with both the
74+
// struct/nested enum, and the static vars/enum cases.
75+
// But we want unique USRs for the above symbols, so use the clang USR
76+
// for the enum and enum cases, and the Swift USR for the struct and vars.
77+
//
78+
if (isa<clang::EnumDecl>(ClangD)) {
79+
if (ClangD->hasAttr<clang::NSErrorDomainAttr>() && isa<StructDecl>(D))
80+
return ClangNode();
81+
} else if (auto *ClangEnumConst = dyn_cast<clang::EnumConstantDecl>(ClangD)) {
82+
if (auto *ClangEnum = dyn_cast<clang::EnumDecl>(ClangEnumConst->getDeclContext())) {
83+
if (ClangEnum->hasAttr<clang::NSErrorDomainAttr>() && isa<VarDecl>(D))
84+
return ClangNode();
85+
}
86+
}
87+
}
88+
return ClangN;
89+
};
90+
91+
if (ClangNode ClangN = interpretAsClangNode(D)) {
5992
llvm::SmallString<128> Buf;
6093
if (auto ClangD = ClangN.getAsDecl()) {
6194
bool Ignore = clang::index::generateUSRForDecl(ClangD, Buf);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@import Foundation;
2+
3+
#define NS_ERROR_ENUM(_type, _name, _domain) \
4+
enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type
5+
6+
@class NSString;
7+
extern const NSString *const MyErrorDomain;
8+
/// This is my cool error code.
9+
typedef NS_ERROR_ENUM(int, MyErrorCode, MyErrorDomain) {
10+
/// This is first error.
11+
MyErrFirst,
12+
/// This is second error.
13+
MyErrSecond,
14+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module "MyError" {
2+
header "MyError.h"
3+
export *
4+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// REQUIRES: OS=macosx
2+
// RUN: %sourcekitd-test -req=doc-info -module MyError -- -I %S/Inputs \
3+
// RUN: %mcp_opt -sdk %sdk | %sed_clean > %t.response
4+
// RUN: FileCheck -input-file=%t.response %s
5+
6+
// CHECK: struct MyError {
7+
// CHECK: enum Code : Int32 {
8+
// CHECK: case errFirst
9+
// CHECK: case errSecond
10+
// CHECK: }
11+
// CHECK: static var errFirst: MyError.Code { get }
12+
// CHECK: static var errSecond: MyError.Code { get }
13+
14+
// CHECK: key.kind: source.lang.swift.decl.struct,
15+
// CHECK-NEXT: key.name: "MyError",
16+
// CHECK-NEXT: key.usr: "s:VSC7MyError",
17+
// CHECK-NEXT: This is my cool error code.
18+
19+
// CHECK: key.kind: source.lang.swift.decl.enum,
20+
// CHECK-NEXT: key.name: "Code",
21+
// CHECK-NEXT: key.usr: "c:@E@MyErrorCode",
22+
// CHECK-NEXT: This is my cool error code.
23+
24+
// CHECK: key.kind: source.lang.swift.decl.enumelement,
25+
// CHECK-NEXT: key.name: "errFirst",
26+
// CHECK-NEXT: key.usr: "c:@E@MyErrorCode@MyErrFirst",
27+
// CHECK-NEXT: This is first error.
28+
29+
// CHECK: key.kind: source.lang.swift.decl.var.static,
30+
// CHECK-NEXT: key.name: "errFirst",
31+
// CHECK-NEXT: key.usr: "s:ZvVSC7MyError8errFirstOS_4Code",
32+
// CHECK-NEXT: This is first error.

0 commit comments

Comments
 (0)