Skip to content

Commit 061ce72

Browse files
CrazyFanFankongkaikai
authored andcommitted
[Diagnostics] Add fix-it to @main struct without main static function.
1 parent 98b1494 commit 061ce72

10 files changed

+125
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4264,6 +4264,14 @@ ERROR(attr_MainType_without_main,none,
42644264
"%0 is annotated with '@main' and must provide a main static function "
42654265
"of type %" SELECT_APPLICATION_MAIN_TYPES "1",
42664266
(const ValueDecl *, bool))
4267+
NOTE(note_add_main_sync,none,
4268+
"add 'static func main()'", ())
4269+
NOTE(note_add_main_sync_throws,none,
4270+
"add 'static func main() throws'", ())
4271+
NOTE(note_add_main_async,none,
4272+
"add 'static func main() async'", ())
4273+
NOTE(note_add_main_async_throws,none,
4274+
"add 'static func main() async throws'", ())
42674275

42684276
#undef SELECT_APPLICATION_MAIN_TYPES
42694277
#undef SELECT_APPLICATION_MAIN

lib/Sema/TypeCheckAttr.cpp

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "TypeCheckObjC.h"
2323
#include "TypeCheckType.h"
2424
#include "TypeChecker.h"
25+
#include "swift/AST/ASTPrinter.h"
2526
#include "swift/AST/ASTVisitor.h"
2627
#include "swift/AST/AvailabilityInference.h"
2728
#include "swift/AST/ClangModuleLoader.h"
@@ -3081,6 +3082,36 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
30813082
return std::make_pair(body, /*typechecked=*/false);
30823083
}
30833084

3085+
static llvm::SmallString<128>
3086+
generateMainFunctionText(ASTContext &C, NominalTypeDecl *parentDecl,
3087+
bool isThrows, bool isAsync) {
3088+
StringRef ExtraIndent;
3089+
StringRef CurrentIndent = Lexer::getIndentationForLine(
3090+
C.SourceMgr, parentDecl->getStartLoc(), &ExtraIndent);
3091+
std::string MethodIndent = (CurrentIndent + ExtraIndent).str();
3092+
3093+
llvm::SmallString<128> Text;
3094+
llvm::raw_svector_ostream OS(Text);
3095+
ExtraIndentStreamPrinter Printer(OS, MethodIndent);
3096+
3097+
Printer.printNewline();
3098+
3099+
Printer << "static func main() ";
3100+
if (isAsync)
3101+
Printer << "async ";
3102+
if (isThrows)
3103+
Printer << "throws ";
3104+
3105+
// Print the "{ <#code#> }" placeholder body.
3106+
Printer << "{\n";
3107+
Printer.printIndent();
3108+
Printer << ExtraIndent << getCodePlaceholder();
3109+
Printer.printNewline();
3110+
Printer << "}\n";
3111+
3112+
return Text;
3113+
}
3114+
30843115
FuncDecl *
30853116
SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
30863117
Decl *D) const {
@@ -3210,9 +3241,43 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
32103241
const bool hasAsyncSupport =
32113242
AvailabilityRange::forDeploymentTarget(context).isContainedIn(
32123243
context.getBackDeployedConcurrencyAvailability());
3213-
context.Diags.diagnose(attr->getLocation(),
3214-
diag::attr_MainType_without_main,
3215-
nominal, hasAsyncSupport);
3244+
3245+
auto location = attr->getLocation();
3246+
auto fixLocation = braces.Start;
3247+
3248+
context.Diags.diagnose(location, diag::attr_MainType_without_main, nominal,
3249+
hasAsyncSupport);
3250+
3251+
// Offer fix-its to add the `main` function for different combinations of
3252+
// effects, starting with no effects.
3253+
3254+
context.Diags.diagnose(location, diag::note_add_main_sync)
3255+
.fixItInsertAfter(fixLocation, generateMainFunctionText(
3256+
context, nominal, /*isThrows*/ false,
3257+
/*isAsync*/ false)
3258+
.str());
3259+
3260+
context.Diags.diagnose(location, diag::note_add_main_sync_throws)
3261+
.fixItInsertAfter(fixLocation, generateMainFunctionText(
3262+
context, nominal, /*isThrows*/ true,
3263+
/*isAsync*/ false)
3264+
.str());
3265+
3266+
if (hasAsyncSupport) {
3267+
context.Diags.diagnose(location, diag::note_add_main_async)
3268+
.fixItInsertAfter(fixLocation,
3269+
generateMainFunctionText(context, nominal,
3270+
/*isThrows*/ false,
3271+
/*isAsync*/ true)
3272+
.str());
3273+
3274+
context.Diags.diagnose(location, diag::note_add_main_async_throws)
3275+
.fixItInsertAfter(fixLocation,
3276+
generateMainFunctionText(context, nominal,
3277+
/*isThrows*/ true,
3278+
/*isAsync*/ true)
3279+
.str());
3280+
}
32163281
attr->setInvalid();
32173282
return nullptr;
32183283
}

test/attr/ApplicationMain/attr_main_arguments.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

33
@main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}}
4+
// expected-note@-1{{add 'static func main()'}} {{8:16-16=\n static func main() {\n <#code#>\n }\n}}
5+
// expected-note@-2{{add 'static func main() throws'}} {{8:16-16=\n static func main() throws {\n <#code#>\n }\n}}
6+
// expected-note@-3{{add 'static func main() async'}} {{8:16-16=\n static func main() async {\n <#code#>\n }\n}}
7+
// expected-note@-4{{add 'static func main() async throws'}} {{8:16-16=\n static func main() async throws {\n <#code#>\n }\n}}
48
struct MyBase {
59
static func main(_ argc: Int, _ argv: [String]) {
610
}
711
}
8-

test/attr/ApplicationMain/attr_main_dynamicCallable.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

33
@main // expected-error{{'Foo' is annotated with '@main' and must provide a main static function}}
4+
// expected-note@-1{{add 'static func main()'}} {{8:13-13=\n static func main() {\n <#code#>\n }\n}}
5+
// expected-note@-2{{add 'static func main() throws'}} {{8:13-13=\n static func main() throws {\n <#code#>\n }\n}}
6+
// expected-note@-3{{add 'static func main() async'}} {{8:13-13=\n static func main() async {\n <#code#>\n }\n}}
7+
// expected-note@-4{{add 'static func main() async throws'}} {{8:13-13=\n static func main() async throws {\n <#code#>\n }\n}}
48
struct Foo {
59
@dynamicCallable
610
struct main {

test/attr/ApplicationMain/attr_main_dynamicMemberLookup.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

33
@main @dynamicMemberLookup // expected-error{{'Main' is annotated with '@main' and must provide a main static function}}
4+
// expected-note@-1{{add 'static func main()'}} {{8:14-14=\n static func main() {\n <#code#>\n }\n}}
5+
// expected-note@-2{{add 'static func main() throws'}} {{8:14-14=\n static func main() throws {\n <#code#>\n }\n}}
6+
// expected-note@-3{{add 'static func main() async'}} {{8:14-14=\n static func main() async {\n <#code#>\n }\n}}
7+
// expected-note@-4{{add 'static func main() async throws'}} {{8:14-14=\n static func main() async throws {\n <#code#>\n }\n}}
48
struct Main {
59
subscript(dynamicMember member: String) -> () -> Void {
610
return {
711
}
812
}
913
}
10-

test/attr/ApplicationMain/attr_main_extension_nofunc.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ class EntryPoint {
44
}
55

66
@main // expected-error{{'EntryPoint' is annotated with '@main' and must provide a main static function}}
7+
// expected-note@-1{{add 'static func main()'}} {{11:23-23=\n static func main() {\n <#code#>\n }\n}}
8+
// expected-note@-2{{add 'static func main() throws'}} {{11:23-23=\n static func main() throws {\n <#code#>\n }\n}}
9+
// expected-note@-3{{add 'static func main() async'}} {{11:23-23=\n static func main() async {\n <#code#>\n }\n}}
10+
// expected-note@-4{{add 'static func main() async throws'}} {{11:23-23=\n static func main() async throws {\n <#code#>\n }\n}}
711
extension EntryPoint {
812
}
9-
10-
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

33
@main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}}
4+
// expected-note@-1{{add 'static func main()'}} {{8:15-15=\n static func main() {\n <#code#>\n }\n}}
5+
// expected-note@-2{{add 'static func main() throws'}} {{8:15-15=\n static func main() throws {\n <#code#>\n }\n}}
6+
// expected-note@-3{{add 'static func main() async'}} {{8:15-15=\n static func main() async {\n <#code#>\n }\n}}
7+
// expected-note@-4{{add 'static func main() async throws'}} {{8:15-15=\n static func main() async throws {\n <#code#>\n }\n}}
48
class MyBase {
59
func main() {
610
}
711
}
812

13+
enum Nested {
14+
@main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}}
15+
// expected-note@-1{{add 'static func main()'}} {{19:17-17=\n static func main() {\n <#code#>\n }\n}}
16+
// expected-note@-2{{add 'static func main() throws'}} {{19:17-17=\n static func main() throws {\n <#code#>\n }\n}}
17+
// expected-note@-3{{add 'static func main() async'}} {{19:17-17=\n static func main() async {\n <#code#>\n }\n}}
18+
// expected-note@-4{{add 'static func main() async throws'}} {{19:17-17=\n static func main() async throws {\n <#code#>\n }\n}}
19+
class MyBase {
20+
func main() {
21+
}
22+
}
23+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -target %target-swift-5.0-abi-triple -verify %s
2+
// REQUIRES: OS=macosx && CPU=x86_64
3+
4+
@main // expected-error{{'MyBaseWithoutAsyncSupport' is annotated with '@main' and must provide a main static function}}
5+
// expected-note@-1{{add 'static func main()'}} {{7:34-34=\n static func main() {\n <#code#>\n }\n}}
6+
// expected-note@-2{{add 'static func main() throws'}} {{7:34-34=\n static func main() throws {\n <#code#>\n }\n}}
7+
class MyBaseWithoutAsyncSupport {
8+
func main() {
9+
}
10+
}

test/attr/ApplicationMain/attr_main_return.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

33
@main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}}
4+
// expected-note@-1{{add 'static func main()'}} {{8:16-16=\n static func main() {\n <#code#>\n }\n}}
5+
// expected-note@-2{{add 'static func main() throws'}} {{8:16-16=\n static func main() throws {\n <#code#>\n }\n}}
6+
// expected-note@-3{{add 'static func main() async'}} {{8:16-16=\n static func main() async {\n <#code#>\n }\n}}
7+
// expected-note@-4{{add 'static func main() async throws'}} {{8:16-16=\n static func main() async throws {\n <#code#>\n }\n}}
48
struct MyBase {
59
static func main() -> Int {
610
}
711
}
8-

test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ extension Runnable where Self : OtherThing {
1616
}
1717

1818
@main //expected-error{{'EntryPoint' is annotated with '@main' and must provide a main static function}}
19+
// expected-note@-1{{add 'static func main()'}} {{23:31-31=\n static func main() {\n <#code#>\n }\n}}
20+
// expected-note@-2{{add 'static func main() throws'}} {{23:31-31=\n static func main() throws {\n <#code#>\n }\n}}
21+
// expected-note@-3{{add 'static func main() async'}} {{23:31-31=\n static func main() async {\n <#code#>\n }\n}}
22+
// expected-note@-4{{add 'static func main() async throws'}} {{23:31-31=\n static func main() async throws {\n <#code#>\n }\n}}
1923
struct EntryPoint : Runnable {
2024
func run() {
2125
}
2226
}
23-

0 commit comments

Comments
 (0)