Skip to content

Index synthesized wrapper refs by substituting the Clang decl. #80074

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 3 commits into from
Mar 25, 2025
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
5 changes: 5 additions & 0 deletions include/swift/AST/ClangModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class SourceLocation;
namespace swift {

class ClangInheritanceInfo;
class ClangNode;
class ConcreteDeclRef;
class Decl;
class FuncDecl;
Expand Down Expand Up @@ -321,6 +322,10 @@ class ClangModuleLoader : public ModuleLoader {
getTypeDefForCXXCFOptionsDefinition(const clang::Decl *candidateDecl) = 0;

virtual SourceLoc importSourceLocation(clang::SourceLocation loc) = 0;

/// Just like Decl::getClangNode() except we look through to the 'Code'
/// enum of an error wrapper struct.
virtual ClangNode getEffectiveClangNode(const Decl *decl) const = 0;
};

/// Describes a C++ template instantiation error.
Expand Down
37 changes: 31 additions & 6 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,20 +713,45 @@ class RequirementRequest :
bool isCached() const;
};

struct USRGenerationOptions {
/// @brief Whether to emit USRs using the Swift declaration when it is
/// synthesized from a Clang based declaration. Useful in cases where Swift
/// declarations are synthesized from Clang nodes but the caller actually
/// wants the USR of the Swift declaration.
bool distinguishSynthesizedDecls;

friend llvm::hash_code hash_value(const USRGenerationOptions &options) {
return llvm::hash_value(options.distinguishSynthesizedDecls);
}

friend bool operator==(const USRGenerationOptions &lhs,
const USRGenerationOptions &rhs) {
return lhs.distinguishSynthesizedDecls == rhs.distinguishSynthesizedDecls;
}

friend bool operator!=(const USRGenerationOptions &lhs,
const USRGenerationOptions &rhs) {
return !(lhs == rhs);
}
};

void simple_display(llvm::raw_ostream &out,
const USRGenerationOptions &options);

/// Generate the USR for the given declaration.
class USRGenerationRequest :
public SimpleRequest<USRGenerationRequest,
std::string(const ValueDecl*),
RequestFlags::Cached>
{
class USRGenerationRequest
: public SimpleRequest<USRGenerationRequest,
std::string(const ValueDecl *, USRGenerationOptions),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
std::string evaluate(Evaluator &eval, const ValueDecl *d) const;
std::string evaluate(Evaluator &eval, const ValueDecl *d,
USRGenerationOptions options) const;

public:
// Caching
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ SWIFT_REQUEST(TypeChecker, TypeCheckASTNodeAtLocRequest,
Uncached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, UnderlyingTypeRequest, Type(TypeAliasDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, USRGenerationRequest, std::string(const ValueDecl *),
SWIFT_REQUEST(TypeChecker, USRGenerationRequest,
std::string(const ValueDecl *, USRGenerationOptions),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsABICompatibleOverrideRequest,
bool(ValueDecl *), Cached, NoLocationInfo)
Expand Down
10 changes: 8 additions & 2 deletions include/swift/AST/USRGeneration.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ bool printTypeUSR(Type Ty, raw_ostream &OS);
bool printDeclTypeUSR(const ValueDecl *D, raw_ostream &OS);

/// Prints out the USR for the given ValueDecl.
/// @param distinguishSynthesizedDecls Whether to use the USR of the
/// synthesized declaration instead of the USR of the underlying Clang USR.
/// \returns true if it failed, false on success.
bool printValueDeclUSR(const ValueDecl *D, raw_ostream &OS);
bool printValueDeclUSR(const ValueDecl *D, raw_ostream &OS,
bool distinguishSynthesizedDecls = false);

/// Prints out the USR for the given ModuleEntity.
/// In case module aliasing is used, it prints the real module name. For example,
Expand All @@ -64,8 +67,11 @@ bool printAccessorUSR(const AbstractStorageDecl *D, AccessorKind AccKind,
bool printExtensionUSR(const ExtensionDecl *ED, raw_ostream &OS);

/// Prints out the USR for the given Decl.
/// @param distinguishSynthesizedDecls Whether to use the USR of the
/// synthesized declaration instead of the USR of the underlying Clang USR.
/// \returns true if it failed, false on success.
bool printDeclUSR(const Decl *D, raw_ostream &OS);
bool printDeclUSR(const Decl *D, raw_ostream &OS,
bool distinguishSynthesizedDecls = false);

/// Demangle a mangle-name-based USR to a human readable name.
std::string demangleUSR(StringRef mangled);
Expand Down
2 changes: 1 addition & 1 deletion include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ class ClangImporter final : public ClangModuleLoader {

/// Just like Decl::getClangNode() except we look through to the 'Code'
/// enum of an error wrapper struct.
ClangNode getEffectiveClangNode(const Decl *decl) const;
ClangNode getEffectiveClangNode(const Decl *decl) const override;

/// Look for textually included declarations from the bridging header.
///
Expand Down
48 changes: 32 additions & 16 deletions lib/AST/USRGeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@
//
//===----------------------------------------------------------------------===//

#include "swift/AST/USRGeneration.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/GenericParamList.h"
#include "swift/AST/Module.h"
#include "swift/AST/USRGeneration.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/SwiftNameTranslation.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/USRGeneration.h"
#include "swift/Basic/Assertions.h"
#include "swift/Demangling/Demangler.h"
#include "llvm/ADT/SmallString.h"
#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"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;
using namespace ide;
Expand Down Expand Up @@ -170,9 +170,15 @@ static bool shouldUseObjCUSR(const Decl *D) {
return false;
}

void swift::simple_display(llvm::raw_ostream &out,
const USRGenerationOptions &options) {
out << "USRGenerationOptions (distinguishSynthesizedDecls: "
<< options.distinguishSynthesizedDecls << ")";
}

std::string
swift::USRGenerationRequest::evaluate(Evaluator &evaluator,
const ValueDecl *D) const {
swift::USRGenerationRequest::evaluate(Evaluator &evaluator, const ValueDecl *D,
USRGenerationOptions options) const {
if (auto *VD = dyn_cast<VarDecl>(D))
D = VD->getCanonicalVarDecl();

Expand All @@ -184,8 +190,9 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator,
if (isa<ModuleDecl>(D))
return std::string(); // Ignore.

auto interpretAsClangNode = [](const ValueDecl *D)->ClangNode {
ClangNode ClangN = D->getClangNode();
auto interpretAsClangNode = [&options](const ValueDecl *D) -> ClangNode {
auto *importer = D->getASTContext().getClangModuleLoader();
ClangNode ClangN = importer->getEffectiveClangNode(D);
if (auto ClangD = ClangN.getAsDecl()) {
// NSErrorDomain causes the clang enum to be imported like this:
//
Expand All @@ -203,12 +210,19 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator,
// But we want unique USRs for the above symbols, so use the clang USR
// for the enum cases, and the Swift USR for the vars.
//
if (!options.distinguishSynthesizedDecls) {
return ClangN;
}
if (auto *ClangEnumConst = dyn_cast<clang::EnumConstantDecl>(ClangD)) {
if (auto *ClangEnum = dyn_cast<clang::EnumDecl>(ClangEnumConst->getDeclContext())) {
if (auto *ClangEnum =
dyn_cast<clang::EnumDecl>(ClangEnumConst->getDeclContext())) {
if (ClangEnum->hasAttr<clang::NSErrorDomainAttr>() && isa<VarDecl>(D))
return ClangNode();
}
}
if (D->getAttrs().hasAttribute<ClangImporterSynthesizedTypeAttr>()) {
return ClangNode();
}
}
return ClangN;
};
Expand Down Expand Up @@ -292,10 +306,11 @@ bool ide::printModuleUSR(ModuleEntity Mod, raw_ostream &OS) {
}
}

bool ide::printValueDeclUSR(const ValueDecl *D, raw_ostream &OS) {
auto result = evaluateOrDefault(D->getASTContext().evaluator,
USRGenerationRequest { D },
std::string());
bool ide::printValueDeclUSR(const ValueDecl *D, raw_ostream &OS,
bool distinguishSynthesizedDecls) {
auto result = evaluateOrDefault(
D->getASTContext().evaluator,
USRGenerationRequest{D, {distinguishSynthesizedDecls}}, std::string());
if (result.empty())
return true;
OS << result;
Expand Down Expand Up @@ -354,9 +369,10 @@ bool ide::printExtensionUSR(const ExtensionDecl *ED, raw_ostream &OS) {
return true;
}

bool ide::printDeclUSR(const Decl *D, raw_ostream &OS) {
bool ide::printDeclUSR(const Decl *D, raw_ostream &OS,
bool distinguishSynthesizedDecls) {
if (auto *VD = dyn_cast<ValueDecl>(D)) {
if (ide::printValueDeclUSR(VD, OS)) {
if (ide::printValueDeclUSR(VD, OS, distinguishSynthesizedDecls)) {
return true;
}
} else if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/PrintAsClang/ClangSyntaxPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ void ClangSyntaxPrinter::printSymbolUSRAttribute(const ValueDecl *D) const {
return;
}
auto result = evaluateOrDefault(D->getASTContext().evaluator,
USRGenerationRequest{D}, std::string());
USRGenerationRequest{D, {}}, std::string());
if (result.empty())
return;
os << " SWIFT_SYMBOL(\"" << result << "\")";
Expand Down
2 changes: 1 addition & 1 deletion lib/SymbolGraphGen/Symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ void Symbol::printPath(llvm::raw_ostream &OS) const {

void Symbol::getUSR(SmallVectorImpl<char> &USR) const {
llvm::raw_svector_ostream OS(USR);
ide::printDeclUSR(D, OS);
ide::printDeclUSR(D, OS, /*distinguishSynthesizedDecls*/ true);
if (SynthesizedBaseTypeDecl) {
OS << "::SYNTHESIZED::";
ide::printDeclUSR(SynthesizedBaseTypeDecl, OS);
Expand Down
43 changes: 43 additions & 0 deletions test/Index/index_imported_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@
@property int categoryAddedProperty;
@end

extern NSErrorDomain const TestErrorDomain;

typedef NS_ERROR_ENUM(TestErrorDomain, TestError){
TestErrorFoo = 0,
TestErrorBar = 1,
};

typedef NS_ENUM(NSInteger, ErrCategory) {
ErrCategoryA = 0,
ErrCategoryB = 1,
};

//--- module.modulemap
module objc_decls {
header "objc_decls.h"
Expand Down Expand Up @@ -48,3 +60,34 @@ class SubObjCClass: ObjCClass {
// CHECK-NEXT: RelOver | instance-method/Swift | protocolAddedMethod() | c:objc(pl)MemberAdding(im)protocolAddedMethod
// CHECK-NEXT: RelChild | class/Swift | SubObjCClass | c:@M@swift_ide_test@objc(cs)SubObjCClass
}
func testEnumIndex() {
let _: TestError.Code = .bar
// CHECK: [[@LINE-1]]:10 | struct/Swift | TestError | c:@E@TestError | Ref | rel: 0
// CHECK: [[@LINE-2]]:20 | enum/Swift | Code | c:@E@TestError | Ref,RelCont | rel: 1
// CHECK: [[@LINE-3]]:28 | enumerator/Swift | bar | c:@E@TestError@TestErrorBar | Ref,RelCont | rel: 1
let e = TestError.foo
// CHECK: [[@LINE-1]]:11 | struct/Swift | TestError | c:@E@TestError | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:21 | static-property/Swift | foo | c:@E@TestError@TestErrorFoo | Ref,Read,RelCont | rel: 1
switch e {
case TestError.foo: break
// CHECK: [[@LINE-1]]:10 | struct/Swift | TestError | c:@E@TestError | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:20 | static-property/Swift | foo | c:@E@TestError@TestErrorFoo | Ref,Read,RelCont | rel: 1
case .bar: break
// CHECK: [[@LINE-1]]:11 | enumerator/Swift | bar | c:@E@TestError@TestErrorBar | Ref,RelCont | rel: 1
default: break
}
let _ = ErrCategory.B
// CHECK: [[@LINE-1]]:11 | enum/Swift | ErrCategory | c:@E@ErrCategory | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:23 | enumerator/Swift | B | c:@E@ErrCategory@ErrCategoryB | Ref,RelCont | rel: 1
let c: ErrCategory = .A
// CHECK: [[@LINE-1]]:10 | enum/Swift | ErrCategory | c:@E@ErrCategory | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:25 | enumerator/Swift | A | c:@E@ErrCategory@ErrCategoryA | Ref,RelCont | rel: 1
switch c {
case ErrCategory.A: break
// CHECK: [[@LINE-1]]:22 | enumerator/Swift | A | c:@E@ErrCategory@ErrCategoryA | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:10 | enum/Swift | ErrCategory | c:@E@ErrCategory | Ref,RelCont | rel: 1
// CHECK: [[@LINE-3]]:22 | enum/Swift | ErrCategory | c:@E@ErrCategory | Ref,RelCont | rel: 1
case .B: break
// CHECK: [[@LINE-1]]:11 | enumerator/Swift | B | c:@E@ErrCategory@ErrCategoryB | Ref,RelCont | rel: 1
}
}
6 changes: 6 additions & 0 deletions test/SymbolGraph/ClangImporter/ErrorEnum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
// but it should be hidden for a similar reason
// CHECK-NOT: "precise": "s:SYsSHRzSH8RawValueSYRpzrlE4hash4intoys6HasherVz_tF::SYNTHESIZED::c:@E@MyErrorCode"

// MyError
// CHECK-DAG: "precise": "s:SC11MyErrorCodeLeV"

// MyError.errFirst
// CHECK-DAG: "precise": "s:SC11MyErrorCodeLeV8errFirstSoAAVvpZ"
Comment on lines +21 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating this test! This would have been my only concern with this PR, but it looks like that's settled.


// MyErrorCode.Code
// CHECK-DAG: "precise": "c:@E@MyErrorCode"

Expand Down
2 changes: 1 addition & 1 deletion tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ static bool initDocEntityInfo(const Decl *D,
SwiftLangSupport::printDisplayName(VD, NameOS);
{
llvm::raw_svector_ostream OS(Info.USR);
SwiftLangSupport::printUSR(VD, OS);
SwiftLangSupport::printUSR(VD, OS, /*distinguishSynthesizedDecls*/ true);
if (SynthesizedTarget) {
OS << SwiftLangSupport::SynthesizedUSRSeparator;
SwiftLangSupport::printUSR(SynthesizedTargetNTD, OS);
Expand Down
5 changes: 3 additions & 2 deletions tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,9 @@ bool SwiftLangSupport::printDisplayName(const swift::ValueDecl *D,
return false;
}

bool SwiftLangSupport::printUSR(const ValueDecl *D, llvm::raw_ostream &OS) {
return ide::printValueDeclUSR(D, OS);
bool SwiftLangSupport::printUSR(const ValueDecl *D, llvm::raw_ostream &OS,
bool distinguishSynthesizedDecls) {
return ide::printValueDeclUSR(D, OS, distinguishSynthesizedDecls);
}

bool SwiftLangSupport::printDeclTypeUSR(const ValueDecl *D, llvm::raw_ostream &OS) {
Expand Down
5 changes: 4 additions & 1 deletion tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,11 @@ class SwiftLangSupport : public LangSupport {
static bool printDisplayName(const swift::ValueDecl *D, llvm::raw_ostream &OS);

/// Generate a USR for a Decl, including the prefix.
/// @param distinguishSynthesizedDecls Whether to use the USR of the
/// synthesized declaration instead of the USR of the underlying Clang USR.
/// \returns true if the results should be ignored, false otherwise.
static bool printUSR(const swift::ValueDecl *D, llvm::raw_ostream &OS);
static bool printUSR(const swift::ValueDecl *D, llvm::raw_ostream &OS,
bool distinguishSynthesizedDecls = false);

/// Generate a USR for the Type of a given decl.
/// \returns true if the results should be ignored, false otherwise.
Expand Down