Skip to content

Commit c4fff12

Browse files
committed
[CodeCompletion] Lazily compute contextual diagnostics
1 parent 025e919 commit c4fff12

10 files changed

+114
-110
lines changed

include/swift/IDE/CodeCompletionResult.h

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ class ContextFreeCodeCompletionResult {
341341
NullTerminatedStringRef DiagnosticMessage;
342342
NullTerminatedStringRef FilterName;
343343

344+
/// If the result represents a \c ValueDecl the name by which this decl should
345+
/// be refered to in diagnostics.
346+
NullTerminatedStringRef NameForDiagnostics;
347+
344348
public:
345349
/// Memberwise initializer. \p AssociatedKInd is opaque and will be
346350
/// interpreted based on \p Kind. If \p KnownOperatorKind is \c None and the
@@ -361,13 +365,15 @@ class ContextFreeCodeCompletionResult {
361365
ContextFreeNotRecommendedReason NotRecommended,
362366
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
363367
NullTerminatedStringRef DiagnosticMessage,
364-
NullTerminatedStringRef FilterName)
368+
NullTerminatedStringRef FilterName,
369+
NullTerminatedStringRef NameForDiagnostics)
365370
: Kind(Kind), KnownOperatorKind(KnownOperatorKind), IsSystem(IsSystem),
366371
CompletionString(CompletionString), ModuleName(ModuleName),
367372
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
368373
ResultType(ResultType), NotRecommended(NotRecommended),
369374
DiagnosticSeverity(DiagnosticSeverity),
370-
DiagnosticMessage(DiagnosticMessage), FilterName(FilterName) {
375+
DiagnosticMessage(DiagnosticMessage), FilterName(FilterName),
376+
NameForDiagnostics(NameForDiagnostics) {
371377
this->AssociatedKind.Opaque = AssociatedKind;
372378
assert((NotRecommended == ContextFreeNotRecommendedReason::None) ==
373379
(DiagnosticSeverity == CodeCompletionDiagnosticSeverity::None) &&
@@ -491,6 +497,10 @@ class ContextFreeCodeCompletionResult {
491497

492498
NullTerminatedStringRef getFilterName() const { return FilterName; }
493499

500+
NullTerminatedStringRef getNameForDiagnostics() const {
501+
return NameForDiagnostics;
502+
}
503+
494504
bool isOperator() const {
495505
if (getKind() == CodeCompletionResultKind::Declaration) {
496506
switch (getAssociatedDeclKind()) {
@@ -528,11 +538,6 @@ class CodeCompletionResult {
528538
ContextualNotRecommendedReason NotRecommended : 4;
529539
static_assert(int(ContextualNotRecommendedReason::MAX_VALUE) < 1 << 4, "");
530540

531-
CodeCompletionDiagnosticSeverity DiagnosticSeverity : 3;
532-
static_assert(int(CodeCompletionDiagnosticSeverity::MAX_VALUE) < 1 << 3, "");
533-
534-
NullTerminatedStringRef DiagnosticMessage;
535-
536541
/// The number of bytes to the left of the code completion point that
537542
/// should be erased first if this completion string is inserted in the
538543
/// editor buffer.
@@ -553,14 +558,10 @@ class CodeCompletionResult {
553558
SemanticContextKind SemanticContext,
554559
CodeCompletionFlair Flair, uint8_t NumBytesToErase,
555560
CodeCompletionResultTypeRelation TypeDistance,
556-
ContextualNotRecommendedReason NotRecommended,
557-
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
558-
NullTerminatedStringRef DiagnosticMessage)
561+
ContextualNotRecommendedReason NotRecommended)
559562
: ContextFree(ContextFree), SemanticContext(SemanticContext),
560563
Flair(Flair.toRaw()), NotRecommended(NotRecommended),
561-
DiagnosticSeverity(DiagnosticSeverity),
562-
DiagnosticMessage(DiagnosticMessage), NumBytesToErase(NumBytesToErase),
563-
TypeDistance(TypeDistance) {}
564+
NumBytesToErase(NumBytesToErase), TypeDistance(TypeDistance) {}
564565

565566
public:
566567
/// Enrich a \c ContextFreeCodeCompletionResult with the following contextual
@@ -578,9 +579,7 @@ class CodeCompletionResult {
578579
const ExpectedTypeContext *TypeContext,
579580
const DeclContext *DC,
580581
const USRBasedTypeContext *USRTypeContext,
581-
ContextualNotRecommendedReason NotRecommended,
582-
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
583-
NullTerminatedStringRef DiagnosticMessage);
582+
ContextualNotRecommendedReason NotRecommended);
584583

585584
const ContextFreeCodeCompletionResult &getContextFreeResult() const {
586585
return ContextFree;
@@ -701,22 +700,19 @@ class CodeCompletionResult {
701700

702701
/// Get the contextual diagnostic severity. This disregards context-free
703702
/// diagnostics.
704-
CodeCompletionDiagnosticSeverity getContextualDiagnosticSeverity() const {
705-
return DiagnosticSeverity;
706-
}
703+
CodeCompletionDiagnosticSeverity getContextualDiagnosticSeverity() const;
707704

708705
/// Get the contextual diagnostic message. This disregards context-free
709706
/// diagnostics.
710-
NullTerminatedStringRef getContextualDiagnosticMessage() const {
711-
return DiagnosticMessage;
712-
}
707+
NullTerminatedStringRef
708+
getContextualDiagnosticMessage(SmallVectorImpl<char> &Scratch) const;
713709

714710
/// Return the contextual diagnostic severity if there was a contextual
715711
/// diagnostic. If there is no contextual diagnostic, return the context-free
716712
/// diagnostic severity.
717713
CodeCompletionDiagnosticSeverity getDiagnosticSeverity() const {
718714
if (NotRecommended != ContextualNotRecommendedReason::None) {
719-
return DiagnosticSeverity;
715+
return getContextualDiagnosticSeverity();
720716
} else {
721717
return getContextFreeResult().getDiagnosticSeverity();
722718
}
@@ -725,9 +721,10 @@ class CodeCompletionResult {
725721
/// Return the contextual diagnostic message if there was a contextual
726722
/// diagnostic. If there is no contextual diagnostic, return the context-free
727723
/// diagnostic message.
728-
NullTerminatedStringRef getDiagnosticMessage() const {
724+
NullTerminatedStringRef
725+
getDiagnosticMessage(SmallVectorImpl<char> &Scratch) const {
729726
if (NotRecommended != ContextualNotRecommendedReason::None) {
730-
return DiagnosticMessage;
727+
return getContextualDiagnosticMessage(Scratch);
731728
} else {
732729
return getContextFreeResult().getDiagnosticMessage();
733730
}

lib/IDE/CodeCompletionCache.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ CodeCompletionCache::~CodeCompletionCache() {}
103103
///
104104
/// This should be incremented any time we commit a change to the format of the
105105
/// cached results. This isn't expected to change very often.
106-
static constexpr uint32_t onDiskCompletionCacheVersion = 7; // Store whether a type can be used as attribute
106+
static constexpr uint32_t onDiskCompletionCacheVersion =
107+
8; // Store name for diagnostics
107108

108109
/// Deserializes CodeCompletionResults from \p in and stores them in \p V.
109110
/// \see writeCacheModule.
@@ -239,6 +240,7 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
239240
auto briefDocIndex = read32le(cursor);
240241
auto diagMessageIndex = read32le(cursor);
241242
auto filterNameIndex = read32le(cursor);
243+
auto nameForDiagnosticsIndex = read32le(cursor);
242244

243245
auto assocUSRCount = read32le(cursor);
244246
SmallVector<NullTerminatedStringRef, 4> assocUSRs;
@@ -258,13 +260,14 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
258260
auto briefDocComment = getString(briefDocIndex);
259261
auto diagMessage = getString(diagMessageIndex);
260262
auto filterName = getString(filterNameIndex);
263+
auto nameForDiagnostics = getString(nameForDiagnosticsIndex);
261264

262265
ContextFreeCodeCompletionResult *result =
263266
new (*V.Allocator) ContextFreeCodeCompletionResult(
264267
kind, associatedKind, opKind, isSystem, string, moduleName,
265268
briefDocComment, makeArrayRef(assocUSRs).copy(*V.Allocator),
266269
CodeCompletionResultType(resultTypes), notRecommended, diagSeverity,
267-
diagMessage, filterName);
270+
diagMessage, filterName, nameForDiagnostics);
268271

269272
V.Results.push_back(result);
270273
}
@@ -426,6 +429,7 @@ static void writeCachedModule(llvm::raw_ostream &out,
426429
LE.write(addString(R->getBriefDocComment())); // index into strings
427430
LE.write(addString(R->getDiagnosticMessage())); // index into strings
428431
LE.write(addString(R->getFilterName())); // index into strings
432+
LE.write(addString(R->getNameForDiagnostics())); // index into strings
429433

430434
LE.write(static_cast<uint32_t>(R->getAssociatedUSRs().size()));
431435
for (unsigned i = 0; i < R->getAssociatedUSRs().size(); ++i) {

lib/IDE/CodeCompletionConsumer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ static MutableArrayRef<CodeCompletionResult *> copyCodeCompletionResults(
8585
*contextFreeResult, SemanticContextKind::OtherModule,
8686
CodeCompletionFlair(),
8787
/*numBytesToErase=*/0, TypeContext, DC, &USRTypeContext,
88-
ContextualNotRecommendedReason::None,
89-
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"");
88+
ContextualNotRecommendedReason::None);
9089
targetSink.Results.push_back(contextualResult);
9190
}
9291

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -157,31 +157,3 @@ bool swift::ide::getContextFreeCompletionDiagnostics(
157157
}
158158
return true;
159159
}
160-
161-
bool swift::ide::getContextualCompletionDiagnostics(
162-
ContextualNotRecommendedReason Reason, const ValueDecl *D,
163-
CodeCompletionDiagnosticSeverity &Severity, llvm::raw_ostream &Out) {
164-
CodeCompletionDiagnostics Diag(D->getASTContext());
165-
switch (Reason) {
166-
case ContextualNotRecommendedReason::InvalidAsyncContext:
167-
// FIXME: Could we use 'diag::async_in_nonasync_function'?
168-
return Diag.getDiagnostics(
169-
Severity, Out, diag::ide_async_in_nonasync_context, D->getName());
170-
case ContextualNotRecommendedReason::CrossActorReference:
171-
return Diag.getDiagnostics(
172-
Severity, Out, diag::ide_cross_actor_reference_swift5, D->getName());
173-
case ContextualNotRecommendedReason::RedundantImport:
174-
return Diag.getDiagnostics(Severity, Out, diag::ide_redundant_import,
175-
D->getName());
176-
case ContextualNotRecommendedReason::RedundantImportIndirect:
177-
return Diag.getDiagnostics(
178-
Severity, Out, diag::ide_redundant_import_indirect, D->getName());
179-
case ContextualNotRecommendedReason::VariableUsedInOwnDefinition:
180-
return Diag.getDiagnostics(
181-
Severity, Out, diag::recursive_accessor_reference,
182-
D->getName().getBaseIdentifier(), /*"getter"*/ 0);
183-
case ContextualNotRecommendedReason::None:
184-
llvm_unreachable("invalid not recommended reason");
185-
}
186-
return true;
187-
}

lib/IDE/CodeCompletionDiagnostics.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,6 @@ bool getContextFreeCompletionDiagnostics(
2929
ContextFreeNotRecommendedReason reason, const ValueDecl *D,
3030
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out);
3131

32-
/// Populate \p severity and \p Out with the contextual diagnostics for \p D.
33-
/// See \c NotRecommendedReason for an explaination of context-free vs.
34-
/// contextual diagnostics.
35-
/// Returns \c true if it fails to generate the diagnostics.
36-
bool getContextualCompletionDiagnostics(
37-
ContextualNotRecommendedReason reason, const ValueDecl *D,
38-
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out);
39-
4032
} // namespace ide
4133
} // namespace swift
4234

lib/IDE/CodeCompletionResult.cpp

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
3939
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"", BriefDocComment,
4040
/*AssociatedUSRs=*/{}, ResultType, NotRecommended, DiagnosticSeverity,
4141
DiagnosticMessage,
42-
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()));
42+
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
43+
/*NameForDiagnostics=*/"");
4344
}
4445

4546
ContextFreeCodeCompletionResult *
@@ -57,7 +58,8 @@ ContextFreeCodeCompletionResult::createKeywordResult(
5758
/*ModuleName=*/"", BriefDocComment,
5859
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
5960
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
60-
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()));
61+
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
62+
/*NameForDiagnostics=*/"");
6163
}
6264

6365
ContextFreeCodeCompletionResult *
@@ -75,7 +77,19 @@ ContextFreeCodeCompletionResult::createLiteralResult(
7577
/*BriefDocComment=*/"",
7678
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
7779
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
78-
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()));
80+
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
81+
/*NameForDiagnostics=*/"");
82+
}
83+
84+
static NullTerminatedStringRef
85+
getDeclNameForDiagnostics(const Decl *D, CodeCompletionResultSink &Sink) {
86+
if (auto VD = dyn_cast<ValueDecl>(D)) {
87+
llvm::SmallString<64> Scratch;
88+
return NullTerminatedStringRef(VD->getName().getString(Scratch),
89+
Sink.getAllocator());
90+
} else {
91+
return "";
92+
}
7993
}
8094

8195
ContextFreeCodeCompletionResult *
@@ -98,7 +112,8 @@ ContextFreeCodeCompletionResult::createDeclResult(
98112
CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl),
99113
CompletionString, ModuleName, BriefDocComment, AssociatedUSRs, ResultType,
100114
NotRecommended, DiagnosticSeverity, DiagnosticMessage,
101-
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()));
115+
getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
116+
/*NameForDiagnostics=*/getDeclNameForDiagnostics(AssociatedDecl, Sink));
102117
}
103118

104119
CodeCompletionOperatorKind
@@ -267,32 +282,78 @@ CodeCompletionResult::CodeCompletionResult(
267282
SemanticContextKind SemanticContext, CodeCompletionFlair Flair,
268283
uint8_t NumBytesToErase, const ExpectedTypeContext *TypeContext,
269284
const DeclContext *DC, const USRBasedTypeContext *USRTypeContext,
270-
ContextualNotRecommendedReason NotRecommended,
271-
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
272-
NullTerminatedStringRef DiagnosticMessage)
285+
ContextualNotRecommendedReason NotRecommended)
273286
: ContextFree(ContextFree), SemanticContext(SemanticContext),
274287
Flair(Flair.toRaw()), NotRecommended(NotRecommended),
275-
DiagnosticSeverity(DiagnosticSeverity),
276-
DiagnosticMessage(DiagnosticMessage), NumBytesToErase(NumBytesToErase),
288+
NumBytesToErase(NumBytesToErase),
277289
TypeDistance(ContextFree.getResultType().calculateTypeRelation(
278290
TypeContext, DC, USRTypeContext)) {}
279291

280292
CodeCompletionResult *
281293
CodeCompletionResult::withFlair(CodeCompletionFlair NewFlair,
282294
CodeCompletionResultSink &Sink) const {
283-
return new (*Sink.Allocator) CodeCompletionResult(
284-
ContextFree, SemanticContext, NewFlair, NumBytesToErase, TypeDistance,
285-
NotRecommended, DiagnosticSeverity, DiagnosticMessage);
295+
return new (*Sink.Allocator)
296+
CodeCompletionResult(ContextFree, SemanticContext, NewFlair,
297+
NumBytesToErase, TypeDistance, NotRecommended);
286298
}
287299

288300
CodeCompletionResult *
289301
CodeCompletionResult::withContextFreeResultSemanticContextAndFlair(
290302
const ContextFreeCodeCompletionResult &NewContextFree,
291303
SemanticContextKind NewSemanticContext, CodeCompletionFlair NewFlair,
292304
CodeCompletionResultSink &Sink) const {
293-
return new (*Sink.Allocator) CodeCompletionResult(
294-
NewContextFree, NewSemanticContext, NewFlair, NumBytesToErase,
295-
TypeDistance, NotRecommended, DiagnosticSeverity, DiagnosticMessage);
305+
return new (*Sink.Allocator)
306+
CodeCompletionResult(NewContextFree, NewSemanticContext, NewFlair,
307+
NumBytesToErase, TypeDistance, NotRecommended);
308+
}
309+
310+
CodeCompletionDiagnosticSeverity
311+
CodeCompletionResult::getContextualDiagnosticSeverity() const {
312+
switch (NotRecommended) {
313+
case ContextualNotRecommendedReason::InvalidAsyncContext:
314+
return CodeCompletionDiagnosticSeverity::Error;
315+
case ContextualNotRecommendedReason::CrossActorReference:
316+
return CodeCompletionDiagnosticSeverity::Warning;
317+
case ContextualNotRecommendedReason::RedundantImport:
318+
return CodeCompletionDiagnosticSeverity::Warning;
319+
case ContextualNotRecommendedReason::RedundantImportIndirect:
320+
return CodeCompletionDiagnosticSeverity::Note;
321+
case ContextualNotRecommendedReason::VariableUsedInOwnDefinition:
322+
return CodeCompletionDiagnosticSeverity::Warning;
323+
case ContextualNotRecommendedReason::None:
324+
return CodeCompletionDiagnosticSeverity::None;
325+
}
326+
}
327+
328+
NullTerminatedStringRef CodeCompletionResult::getContextualDiagnosticMessage(
329+
SmallVectorImpl<char> &Scratch) const {
330+
auto NameForDiagnostics = ContextFree.getNameForDiagnostics();
331+
llvm::raw_svector_ostream Out(Scratch);
332+
switch (NotRecommended) {
333+
case ContextualNotRecommendedReason::InvalidAsyncContext:
334+
Out << "async '" << NameForDiagnostics
335+
<< "' used in a context that does not support concurrency";
336+
break;
337+
case ContextualNotRecommendedReason::CrossActorReference:
338+
Out << "actor-isolated '" << NameForDiagnostics
339+
<< "' should only be referenced from inside the actor";
340+
break;
341+
case ContextualNotRecommendedReason::RedundantImport:
342+
Out << "module '" << NameForDiagnostics << "' is already imported";
343+
break;
344+
case ContextualNotRecommendedReason::RedundantImportIndirect:
345+
Out << "module '" << NameForDiagnostics
346+
<< "' is already imported via another module import";
347+
break;
348+
case ContextualNotRecommendedReason::VariableUsedInOwnDefinition:
349+
Out << "attempting to access '" << NameForDiagnostics
350+
<< "' within its own getter";
351+
break;
352+
case ContextualNotRecommendedReason::None:
353+
llvm_unreachable("invalid not recommended reason");
354+
}
355+
Out << '\0';
356+
return NullTerminatedStringRef(Out.str().data(), Out.str().size() - 1);
296357
}
297358

298359
void CodeCompletionResult::printPrefix(raw_ostream &OS) const {

lib/IDE/CodeCompletionResultBuilder.cpp

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -161,26 +161,8 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
161161
*ContextFreeResult, SemanticContextKind::None, CodeCompletionFlair(),
162162
/*NumBytesToErase=*/0, /*TypeContext=*/nullptr,
163163
/*DC=*/nullptr, /*USRTypeContext=*/nullptr,
164-
ContextualNotRecommendedReason::None,
165-
CodeCompletionDiagnosticSeverity::None, "");
164+
ContextualNotRecommendedReason::None);
166165
} else {
167-
CodeCompletionDiagnosticSeverity ContextualDiagnosticSeverity =
168-
CodeCompletionDiagnosticSeverity::None;
169-
NullTerminatedStringRef ContextualDiagnosticMessage;
170-
if (ContextualNotRecReason != ContextualNotRecommendedReason::None) {
171-
// FIXME: We should generate the message lazily.
172-
if (const auto *VD = dyn_cast<ValueDecl>(AssociatedDecl)) {
173-
CodeCompletionDiagnosticSeverity severity;
174-
SmallString<256> message;
175-
llvm::raw_svector_ostream messageOS(message);
176-
if (!getContextualCompletionDiagnostics(ContextualNotRecReason, VD,
177-
severity, messageOS)) {
178-
ContextualDiagnosticSeverity = severity;
179-
ContextualDiagnosticMessage =
180-
NullTerminatedStringRef(message, Allocator);
181-
}
182-
}
183-
}
184166
assert(
185167
ContextFreeResult != nullptr &&
186168
"ContextFreeResult should have been constructed by the switch above");
@@ -190,8 +172,7 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
190172
// for USRTypeContext.
191173
return new (Allocator) CodeCompletionResult(
192174
*ContextFreeResult, SemanticContext, Flair, NumBytesToErase,
193-
TypeContext, DC, /*USRTypeContext=*/nullptr, ContextualNotRecReason,
194-
ContextualDiagnosticSeverity, ContextualDiagnosticMessage);
175+
TypeContext, DC, /*USRTypeContext=*/nullptr, ContextualNotRecReason);
195176
}
196177
}
197178

0 commit comments

Comments
 (0)