Skip to content

Commit 2c0cf0b

Browse files
authored
Merge pull request #3834 from akyrtzi/enum-error-duplicate-usr
[SourceKit] For clang enums imported as error domains, make sure to use unique USRs.
2 parents c2edbb1 + 62f30d5 commit 2c0cf0b

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)