From 55ef1bcaf010dc22763f4663b641db9befc8e6fd Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 24 Jun 2025 17:13:07 +0100 Subject: [PATCH] [SourceKit] Print backticks if needed in `printDisplayName` Ensure we print raw identifier names with backticks for e.g the document structure request. rdar://152524780 --- include/swift/AST/Identifier.h | 6 +- lib/AST/Identifier.cpp | 19 +- .../Inputs/raw-identifiers.swift | 13 ++ .../DocumentStructure/structure.swift | 3 + .../structure.swift.empty.response | 2 +- .../structure.swift.foobar.response | 2 +- .../structure.swift.invalid.response | 2 +- .../structure.swift.placeholders.response | 6 +- ...cture.swift.raw-identifiers.swift.response | 165 ++++++++++++++++++ .../structure.swift.response | 2 +- .../lib/SwiftLang/SwiftLangSupport.cpp | 3 +- 11 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 test/SourceKit/DocumentStructure/Inputs/raw-identifiers.swift create mode 100644 test/SourceKit/DocumentStructure/structure.swift.raw-identifiers.swift.response diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 6a773015106ba..61aa60fa31056 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -663,8 +663,12 @@ class DeclName { /// /// \param skipEmptyArgumentNames When true, don't print the argument labels /// if they are all empty. + /// + /// \param escapeIfNeeded When true, escape identifiers with backticks + /// when required. llvm::raw_ostream &print(llvm::raw_ostream &os, - bool skipEmptyArgumentNames = false) const; + bool skipEmptyArgumentNames = false, + bool escapeIfNeeded = false) const; /// Print a "pretty" representation of this declaration name to the given /// stream. diff --git a/lib/AST/Identifier.cpp b/lib/AST/Identifier.cpp index 0e32186ebdbee..136b6379d0f9f 100644 --- a/lib/AST/Identifier.cpp +++ b/lib/AST/Identifier.cpp @@ -166,9 +166,15 @@ StringRef DeclName::getString(llvm::SmallVectorImpl &scratch, } llvm::raw_ostream &DeclName::print(llvm::raw_ostream &os, - bool skipEmptyArgumentNames) const { + bool skipEmptyArgumentNames, + bool escapeIfNeeded) const { // Print the base name. - os << getBaseName(); + auto baseName = getBaseName(); + if (escapeIfNeeded && baseName.mustAlwaysBeEscaped()) { + os << "`" << baseName << "`"; + } else { + os << baseName; + } // If this is a simple name, we're done. if (isSimpleName()) @@ -193,8 +199,13 @@ llvm::raw_ostream &DeclName::print(llvm::raw_ostream &os, // Print the argument names. os << "("; - for (auto c : getArgumentNames()) { - os << c << ':'; + for (auto argName : getArgumentNames()) { + if (escapeIfNeeded && argName.mustAlwaysBeEscaped()) { + os << "`" << argName << "`"; + } else { + os << argName; + } + os << ':'; } os << ")"; return os; diff --git a/test/SourceKit/DocumentStructure/Inputs/raw-identifiers.swift b/test/SourceKit/DocumentStructure/Inputs/raw-identifiers.swift new file mode 100644 index 0000000000000..61b94981ce717 --- /dev/null +++ b/test/SourceKit/DocumentStructure/Inputs/raw-identifiers.swift @@ -0,0 +1,13 @@ +struct `A.B` { + func `foo bar`(`a b`: Int, c: Int, `$`: String) {} + func `3four`() {} + func `baz`(`x`: Int) {} +} +extension `A.B` {} + +struct Outer { + struct Inner {} +} +extension Outer.Inner {} + +func + (lhs: `A.B`, rhs: `A.B`) -> `A.B` { lhs } diff --git a/test/SourceKit/DocumentStructure/structure.swift b/test/SourceKit/DocumentStructure/structure.swift index 6ee2958265e01..5b840fe4e4f50 100644 --- a/test/SourceKit/DocumentStructure/structure.swift +++ b/test/SourceKit/DocumentStructure/structure.swift @@ -7,6 +7,9 @@ // RUN: %sourcekitd-test -req=structure %S/../Inputs/placeholders.swift | %sed_clean > %t.placeholders.response // RUN: %diff -u %s.placeholders.response %t.placeholders.response +// RUN: %sourcekitd-test -req=structure %S/Inputs/raw-identifiers.swift | %sed_clean > %t.raw-identifiers.swift.response +// RUN: %diff -u %s.raw-identifiers.swift.response %t.raw-identifiers.swift.response + // RUN: %sourcekitd-test -req=structure %S/Inputs/main.swift -name -foobar | %sed_clean > %t.foobar.response // RUN: %diff -u %s.foobar.response %t.foobar.response diff --git a/test/SourceKit/DocumentStructure/structure.swift.empty.response b/test/SourceKit/DocumentStructure/structure.swift.empty.response index f362c577bc8c2..b33850b7693cf 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.empty.response +++ b/test/SourceKit/DocumentStructure/structure.swift.empty.response @@ -1113,7 +1113,7 @@ key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.internal, key.setter_accessibility: source.lang.swift.accessibility.internal, - key.name: "$", + key.name: "`$`", key.offset: 1982, key.length: 24, key.nameoffset: 1999, diff --git a/test/SourceKit/DocumentStructure/structure.swift.foobar.response b/test/SourceKit/DocumentStructure/structure.swift.foobar.response index 0279f3a4e24d7..e97d96ea110f6 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.foobar.response +++ b/test/SourceKit/DocumentStructure/structure.swift.foobar.response @@ -1113,7 +1113,7 @@ key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.internal, key.setter_accessibility: source.lang.swift.accessibility.internal, - key.name: "$", + key.name: "`$`", key.offset: 1982, key.length: 24, key.nameoffset: 1999, diff --git a/test/SourceKit/DocumentStructure/structure.swift.invalid.response b/test/SourceKit/DocumentStructure/structure.swift.invalid.response index 612f5146fae7f..8bbd0ea196fba 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.invalid.response +++ b/test/SourceKit/DocumentStructure/structure.swift.invalid.response @@ -60,7 +60,7 @@ { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.internal, - key.name: "3", + key.name: "`3`", key.offset: 84, key.length: 10, key.nameoffset: 90, diff --git a/test/SourceKit/DocumentStructure/structure.swift.placeholders.response b/test/SourceKit/DocumentStructure/structure.swift.placeholders.response index 575b749ac5374..a271d4876db60 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.placeholders.response +++ b/test/SourceKit/DocumentStructure/structure.swift.placeholders.response @@ -6,7 +6,7 @@ { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.internal, - key.name: "<#MyCls#>", + key.name: "`<#MyCls#>`", key.offset: 0, key.length: 35, key.nameoffset: 6, @@ -29,7 +29,7 @@ { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.internal, - key.name: "<#test1#>()", + key.name: "`<#test1#>`()", key.offset: 37, key.length: 52, key.nameoffset: 42, @@ -56,7 +56,7 @@ key.substructure: [ { key.kind: source.lang.swift.decl.var.local, - key.name: "<#name#>", + key.name: "`<#name#>`", key.offset: 63, key.length: 8, key.nameoffset: 63, diff --git a/test/SourceKit/DocumentStructure/structure.swift.raw-identifiers.swift.response b/test/SourceKit/DocumentStructure/structure.swift.raw-identifiers.swift.response new file mode 100644 index 0000000000000..44ac96cbe8469 --- /dev/null +++ b/test/SourceKit/DocumentStructure/structure.swift.raw-identifiers.swift.response @@ -0,0 +1,165 @@ +{ + key.offset: 0, + key.length: 246, + key.diagnostic_stage: source.diagnostic.stage.swift.parse, + key.substructure: [ + { + key.kind: source.lang.swift.decl.struct, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "`A.B`", + key.offset: 0, + key.length: 115, + key.nameoffset: 7, + key.namelength: 3, + key.bodyoffset: 14, + key.bodylength: 100, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "`foo bar`(`a b`:c:`$`:)", + key.offset: 17, + key.length: 50, + key.nameoffset: 22, + key.namelength: 42, + key.bodyoffset: 66, + key.bodylength: 0, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "`a b`", + key.offset: 32, + key.length: 10, + key.typename: "Int", + key.nameoffset: 32, + key.namelength: 3 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "c", + key.offset: 44, + key.length: 6, + key.typename: "Int", + key.nameoffset: 44, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "`$`", + key.offset: 52, + key.length: 11, + key.typename: "String", + key.nameoffset: 52, + key.namelength: 1 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "`3four`()", + key.offset: 70, + key.length: 17, + key.nameoffset: 75, + key.namelength: 9, + key.bodyoffset: 86, + key.bodylength: 0 + }, + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "baz(x:)", + key.offset: 90, + key.length: 23, + key.nameoffset: 95, + key.namelength: 15, + key.bodyoffset: 112, + key.bodylength: 0, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "x", + key.offset: 101, + key.length: 8, + key.typename: "Int", + key.nameoffset: 101, + key.namelength: 1 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "`A.B`", + key.offset: 116, + key.length: 18, + key.nameoffset: 126, + key.namelength: 5, + key.bodyoffset: 133, + key.bodylength: 0 + }, + { + key.kind: source.lang.swift.decl.struct, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Outer", + key.offset: 136, + key.length: 34, + key.nameoffset: 143, + key.namelength: 5, + key.bodyoffset: 150, + key.bodylength: 19, + key.substructure: [ + { + key.kind: source.lang.swift.decl.struct, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Inner", + key.offset: 153, + key.length: 15, + key.nameoffset: 160, + key.namelength: 5, + key.bodyoffset: 167, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer.Inner", + key.offset: 171, + key.length: 24, + key.nameoffset: 181, + key.namelength: 11, + key.bodyoffset: 194, + key.bodylength: 0 + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 197, + key.length: 48, + key.typename: "`A.B`", + key.nameoffset: 202, + key.namelength: 26, + key.bodyoffset: 239, + key.bodylength: 5, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 205, + key.length: 10, + key.typename: "`A.B`" + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 217, + key.length: 10, + key.typename: "`A.B`" + } + ] + } + ] +} diff --git a/test/SourceKit/DocumentStructure/structure.swift.response b/test/SourceKit/DocumentStructure/structure.swift.response index 6c69cef5307e6..b001a5484be4d 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.response +++ b/test/SourceKit/DocumentStructure/structure.swift.response @@ -1113,7 +1113,7 @@ key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.internal, key.setter_accessibility: source.lang.swift.accessibility.internal, - key.name: "$", + key.name: "`$`", key.offset: 1982, key.length: 24, key.nameoffset: 1999, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index cab1fa17d6542..3c75e3defc357 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -891,7 +891,8 @@ bool SwiftLangSupport::printDisplayName(const swift::ValueDecl *D, if (!D->hasName()) return true; - OS << D->getName(); + D->getName().print(OS, /*skipEmptyArgumentNames*/ false, + /*escapeIfNeeded*/ true); return false; }