Skip to content

Commit 5422954

Browse files
authored
[Distributed] Offer fixit for import Distributed when it is required (#72948)
1 parent abaa638 commit 5422954

10 files changed

+114
-40
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,9 @@ namespace swift {
727727

728728
static const char *fixItStringFor(const FixItID id);
729729

730+
/// Get the best location where an 'import' fixit might be offered.
731+
SourceLoc getBestAddImportFixItLoc(const Decl *Member) const;
732+
730733
/// Add a token-based replacement fix-it to the currently-active
731734
/// diagnostic.
732735
template <typename... ArgTypes>
@@ -783,6 +786,9 @@ namespace swift {
783786
return fixItReplaceChars(L, L, "%0", {Str});
784787
}
785788

789+
/// Add a fix-it suggesting to 'import' some module.
790+
InFlightDiagnostic &fixItAddImport(StringRef ModuleName);
791+
786792
/// Add an insertion fix-it to the currently-active diagnostic. The
787793
/// text is inserted immediately *after* the token specified.
788794
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef Str) {
@@ -1391,6 +1397,11 @@ namespace swift {
13911397
SourceLoc getDefaultDiagnosticLoc() const {
13921398
return bufferIndirectlyCausingDiagnostic;
13931399
}
1400+
SourceLoc getBestAddImportFixItLoc(const Decl *Member,
1401+
SourceFile *sourceFile) const;
1402+
SourceLoc getBestAddImportFixItLoc(const Decl *Member) const {
1403+
return getBestAddImportFixItLoc(Member, nullptr);
1404+
}
13941405
};
13951406

13961407
/// Remember details about the state of a diagnostic engine and restore them

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5436,7 +5436,7 @@ ERROR(conflicting_default_argument_isolation,none,
54365436
"default argument cannot be both %0 and %1",
54375437
(ActorIsolation, ActorIsolation))
54385438
ERROR(distributed_decl_needs_explicit_distributed_import,none,
5439-
"%kind0 requires explicit import of Distributed module",
5439+
"%kind0 declared without importing module 'Distributed'",
54405440
(const ValueDecl *))
54415441
NOTE(distributed_func_cannot_overload_on_async_only,none,
54425442
"%0 previously declared here, cannot overload distributed methods on effect only", (const ValueDecl *))

lib/AST/DiagnosticEngine.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,75 @@ InFlightDiagnostic::fixItReplaceChars(SourceLoc Start, SourceLoc End,
302302
return *this;
303303
}
304304

305+
SourceLoc
306+
DiagnosticEngine::getBestAddImportFixItLoc(const Decl *Member,
307+
SourceFile *sourceFile) const {
308+
auto &SM = SourceMgr;
309+
310+
SourceLoc bestLoc;
311+
312+
auto SF =
313+
sourceFile ? sourceFile : Member->getDeclContext()->getParentSourceFile();
314+
if (!SF) {
315+
return bestLoc;
316+
}
317+
318+
for (auto item : SF->getTopLevelItems()) {
319+
// If we found an import declaration, we want to insert after it.
320+
if (auto importDecl =
321+
dyn_cast_or_null<ImportDecl>(item.dyn_cast<Decl *>())) {
322+
SourceLoc loc = importDecl->getEndLoc();
323+
if (loc.isValid()) {
324+
bestLoc = Lexer::getLocForEndOfLine(SM, loc);
325+
}
326+
327+
// Keep looking for more import declarations.
328+
continue;
329+
}
330+
331+
// If we got a location based on import declarations, we're done.
332+
if (bestLoc.isValid())
333+
break;
334+
335+
// For any other item, we want to insert before it.
336+
SourceLoc loc = item.getStartLoc();
337+
if (loc.isValid()) {
338+
bestLoc = Lexer::getLocForStartOfLine(SM, loc);
339+
break;
340+
}
341+
}
342+
343+
return bestLoc;
344+
}
345+
346+
InFlightDiagnostic &InFlightDiagnostic::fixItAddImport(StringRef ModuleName) {
347+
assert(IsActive && "Cannot modify an inactive diagnostic");
348+
auto Member = Engine->ActiveDiagnostic->getDecl();
349+
SourceLoc bestLoc = Engine->getBestAddImportFixItLoc(Member);
350+
351+
if (bestLoc.isValid()) {
352+
llvm::SmallString<64> importText;
353+
354+
// @_spi imports.
355+
if (Member->isSPI()) {
356+
auto spiGroups = Member->getSPIGroups();
357+
if (!spiGroups.empty()) {
358+
importText += "@_spi(";
359+
importText += spiGroups[0].str();
360+
importText += ") ";
361+
}
362+
}
363+
364+
importText += "import ";
365+
importText += ModuleName;
366+
importText += "\n";
367+
368+
return fixItInsert(bestLoc, importText);
369+
}
370+
371+
return *this;
372+
}
373+
305374
InFlightDiagnostic &InFlightDiagnostic::fixItExchange(SourceRange R1,
306375
SourceRange R2) {
307376
assert(IsActive && "Cannot modify an inactive diagnostic");

lib/Basic/SourceLoc.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "swift/Basic/Range.h"
1413
#include "swift/Basic/SourceLoc.h"
14+
#include "swift/AST/SourceFile.h"
15+
#include "swift/Basic/Range.h"
1516
#include "swift/Basic/SourceManager.h"
1617
#include "llvm/Support/FileSystem.h"
1718
#include "llvm/Support/MemoryBuffer.h"
1819
#include "llvm/Support/PrettyStackTrace.h"
19-
#include "llvm/Support/raw_ostream.h"
2020
#include "llvm/Support/Signals.h"
21+
#include "llvm/Support/raw_ostream.h"
2122

2223
using namespace swift;
2324

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6195,34 +6195,8 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
61956195
Member->getDescriptiveKind(), Member->getName(),
61966196
definingModule->getName());
61976197

6198-
auto enclosingSF = getDC()->getParentSourceFile();
6199-
SourceLoc bestLoc;
6200-
SourceManager &srcMgr = Member->getASTContext().SourceMgr;
6201-
for (auto item : enclosingSF->getTopLevelItems()) {
6202-
// If we found an import declaration, we want to insert after it.
6203-
if (auto importDecl =
6204-
dyn_cast_or_null<ImportDecl>(item.dyn_cast<Decl *>())) {
6205-
SourceLoc loc = importDecl->getEndLoc();
6206-
if (loc.isValid()) {
6207-
bestLoc = Lexer::getLocForEndOfLine(srcMgr, loc);
6208-
}
6209-
6210-
// Keep looking for more import declarations.
6211-
continue;
6212-
}
6213-
6214-
// If we got a location based on import declarations, we're done.
6215-
if (bestLoc.isValid())
6216-
break;
6217-
6218-
// For any other item, we want to insert before it.
6219-
SourceLoc loc = item.getStartLoc();
6220-
if (loc.isValid()) {
6221-
bestLoc = Lexer::getLocForStartOfLine(srcMgr, loc);
6222-
break;
6223-
}
6224-
}
6225-
6198+
SourceLoc bestLoc =
6199+
getBestAddImportFixItLocation(Member, getDC()->getParentSourceFile());
62266200
if (bestLoc.isValid()) {
62276201
llvm::SmallString<64> importText;
62286202

lib/Sema/CSDiagnostics.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ class FailureDiagnostic {
9090

9191
ConstraintLocator *getLocator() const { return Locator; }
9292

93+
SourceLoc getBestAddImportFixItLocation(const Decl *Member,
94+
SourceFile *sourceFile) const {
95+
auto &engine = Member->getASTContext().Diags;
96+
return engine.getBestAddImportFixItLoc(Member, sourceFile);
97+
}
98+
9399
Type getType(ASTNode node, bool wantRValue = true) const;
94100

95101
/// Get type associated with a given ASTNode without resolving it,

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ DistributedModuleIsAvailableRequest::evaluate(Evaluator &evaluator,
4949
auto DistributedModule = C.getLoadedModule(C.Id_Distributed);
5050
if (!DistributedModule) {
5151
decl->diagnose(diag::distributed_decl_needs_explicit_distributed_import,
52-
decl);
52+
decl)
53+
.fixItAddImport("Distributed");
5354
return false;
5455
}
5556

test/Distributed/distributed_actor_missing_distributed_import.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
actor SomeActor {}
66

77
distributed actor DA {}
8-
// expected-error@-1{{distributed actor 'DA' requires explicit import of Distributed module}}
8+
// expected-error@-1{{distributed actor 'DA' declared without importing module 'Distributed'}}
99

1010
distributed actor class DAC {}
1111
// expected-error@-1{{distributed' can only be applied to 'actor' definitions, and distributed actor-isolated async functions}}
@@ -18,23 +18,23 @@ actor A {
1818
}
1919

2020
actor B {
21-
distributed var neverOk: String { // expected-error{{distributed property 'neverOk' requires explicit import of Distributed module}}
21+
distributed var neverOk: String { // expected-error{{distributed property 'neverOk' declared without importing module 'Distributed'}}
2222
""
2323
}
2424
}
2525

26-
// expected-error@+1{{distributed actor 'DA2' requires explicit import of Distributed module}}
26+
// expected-error@+1{{distributed actor 'DA2' declared without importing module 'Distributed'}}
2727
distributed actor DA2 {
2828

2929
func normal() async {}
3030

31-
// expected-error@+1{{distributed instance method 'dist()' requires explicit import of Distributed module}}
31+
// expected-error@+1{{distributed instance method 'dist()' declared without importing module 'Distributed'}}
3232
distributed func dist() {}
3333

34-
// expected-error@+1{{distributed instance method 'distAsync()' requires explicit import of Distributed module}}
34+
// expected-error@+1{{distributed instance method 'distAsync()' declared without importing module 'Distributed'}}
3535
distributed func distAsync() async {}
3636

37-
// expected-error@+1{{distributed property 'neverOk' requires explicit import of Distributed module}}
37+
// expected-error@+1{{distributed property 'neverOk' declared without importing module 'Distributed'}}
3838
distributed var neverOk: String {
3939
""
4040
}

test/Distributed/distributed_missing_import.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
actor SomeActor { }
66

77
distributed actor MissingImportDistributedActor_0 { }
8-
// expected-error@-1{{distributed actor 'MissingImportDistributedActor_0' requires explicit import of Distributed module}}
8+
// expected-error@-1{{distributed actor 'MissingImportDistributedActor_0' declared without importing module 'Distributed'}}{{5:1-1=import Distributed\n}}
99

1010
let t: DistributedActorSystem // expected-error{{cannot find type 'DistributedActorSystem' in scope}}
11-
let a: ActorAddress // expected-error{{cannot find type 'ActorAddress' in scope}}
1211

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
2+
// REQUIRES: concurrency
3+
// REQUIRES: distributed
4+
5+
import Swift // just any import so we can anchor the fixit onto it
6+
7+
actor SomeActor { }
8+
9+
distributed actor MissingImportDistributedActor_0 { }
10+
// expected-error@-1{{distributed actor 'MissingImportDistributedActor_0' declared without importing module 'Distributed'}}{{6:1-1=import Distributed\n}}
11+
12+
let t: DistributedActorSystem // expected-error{{cannot find type 'DistributedActorSystem' in scope}}
13+

0 commit comments

Comments
 (0)