[SourceKit] Report cursor info information for literals

Adds literals support for the CursorInfo request.
Previously CursorInfo just returned error for literals.
Implements issue #57725
This commit is contained in:
Simon Barinka
2023-07-30 14:22:08 +02:00
parent d2c8ede38d
commit fcd084f9dd
9 changed files with 223 additions and 24 deletions

View File

@@ -349,7 +349,7 @@ bool CursorInfoResolver::walkToExprPost(Expr *E) {
if (OutermostCursorExpr && isCursorOn(E, LocToResolve)) {
CursorInfo = new ResolvedExprStartCursorInfo(
CursorInfo->getSourceFile(), CursorInfo->getLoc(), OutermostCursorExpr);
CursorInfo->getSourceFile(), CursorInfo->getLoc(), E);
return false;
}

View File

@@ -230,6 +230,11 @@ enum E7: String {
func checkAnyIsAKeyword(x: Any) {}
var nilLiteral1: Int? = nil
var nilLiteral2: Int! = nil
var binExpr = 1 + 2 + 3
// REQUIRES: objc_interop
// RUN: %empty-directory(%t.tmp)
// RUN: %swiftc_driver -emit-module -o %t.tmp/FooSwiftModule.swiftmodule %S/Inputs/FooSwiftModule.swift
@@ -803,9 +808,52 @@ func checkAnyIsAKeyword(x: Any) {}
// CHECK93: <Declaration>case b = &quot;f&quot;</Declaration>
// CHECK93-NEXT: <decl.enumelement><syntaxtype.keyword>case</syntaxtype.keyword> <decl.name>b</decl.name> = <syntaxtype.string>&quot;f&quot;</syntaxtype.string></decl.enumelement>
// RUN: %sourcekitd-test -req=cursor -pos=227:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK94 %s
// CHECK94: <empty cursor info; internal diagnostic: "Resolved to incomplete expression or statement.">
// RUN: %sourcekitd-test -req=cursor -pos=23:23 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK94 %s
// CHECK94: source.lang.swift.expr.literal ()
// CHECK94-EMPTY:
// CHECK94-NEXT: s:SS
// CHECK94-NEXT: source.lang.swift
// CHECK94-NEXT: String
// CHECK94-NEXT: $sSSD
// CHECK94-NEXT: Swift
// CHECK94-NEXT: <Group>String</Group>
// CHECK94-NEXT: SYSTEM
// CHECK94-NEXT: <Declaration>@frozen @_eagerMove struct String</Declaration>
// CHECK94-NEXT: <decl.struct><syntaxtype.attribute.builtin><syntaxtype.attribute.name>@frozen</syntaxtype.attribute.name></syntaxtype.attribute.builtin> <syntaxtype.attribute.builtin><syntaxtype.attribute.name>@_eagerMove</syntaxtype.attribute.name></syntaxtype.attribute.builtin> <syntaxtype.keyword>struct</syntaxtype.keyword> <decl.name>String</decl.name></decl.struct>
// RUN: %sourcekitd-test -req=cursor -pos=231:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK95 %s
// CHECK95: <Declaration>func checkAnyIsAKeyword(x: Any)</Declaration>
// CHECK95-NEXT: <decl.function.free><syntaxtype.keyword>func</syntaxtype.keyword> <decl.name>checkAnyIsAKeyword</decl.name>(<decl.var.parameter><decl.var.parameter.argument_label>x</decl.var.parameter.argument_label>: <decl.var.parameter.type><syntaxtype.keyword>Any</syntaxtype.keyword></decl.var.parameter.type></decl.var.parameter>)</decl.function.free>
// RUN: %sourcekitd-test -req=cursor -pos=233:25 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK96 %s
// CHECK96: source.lang.swift.expr.literal
// CHECK96-EMPTY:
// CHECK96-NEXT: s:Sq
// CHECK96-NEXT: source.lang.swift
// CHECK96-NEXT: Int?
// CHECK96-NEXT: $sSiXSqD
// CHECK96-NEXT: Swift
// CHECK96-NEXT: <Group>Optional</Group>
// CHECK96-NEXT: SYSTEM
// RUN: %sourcekitd-test -req=cursor -pos=234:25 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK97 %s
// CHECK97: source.lang.swift.expr.literal
// CHECK97-EMPTY:
// CHECK97-NEXT: s:Sq
// CHECK97-NEXT: source.lang.swift
// CHECK97-NEXT: Int?
// CHECK97-NEXT: $sSiXSqD
// CHECK97-NEXT: Swift
// CHECK97-NEXT: <Group>Optional</Group>
// CHECK97-NEXT: SYSTEM
// RUN: %sourcekitd-test -req=cursor -pos=236:19 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK98 %s
// CHECK98: source.lang.swift.expr.literal
// CHECK98-EMPTY:
// CHECK98-NEXT: s:Si
// CHECK98-NEXT: source.lang.swift
// CHECK98-NEXT: Int
// CHECK98-NEXT: $sSiD
// CHECK98-NEXT: Swift
// CHECK98-NEXT: <Group>Math/Integers</Group>
// CHECK98-NEXT: SYSTEM

View File

@@ -21,6 +21,15 @@ func foo2(_ a : inout [S1]) {
import Swift
func foo3(a: Float, b: Bool) {}
import AppKit.NSColor
let colorResource = #colorLiteral(red: 0.8549019694, green: 0.250980407, blue: 0.4784313738, alpha: 1)
let arrLiteral = [1, 2, 3]
let arrNonConst = [1, 2, d]
let dictLiteral = [1:2, 3:4]
let dictNonCost = [1:2, 3:d]
// REQUIRES: objc_interop
// RUN: %empty-directory(%t)
@@ -222,3 +231,59 @@ func foo3(a: Float, b: Bool) {}
// RUN: %sourcekitd-test -req=cursor -pos=22:25 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-BOOL1 %s
// CHECK-BOOL1: s:Sb
// RUN: %sourcekitd-test_plain \
// RUN: -req=open -name %s %s -- %s -sdk %sdk == \
// RUN: -req=cursor -pos=25:29 %s -- %s -sdk %sdk | %FileCheck -check-prefix=CHECK-OBJ-LITERAL %s
// CHECK-OBJ-LITERAL: source.lang.swift.expr.object_literal
// CHECK-OBJ-LITERAL-EMPTY:
// CHECK-OBJ-LITERAL: c:objc(cs)NSColor
// CHECK-OBJ-LITERAL: source.lang.objc
// CHECK-OBJ-LITERAL: NSColor
// CHECK-OBJ-LITERAL: $sSo7NSColorCD
// CHECK-OBJ-LITERAL: AppKit.NSColor
// CHECK-OBJ-LITERAL: SYSTEM
// RUN: %sourcekitd-test_plain -req=cursor -pos=27:18 %s -- %s -sdk %sdk | %FileCheck -check-prefix=CHECK-ARRAY1 %s
// CHECK-ARRAY1: source.lang.swift.expr.literal
// CHECK-ARRAY1-EMPTY:
// CHECK-ARRAY1: s:Sa
// CHECK-ARRAY1: source.lang.swift
// CHECK-ARRAY1: [Int]
// CHECK-ARRAY1: $sSiXSaD
// CHECK-ARRAY1: Swift
// CHECK-ARRAY1: <Group>Collection/Array</Group>
// CHECK-ARRAY1: SYSTEM
// RUN: %sourcekitd-test_plain -req=cursor -pos=28:19 %s -- %s -sdk %sdk | %FileCheck -check-prefix=CHECK-ARRAY2 %s
// CHECK-ARRAY2: source.lang.swift.expr.literal
// CHECK-ARRAY2-EMPTY:
// CHECK-ARRAY2: s:Sa
// CHECK-ARRAY2: source.lang.swift
// CHECK-ARRAY2: [Any]
// CHECK-ARRAY2: $sypXSaD
// CHECK-ARRAY2: Swift
// CHECK-ARRAY2: <Group>Collection/Array</Group>
// CHECK-ARRAY2: SYSTEM
// RUN: %sourcekitd-test_plain -req=cursor -pos=30:19 %s -- %s -sdk %sdk | %FileCheck -check-prefix=CHECK-DICT1 %s
// CHECK-DICT1: source.lang.swift.expr.literal
// CHECK-DICT1-EMPTY:
// CHECK-DICT1: s:SD
// CHECK-DICT1: source.lang.swift
// CHECK-DICT1: [Int : Int]
// CHECK-DICT1: $sS2iXSDD
// CHECK-DICT1: Swift
// CHECK-DICT1: <Group>Collection/HashedCollections</Group>
// CHECK-DICT1: SYSTEM
// RUN: %sourcekitd-test_plain -req=cursor -pos=31:19 %s -- %s -sdk %sdk | %FileCheck -check-prefix=CHECK-DICT2 %s
// CHECK-DICT2: source.lang.swift.expr.literal
// CHECK-DICT2-EMPTY:
// CHECK-DICT2: s:SD
// CHECK-DICT2: source.lang.swift
// CHECK-DICT2: [Int : Any]
// CHECK-DICT2: $sSiypXSDD
// CHECK-DICT2: Swift
// CHECK-DICT2: <Group>Collection/HashedCollections</Group>
// CHECK-DICT2: SYSTEM

View File

@@ -15,6 +15,7 @@ elif 'swift_evolve' in config.available_features:
else:
sk_path_sanitize = os.path.join(os.path.dirname(__file__), 'Inputs', 'sourcekitd_path_sanitize.py')
config.substitutions.append( ('%sourcekitd-test_plain', config.sourcekitd_test) )
config.substitutions.append( ('%sourcekitd-test', '%s -module-cache-path %r' % (config.sourcekitd_test, config.clang_module_cache_path)) )
config.substitutions.append( ('%complete-test', '%s -module-cache-path=%r' % (config.complete_test, config.clang_module_cache_path)) )
config.substitutions.append( ('%swiftlib_dir', config.swiftlib_dir) )

View File

@@ -333,6 +333,13 @@ UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) {
return UIdentVisitor(IsRef).visit(const_cast<Decl*>(D));
}
UIdent SwiftLangSupport::getUIDForLiteral(const Expr *Lit) {
if (dyn_cast<ObjectLiteralExpr>(Lit)) {
return KindExprObjectLiteral;
}
return KindExprLiteral;
}
UIdent SwiftLangSupport::getUIDForExtensionOfDecl(const Decl *D) {
switch (D->getKind()) {
case swift::DeclKind::Struct:

View File

@@ -427,6 +427,7 @@ public:
static SourceKit::UIdent getUIDForDeclLanguage(const swift::Decl *D);
static SourceKit::UIdent getUIDForDecl(const swift::Decl *D,
bool IsRef = false);
static SourceKit::UIdent getUIDForLiteral(const swift::Expr *Lit);
static SourceKit::UIdent getUIDForExtensionOfDecl(const swift::Decl *D);
static SourceKit::UIdent getUIDForLocalVar(bool IsRef = false);
static SourceKit::UIdent getUIDForRefactoringKind(

View File

@@ -776,6 +776,7 @@ struct DeclInfo {
Type ContainerType;
bool IsRef;
bool IsDynamic;
Expr* LitExpr;
ArrayRef<NominalTypeDecl *> ReceiverTypes;
/// If VD is a synthesized property wrapper backing storage (_foo) or
@@ -788,10 +789,10 @@ struct DeclInfo {
bool InSynthesizedExtension = false;
DeclInfo(const ValueDecl *VD, Type ContainerType, bool IsRef, bool IsDynamic,
ArrayRef<NominalTypeDecl *> ReceiverTypes,
Expr *LitExpr, ArrayRef<NominalTypeDecl *> ReceiverTypes,
const CompilerInvocation &Invoc)
: VD(VD), ContainerType(ContainerType), IsRef(IsRef),
IsDynamic(IsDynamic), ReceiverTypes(ReceiverTypes) {
IsDynamic(IsDynamic), LitExpr(LitExpr), ReceiverTypes(ReceiverTypes) {
if (VD == nullptr)
return;
@@ -944,12 +945,17 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
SmallString<256> Buffer;
SmallVector<StringRef, 4> Strings;
llvm::raw_svector_ostream OS(Buffer);
bool isLiteral = DInfo.LitExpr != nullptr;
Symbol.DeclarationLang = SwiftLangSupport::getUIDForDeclLanguage(DInfo.VD);
Symbol.Kind = SwiftLangSupport::getUIDForDecl(DInfo.VD, DInfo.IsRef);
if (isLiteral) {
Symbol.Kind = SwiftLangSupport::getUIDForLiteral(DInfo.LitExpr);
} else {
Symbol.Kind = SwiftLangSupport::getUIDForDecl(DInfo.VD, DInfo.IsRef);
SwiftLangSupport::printDisplayName(DInfo.VD, OS);
Symbol.Name = copyAndClearString(Allocator, Buffer);
}
SwiftLangSupport::printUSR(DInfo.OriginalProperty, OS);
if (DInfo.InSynthesizedExtension) {
@@ -958,10 +964,17 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
}
Symbol.USR = copyAndClearString(Allocator, Buffer);
Type InterfaceType;
if (isLiteral) {
InterfaceType = DInfo.LitExpr->getType();
} else {
InterfaceType = DInfo.VD->getInterfaceType();
}
{
PrintOptions Options;
Options.PrintTypeAliasUnderlyingType = true;
DInfo.VD->getInterfaceType().print(OS, Options);
InterfaceType.print(OS, Options);
}
Symbol.TypeName = copyAndClearString(Allocator, Buffer);
@@ -970,7 +983,7 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
// But ParameterizedProtocolType can currently occur in 'typealias'
// declarations. rdar://99176683
// To avoid crashing in USR generation, return an error for now.
if (auto Ty = DInfo.VD->getInterfaceType()) {
if (Type Ty = InterfaceType) {
while (auto MetaTy = Ty->getAs<MetatypeType>()) {
Ty = MetaTy->getInstanceType();
}
@@ -981,7 +994,11 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
}
}
if (isLiteral) {
SwiftLangSupport::printTypeUSR(DInfo.LitExpr->getType(), OS);
} else {
SwiftLangSupport::printDeclTypeUSR(DInfo.VD, OS);
}
Symbol.TypeUSR = copyAndClearString(Allocator, Buffer);
if (DInfo.ContainerType && !DInfo.ContainerType->hasArchetype()) {
@@ -1151,6 +1168,31 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
return llvm::Error::success();
}
static bool
addCursorInfoForLiteral(CursorInfoData &Data, Expr *LitExpr,
SwiftLangSupport &Lang,
const CompilerInvocation &CompInvoc,
SourceLoc CursorLoc,
ArrayRef<ImmutableTextSnapshotRef> PreviousSnaps) {
Type LitType = LitExpr->getType();
if (!LitType || !LitType->getAnyNominal()) {
return false;
}
TypeDecl *Decl = LitType->getAnyNominal();
auto &Symbol = Data.Symbols.emplace_back();
DeclInfo Info(Decl, nullptr, false, false, LitExpr, {}, CompInvoc);
auto Err = fillSymbolInfo(Symbol, Info, CursorLoc, false, Lang, CompInvoc,
PreviousSnaps, Data.Allocator);
bool Success = true;
llvm::handleAllErrors(
std::move(Err), [&](const llvm::StringError &E) {
Data.InternalDiagnostic =
copyCString(E.getMessage(), Data.Allocator);
Success = false;
});
return Success;
}
static bool
addCursorInfoForDecl(CursorInfoData &Data, ResolvedValueRefCursorInfoPtr Info,
bool AddRefactorings, bool AddSymbolGraph,
@@ -1158,8 +1200,8 @@ addCursorInfoForDecl(CursorInfoData &Data, ResolvedValueRefCursorInfoPtr Info,
std::string &Diagnostic,
ArrayRef<ImmutableTextSnapshotRef> PreviousSnaps) {
DeclInfo OrigInfo(Info->getValueD(), Info->getContainerType(), Info->isRef(),
Info->isDynamic(), Info->getReceiverTypes(), Invoc);
DeclInfo CtorTypeInfo(Info->getCtorTyRef(), Type(), true, false,
Info->isDynamic(), nullptr, Info->getReceiverTypes(), Invoc);
DeclInfo CtorTypeInfo(Info->getCtorTyRef(), Type(), true, false, nullptr,
ArrayRef<NominalTypeDecl *>(), Invoc);
DeclInfo &MainInfo = CtorTypeInfo.VD ? CtorTypeInfo : OrigInfo;
if (MainInfo.Unavailable) {
@@ -1196,7 +1238,7 @@ addCursorInfoForDecl(CursorInfoData &Data, ResolvedValueRefCursorInfoPtr Info,
if (!Info->isRef()) {
for (auto D : Info->getShorthandShadowedDecls()) {
CursorSymbolInfo &SymbolInfo = Data.Symbols.emplace_back();
DeclInfo DInfo(D, Type(), /*IsRef=*/true, /*IsDynamic=*/false,
DeclInfo DInfo(D, Type(), /*IsRef=*/true, /*IsDynamic=*/false, nullptr,
ArrayRef<NominalTypeDecl *>(), Invoc);
if (auto Err =
fillSymbolInfo(SymbolInfo, DInfo, Info->getLoc(), AddSymbolGraph,
@@ -1630,21 +1672,54 @@ static void resolveCursor(
}
case CursorInfoKind::ExprStart:
case CursorInfoKind::StmtStart: {
// If code under cursor is literal expression returns Expr,
// otherwise nullptr
auto tryGetLiteralExpr = [](auto CI) -> Expr * {
auto *ExprInfo = dyn_cast<ResolvedExprStartCursorInfo>(CI);
if (!ExprInfo || !ExprInfo->getTrailingExpr()) {
return nullptr;
}
Expr* E = ExprInfo->getTrailingExpr();
if (dyn_cast<LiteralExpr>(E)) {
return E;
}
switch (E->getKind()) {
case ExprKind::Array:
case ExprKind::Dictionary:
case ExprKind::KeyPath: {
return E;
}
default:
return nullptr;
}
};
CursorInfoData Data;
if (Actionables) {
addRefactorings(
Actions, collectRefactorings(CursorInfo, /*ExcludeRename=*/true));
if (!Actions.empty()) {
CursorInfoData Data;
Data.AvailableActions = Actions;
Receiver(RequestResult<CursorInfoData>::fromResult(Data));
return;
}
// Handle literal expression
if (auto *LitExpr = tryGetLiteralExpr(CursorInfo)) {
bool Success = addCursorInfoForLiteral(Data, LitExpr, Lang, CompInvok,
CursorInfo->getLoc(),
getPreviousASTSnaps());
if (!Success) {
Data.Symbols.clear();
Data.AvailableActions.clear();
}
}
CursorInfoData Info;
Info.InternalDiagnostic =
if (Data.InternalDiagnostic.empty() && Data.AvailableActions.empty() &&
Data.Symbols.empty()) {
Data.InternalDiagnostic =
"Resolved to incomplete expression or statement.";
Receiver(RequestResult<CursorInfoData>::fromResult(Info));
}
Receiver(RequestResult<CursorInfoData>::fromResult(Data));
return;
}
case CursorInfoKind::Invalid:

View File

@@ -1945,6 +1945,7 @@ struct ResponseSymbolInfo {
}
OS << ")" << '\n';
if (Name)
OS << Name << '\n';
if (USR)
OS << USR << '\n';

View File

@@ -405,6 +405,7 @@ UID_KINDS = [
KIND('ExprArray', 'source.lang.swift.expr.array'),
KIND('ExprDictionary', 'source.lang.swift.expr.dictionary'),
KIND('ExprObjectLiteral', 'source.lang.swift.expr.object_literal'),
KIND('ExprLiteral', 'source.lang.swift.expr.literal'),
KIND('ExprTuple', 'source.lang.swift.expr.tuple'),
KIND('ExprClosure', 'source.lang.swift.expr.closure'),
KIND('StructureElemId', 'source.lang.swift.structure.elem.id'),