Skip to content

Commit fe228a4

Browse files
committed
[IDE] Allow typeCheckASTNodeAtLoc to type check declarations
We need the ability to type check declarations when entering using `typeCheckASTNodeAtLoc` for solver-based cursor info. This causes more context to be type checked for code completion and breaks some type completion after dot if the base type is not fully qualified because the type checker will mark `TypeRepr` as invalid. But since we don’t actually need to run the type checker for type member completion, we can just not call `typeCheckContextAt` for `TypeIdentifierWithDot` completions.
1 parent 8e857d5 commit fe228a4

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,25 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
174174

175175
/// \returns true on success, false on failure.
176176
bool typecheckParsedType() {
177+
// If the type appeared inside an extension, make sure that extension has
178+
// been bound.
179+
auto SF = CurDeclContext->getParentSourceFile();
180+
auto visitTopLevelDecl = [&](Decl *D) {
181+
if (auto ED = dyn_cast<ExtensionDecl>(D)) {
182+
if (ED->getSourceRange().contains(ParsedTypeLoc.getLoc())) {
183+
ED->computeExtendedNominal();
184+
}
185+
}
186+
};
187+
for (auto item : SF->getTopLevelItems()) {
188+
if (auto D = item.dyn_cast<Decl *>()) {
189+
visitTopLevelDecl(D);
190+
}
191+
}
192+
for (auto *D : SF->getHoistedDecls()) {
193+
visitTopLevelDecl(D);
194+
}
195+
177196
assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr");
178197
if (ParsedTypeLoc.wasValidated() && !ParsedTypeLoc.isError()) {
179198
return true;
@@ -1576,11 +1595,14 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) {
15761595
return;
15771596

15781597
undoSingleExpressionReturn(CurDeclContext);
1579-
typeCheckContextAt(
1580-
TypeCheckASTNodeAtLocContext::declContext(CurDeclContext),
1581-
ParsedExpr
1582-
? ParsedExpr->getLoc()
1583-
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc());
1598+
if (Kind != CompletionKind::TypeIdentifierWithDot) {
1599+
// Type member completion does not need a type-checked AST.
1600+
typeCheckContextAt(
1601+
TypeCheckASTNodeAtLocContext::declContext(CurDeclContext),
1602+
ParsedExpr
1603+
? ParsedExpr->getLoc()
1604+
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc());
1605+
}
15841606

15851607
// Add keywords even if type checking fails completely.
15861608
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);

lib/Sema/TypeCheckStmt.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,9 +1926,19 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
19261926
if (!braceCharRange.contains(Loc))
19271927
return Action::SkipChildren(S);
19281928

1929-
// Reset the node found in a parent context.
1930-
if (!brace->isImplicit())
1931-
FoundNode = nullptr;
1929+
// Reset the node found in a parent context if it's not part of this
1930+
// brace statement.
1931+
// We must not reset FoundNode if it's inside thei BraceStmt's source
1932+
// range because the found node could be inside a capture list, which is
1933+
// syntactically part of the brace stmt's range but won't be walked as
1934+
// a child of the brace stmt.
1935+
if (!brace->isImplicit() && FoundNode) {
1936+
auto foundNodeCharRange = Lexer::getCharSourceRangeFromSourceRange(
1937+
SM, FoundNode->getSourceRange());
1938+
if (!braceCharRange.contains(foundNodeCharRange)) {
1939+
FoundNode = nullptr;
1940+
}
1941+
}
19321942

19331943
for (ASTNode &node : brace->getElements()) {
19341944
if (SM.isBeforeInBuffer(Loc, node.getStartLoc()))
@@ -1985,6 +1995,18 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
19851995
PreWalkAction walkToDeclPre(Decl *D) override {
19861996
if (auto *newDC = dyn_cast<DeclContext>(D))
19871997
DC = newDC;
1998+
1999+
if (!SM.isBeforeInBuffer(Loc, D->getStartLoc())) {
2000+
// NOTE: We need to check the character loc here because the target
2001+
// loc can be inside the last token of the node. i.e. interpolated
2002+
// string.
2003+
SourceLoc endLoc = Lexer::getLocForEndOfToken(SM, D->getEndLoc());
2004+
if (!(SM.isBeforeInBuffer(endLoc, Loc) || endLoc == Loc)) {
2005+
if (!isa<TopLevelCodeDecl>(D)) {
2006+
FoundNode = new ASTNode(D);
2007+
}
2008+
}
2009+
}
19882010
return Action::Continue();
19892011
}
19902012

@@ -1998,7 +2020,7 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
19982020
}
19992021

20002022
// Nothing found at the location, or the decl context does not own the 'Loc'.
2001-
if (finder.isNull())
2023+
if (finder.isNull() || !finder.getDeclContext())
20022024
return true;
20032025

20042026
DeclContext *DC = finder.getDeclContext();

0 commit comments

Comments
 (0)