Skip to content

Commit 1035213

Browse files
Merge pull request #82989 from CrazyFanFan/feature/fix-it-add-static-main
[Diagnostics] Add fix-it to `@main` struct without main static function.
2 parents d4b28a4 + 061ce72 commit 1035213

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
@@ -4276,6 +4276,14 @@ ERROR(attr_MainType_without_main,none,
42764276
"%0 is annotated with '@main' and must provide a main static function "
42774277
"of type %" SELECT_APPLICATION_MAIN_TYPES "1",
42784278
(const ValueDecl *, bool))
4279+
NOTE(note_add_main_sync,none,
4280+
"add 'static func main()'", ())
4281+
NOTE(note_add_main_sync_throws,none,
4282+
"add 'static func main() throws'", ())
4283+
NOTE(note_add_main_async,none,
4284+
"add 'static func main() async'", ())
4285+
NOTE(note_add_main_async_throws,none,
4286+
"add 'static func main() async throws'", ())
42794287

42804288
#undef SELECT_APPLICATION_MAIN_TYPES
42814289
#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"
@@ -3104,6 +3105,36 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
31043105
return std::make_pair(body, /*typechecked=*/false);
31053106
}
31063107

3108+
static llvm::SmallString<128>
3109+
generateMainFunctionText(ASTContext &C, NominalTypeDecl *parentDecl,
3110+
bool isThrows, bool isAsync) {
3111+
StringRef ExtraIndent;
3112+
StringRef CurrentIndent = Lexer::getIndentationForLine(
3113+
C.SourceMgr, parentDecl->getStartLoc(), &ExtraIndent);
3114+
std::string MethodIndent = (CurrentIndent + ExtraIndent).str();
3115+
3116+
llvm::SmallString<128> Text;
3117+
llvm::raw_svector_ostream OS(Text);
3118+
ExtraIndentStreamPrinter Printer(OS, MethodIndent);
3119+
3120+
Printer.printNewline();
3121+
3122+
Printer << "static func main() ";
3123+
if (isAsync)
3124+
Printer << "async ";
3125+
if (isThrows)
3126+
Printer << "throws ";
3127+
3128+
// Print the "{ <#code#> }" placeholder body.
3129+
Printer << "{\n";
3130+
Printer.printIndent();
3131+
Printer << ExtraIndent << getCodePlaceholder();
3132+
Printer.printNewline();
3133+
Printer << "}\n";
3134+
3135+
return Text;
3136+
}
3137+
31073138
FuncDecl *
31083139
SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
31093140
Decl *D) const {
@@ -3233,9 +3264,43 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
32333264
const bool hasAsyncSupport =
32343265
AvailabilityRange::forDeploymentTarget(context).isContainedIn(
32353266
context.getBackDeployedConcurrencyAvailability());
3236-
context.Diags.diagnose(attr->getLocation(),
3237-
diag::attr_MainType_without_main,
3238-
nominal, hasAsyncSupport);
3267+
3268+
auto location = attr->getLocation();
3269+
auto fixLocation = braces.Start;
3270+
3271+
context.Diags.diagnose(location, diag::attr_MainType_without_main, nominal,
3272+
hasAsyncSupport);
3273+
3274+
// Offer fix-its to add the `main` function for different combinations of
3275+
// effects, starting with no effects.
3276+
3277+
context.Diags.diagnose(location, diag::note_add_main_sync)
3278+
.fixItInsertAfter(fixLocation, generateMainFunctionText(
3279+
context, nominal, /*isThrows*/ false,
3280+
/*isAsync*/ false)
3281+
.str());
3282+
3283+
context.Diags.diagnose(location, diag::note_add_main_sync_throws)
3284+
.fixItInsertAfter(fixLocation, generateMainFunctionText(
3285+
context, nominal, /*isThrows*/ true,
3286+
/*isAsync*/ false)
3287+
.str());
3288+
3289+
if (hasAsyncSupport) {
3290+
context.Diags.diagnose(location, diag::note_add_main_async)
3291+
.fixItInsertAfter(fixLocation,
3292+
generateMainFunctionText(context, nominal,
3293+
/*isThrows*/ false,
3294+
/*isAsync*/ true)
3295+
.str());
3296+
3297+
context.Diags.diagnose(location, diag::note_add_main_async_throws)
3298+
.fixItInsertAfter(fixLocation,
3299+
generateMainFunctionText(context, nominal,
3300+
/*isThrows*/ true,
3301+
/*isAsync*/ true)
3302+
.str());
3303+
}
32393304
attr->setInvalid();
32403305
return nullptr;
32413306
}

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)