Skip to content

Commit 795be6c

Browse files
authored
Merge pull request #58734 from ahoppen/pr/fix-completion-in-property-attribute
[CodeCompletion] Support type checking attributes even if they are not part of the AST
2 parents d5e48d7 + 9dbd582 commit 795be6c

16 files changed

+285
-122
lines changed

include/swift/AST/ASTNode.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ namespace swift {
7878

7979
/// Whether the AST node is implicit.
8080
bool isImplicit() const;
81+
82+
friend llvm::hash_code hash_value(ASTNode N) {
83+
return llvm::hash_value(N.getOpaqueValue());
84+
}
8185
};
8286
} // namespace swift
8387

include/swift/AST/TypeCheckRequests.h

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "swift/AST/ActorIsolation.h"
2020
#include "swift/AST/AnyFunctionRef.h"
21+
#include "swift/AST/ASTNode.h"
2122
#include "swift/AST/ASTTypeIDs.h"
2223
#include "swift/AST/Effects.h"
2324
#include "swift/AST/GenericParamList.h"
@@ -1502,12 +1503,82 @@ class TypeCheckFunctionBodyRequest
15021503
readDependencySource(const evaluator::DependencyRecorder &) const;
15031504
};
15041505

1506+
/// Describes the context in which the AST node to type check in a
1507+
/// \c TypeCheckASTNodeAtLocRequest should be searched. This can be either of
1508+
/// two cases:
1509+
/// 1. A \c DeclContext that contains the node representing the location to
1510+
/// type check
1511+
/// 2. If the node that should be type checked that might not be part of the
1512+
/// AST (e.g. because it is a dangling property attribute), an \c ASTNode
1513+
/// that contains the location to type check in together with a DeclContext
1514+
/// in which we should pretend that node occurs.
1515+
class TypeCheckASTNodeAtLocContext {
1516+
DeclContext *DC;
1517+
ASTNode Node;
1518+
1519+
/// Memberwise initializer
1520+
TypeCheckASTNodeAtLocContext(DeclContext *DC, ASTNode Node)
1521+
: DC(DC), Node(Node) {
1522+
assert(DC != nullptr);
1523+
}
1524+
1525+
public:
1526+
static TypeCheckASTNodeAtLocContext declContext(DeclContext *DC) {
1527+
return TypeCheckASTNodeAtLocContext(DC, /*Node=*/nullptr);
1528+
}
1529+
1530+
static TypeCheckASTNodeAtLocContext node(DeclContext *DC, ASTNode Node) {
1531+
assert(!Node.isNull());
1532+
return TypeCheckASTNodeAtLocContext(DC, Node);
1533+
}
1534+
1535+
DeclContext *getDeclContext() const { return DC; }
1536+
1537+
bool isForUnattachedNode() const { return !Node.isNull(); }
1538+
1539+
ASTNode getUnattachedNode() const {
1540+
assert(isForUnattachedNode());
1541+
return Node;
1542+
}
1543+
1544+
ASTNode &getUnattachedNode() {
1545+
assert(isForUnattachedNode());
1546+
return Node;
1547+
}
1548+
1549+
friend llvm::hash_code hash_value(const TypeCheckASTNodeAtLocContext &ctx) {
1550+
return llvm::hash_combine(ctx.DC, ctx.Node);
1551+
}
1552+
1553+
friend bool operator==(const TypeCheckASTNodeAtLocContext &lhs,
1554+
const TypeCheckASTNodeAtLocContext &rhs) {
1555+
return lhs.DC == rhs.DC && lhs.Node == rhs.Node;
1556+
}
1557+
1558+
friend bool operator!=(const TypeCheckASTNodeAtLocContext &lhs,
1559+
const TypeCheckASTNodeAtLocContext &rhs) {
1560+
return !(lhs == rhs);
1561+
}
1562+
1563+
friend SourceLoc
1564+
extractNearestSourceLoc(const TypeCheckASTNodeAtLocContext &ctx) {
1565+
if (!ctx.Node.isNull()) {
1566+
return ctx.Node.getStartLoc();
1567+
} else {
1568+
return extractNearestSourceLoc(ctx.DC);
1569+
}
1570+
}
1571+
};
1572+
1573+
void simple_display(llvm::raw_ostream &out,
1574+
const TypeCheckASTNodeAtLocContext &ctx);
1575+
15051576
/// Request to typecheck a function body element at the given source location.
15061577
///
15071578
/// Produces true if an error occurred, false otherwise.
15081579
class TypeCheckASTNodeAtLocRequest
15091580
: public SimpleRequest<TypeCheckASTNodeAtLocRequest,
1510-
bool(DeclContext *, SourceLoc),
1581+
bool(TypeCheckASTNodeAtLocContext, SourceLoc),
15111582
RequestFlags::Uncached> {
15121583
public:
15131584
using SimpleRequest::SimpleRequest;
@@ -1516,7 +1587,8 @@ class TypeCheckASTNodeAtLocRequest
15161587
friend SimpleRequest;
15171588

15181589
// Evaluation.
1519-
bool evaluate(Evaluator &evaluator, DeclContext *DC, SourceLoc Loc) const;
1590+
bool evaluate(Evaluator &evaluator, TypeCheckASTNodeAtLocContext,
1591+
SourceLoc Loc) const;
15201592
};
15211593

15221594
/// Request to obtain a list of stored properties in a nominal type.
@@ -3416,6 +3488,7 @@ class GetSourceFileAsyncNode
34163488
bool isCached() const { return true; }
34173489
};
34183490

3491+
void simple_display(llvm::raw_ostream &out, ASTNode node);
34193492
void simple_display(llvm::raw_ostream &out, Type value);
34203493
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);
34213494
void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyRequest,
312312
BraceStmt *(AbstractFunctionDecl *), SeparatelyCached,
313313
NoLocationInfo)
314314
SWIFT_REQUEST(TypeChecker, TypeCheckASTNodeAtLocRequest,
315-
bool(DeclContext *, SourceLoc),
315+
bool(TypeCheckASTNodeAtLocContext, SourceLoc),
316316
Uncached, NoLocationInfo)
317317
SWIFT_REQUEST(TypeChecker, UnderlyingTypeRequest, Type(TypeAliasDecl *),
318318
SeparatelyCached, NoLocationInfo)

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ class CodeCompletionCallbacks {
118118
/// Set target decl for attribute if the CC token is in attribute of the decl.
119119
virtual void setAttrTargetDeclKind(Optional<DeclKind> DK) {}
120120

121+
/// Set that the code completion token occurred in a custom attribute. This
122+
/// allows us to type check the custom attribute even if it is not attached to
123+
/// the AST, e.g. because there is no var declaration following it.
124+
virtual void setCompletingInAttribute(CustomAttr *Attr){};
125+
121126
/// Complete expr-dot after we have consumed the dot.
122127
virtual void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) {};
123128

include/swift/Sema/IDETypeChecking.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#ifndef SWIFT_SEMA_IDETYPECHECKING_H
2020
#define SWIFT_SEMA_IDETYPECHECKING_H
2121

22+
#include "swift/AST/ASTNode.h"
2223
#include "swift/AST/Identifier.h"
24+
#include "swift/AST/TypeCheckRequests.h"
2325
#include "swift/Basic/SourceLoc.h"
2426
#include <memory>
2527
#include <tuple>
@@ -140,8 +142,9 @@ namespace swift {
140142
/// Typecheck the given expression.
141143
bool typeCheckExpression(DeclContext *DC, Expr *&parsedExpr);
142144

143-
/// Type check a function body element which is at \p TargetLoc .
144-
bool typeCheckASTNodeAtLoc(DeclContext *DC, SourceLoc TargetLoc);
145+
/// Type check a function body element which is at \p TagetLoc.
146+
bool typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext TypeCheckCtx,
147+
SourceLoc TargetLoc);
145148

146149
/// Thunk around \c TypeChecker::typeCheckForCodeCompletion to make it
147150
/// available to \c swift::ide.

lib/AST/TypeCheckRequests.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ void swift::simple_display(llvm::raw_ostream &out,
6262
}
6363
}
6464

65+
void swift::simple_display(llvm::raw_ostream &out, ASTNode node) {
66+
if (node) {
67+
node.dump(out);
68+
} else {
69+
out << "null";
70+
}
71+
}
72+
6573
void swift::simple_display(llvm::raw_ostream &out, Type type) {
6674
if (type)
6775
type.print(out);

lib/IDE/CodeCompletion.cpp

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
115115
DeclContext *CurDeclContext = nullptr;
116116
DeclAttrKind AttrKind;
117117

118+
/// When the code completion token occurs in a custom attribute, the attribute
119+
/// it occurs in. Used so we can complete inside the attribute even if it's
120+
/// not attached to the AST, e.g. because there is no var decl it could be
121+
/// attached to.
122+
CustomAttr *AttrWithCompletion = nullptr;
123+
118124
/// In situations when \c SyntaxKind hints or determines
119125
/// completions, i.e. a precedence group attribute, this
120126
/// can be set and used to control the code completion scenario.
@@ -227,6 +233,11 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
227233
AttTargetDK = DK;
228234
}
229235

236+
void setCompletingInAttribute(CustomAttr *Attr) override {
237+
AttrWithCompletion = Attr;
238+
CurDeclContext = P.CurDeclContext;
239+
}
240+
230241
void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) override;
231242
void completeStmtOrExpr(CodeCompletionExpr *E) override;
232243
void completePostfixExprBeginning(CodeCompletionExpr *E) override;
@@ -1358,16 +1369,24 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13581369
? ParsedExpr->getLoc()
13591370
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc();
13601371

1361-
switch (Kind) {
1362-
case CompletionKind::DotExpr: {
1363-
assert(CodeCompleteTokenExpr);
1364-
assert(CurDeclContext);
1365-
1366-
DotExprTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr,
1367-
CurDeclContext);
1372+
auto typeCheckWithLookup = [this, &CompletionLoc](
1373+
TypeCheckCompletionCallback &Lookup) {
13681374
llvm::SaveAndRestore<TypeCheckCompletionCallback*>
13691375
CompletionCollector(Context.CompletionCallback, &Lookup);
1370-
typeCheckContextAt(CurDeclContext, CompletionLoc);
1376+
if (AttrWithCompletion) {
1377+
/// The attribute might not be attached to the AST if there is no var decl
1378+
/// it could be attached to. Type check it standalone.
1379+
ASTNode Call = CallExpr::create(
1380+
CurDeclContext->getASTContext(), AttrWithCompletion->getTypeExpr(),
1381+
AttrWithCompletion->getArgs(), /*implicit=*/true);
1382+
typeCheckContextAt(
1383+
TypeCheckASTNodeAtLocContext::node(CurDeclContext, Call),
1384+
CompletionLoc);
1385+
} else {
1386+
typeCheckContextAt(
1387+
TypeCheckASTNodeAtLocContext::declContext(CurDeclContext),
1388+
CompletionLoc);
1389+
}
13711390

13721391
// This (hopefully) only happens in cases where the expression isn't
13731392
// typechecked during normal compilation either (e.g. member completion in a
@@ -1376,6 +1395,16 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13761395
// tooling in general though.
13771396
if (!Lookup.gotCallback())
13781397
Lookup.fallbackTypeCheck(CurDeclContext);
1398+
};
1399+
1400+
switch (Kind) {
1401+
case CompletionKind::DotExpr: {
1402+
assert(CodeCompleteTokenExpr);
1403+
assert(CurDeclContext);
1404+
1405+
DotExprTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr,
1406+
CurDeclContext);
1407+
typeCheckWithLookup(Lookup);
13791408

13801409
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
13811410

@@ -1390,12 +1419,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13901419

13911420
UnresolvedMemberTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr,
13921421
CurDeclContext);
1393-
llvm::SaveAndRestore<TypeCheckCompletionCallback*>
1394-
CompletionCollector(Context.CompletionCallback, &Lookup);
1395-
typeCheckContextAt(CurDeclContext, CompletionLoc);
1396-
1397-
if (!Lookup.gotCallback())
1398-
Lookup.fallbackTypeCheck(CurDeclContext);
1422+
typeCheckWithLookup(Lookup);
13991423

14001424
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
14011425
Lookup.deliverResults(CurDeclContext, DotLoc, CompletionContext, Consumer);
@@ -1408,9 +1432,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
14081432
// so we can safely cast the \c ParsedExpr back to a \c KeyPathExpr.
14091433
auto KeyPath = cast<KeyPathExpr>(ParsedExpr);
14101434
KeyPathTypeCheckCompletionCallback Lookup(KeyPath);
1411-
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
1412-
Context.CompletionCallback, &Lookup);
1413-
typeCheckContextAt(CurDeclContext, CompletionLoc);
1435+
typeCheckWithLookup(Lookup);
14141436

14151437
Lookup.deliverResults(CurDeclContext, DotLoc, CompletionContext, Consumer);
14161438
return true;
@@ -1420,13 +1442,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
14201442
assert(CurDeclContext);
14211443
ArgumentTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr,
14221444
CurDeclContext);
1423-
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
1424-
Context.CompletionCallback, &Lookup);
1425-
typeCheckContextAt(CurDeclContext, CompletionLoc);
1426-
1427-
if (!Lookup.gotCallback()) {
1428-
Lookup.fallbackTypeCheck(CurDeclContext);
1429-
}
1445+
typeCheckWithLookup(Lookup);
14301446

14311447
Lookup.deliverResults(ShouldCompleteCallPatternAfterParen, CompletionLoc,
14321448
CurDeclContext, CompletionContext, Consumer);
@@ -1453,13 +1469,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
14531469
// need to have a TypeCheckCompletionCallback so we can call
14541470
// deliverResults on it to deliver the keyword results from the completion
14551471
// context's result sink to the consumer.
1456-
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
1457-
Context.CompletionCallback, &Lookup);
1458-
typeCheckContextAt(CurDeclContext, CompletionLoc);
1459-
1460-
if (!Lookup.gotCallback()) {
1461-
Lookup.fallbackTypeCheck(CurDeclContext);
1462-
}
1472+
typeCheckWithLookup(Lookup);
14631473
}
14641474

14651475
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
@@ -1474,13 +1484,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
14741484

14751485
AfterPoundExprCompletion Lookup(CodeCompleteTokenExpr, CurDeclContext,
14761486
ParentStmtKind);
1477-
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
1478-
Context.CompletionCallback, &Lookup);
1479-
typeCheckContextAt(CurDeclContext, CompletionLoc);
1480-
1481-
if (!Lookup.gotCallback()) {
1482-
Lookup.fallbackTypeCheck(CurDeclContext);
1483-
}
1487+
typeCheckWithLookup(Lookup);
14841488

14851489
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
14861490

@@ -1550,7 +1554,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
15501554

15511555
undoSingleExpressionReturn(CurDeclContext);
15521556
typeCheckContextAt(
1553-
CurDeclContext,
1557+
TypeCheckASTNodeAtLocContext::declContext(CurDeclContext),
15541558
ParsedExpr
15551559
? ParsedExpr->getLoc()
15561560
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc());

lib/IDE/ConformingMethodList.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ void ConformingMethodListCallbacks::doneParsing() {
6868
if (!ParsedExpr)
6969
return;
7070

71-
typeCheckContextAt(CurDeclContext, ParsedExpr->getLoc());
71+
typeCheckContextAt(TypeCheckASTNodeAtLocContext::declContext(CurDeclContext),
72+
ParsedExpr->getLoc());
7273

7374
Type T = ParsedExpr->getType();
7475

0 commit comments

Comments
 (0)