From 061ce725d77157c74b5e925edb369483debe21fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crazy=E5=87=A1?= <827799383@qq.com> Date: Thu, 29 May 2025 00:30:28 +0800 Subject: [PATCH] [Diagnostics] Add fix-it to `@main` struct without main static function. --- include/swift/AST/DiagnosticsSema.def | 8 +++ lib/Sema/TypeCheckAttr.cpp | 71 ++++++++++++++++++- .../ApplicationMain/attr_main_arguments.swift | 5 +- .../attr_main_dynamicCallable.swift | 4 ++ .../attr_main_dynamicMemberLookup.swift | 5 +- .../attr_main_extension_nofunc.swift | 6 +- .../ApplicationMain/attr_main_instance.swift | 15 ++++ ..._main_instance_without_async_support.swift | 10 +++ .../ApplicationMain/attr_main_return.swift | 5 +- ...truct_from_two_protocols_one_missing.swift | 5 +- 10 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 test/attr/ApplicationMain/attr_main_instance_without_async_support.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 60e0a5bbf1f07..157cbb4adf309 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4264,6 +4264,14 @@ ERROR(attr_MainType_without_main,none, "%0 is annotated with '@main' and must provide a main static function " "of type %" SELECT_APPLICATION_MAIN_TYPES "1", (const ValueDecl *, bool)) +NOTE(note_add_main_sync,none, + "add 'static func main()'", ()) +NOTE(note_add_main_sync_throws,none, + "add 'static func main() throws'", ()) +NOTE(note_add_main_async,none, + "add 'static func main() async'", ()) +NOTE(note_add_main_async_throws,none, + "add 'static func main() async throws'", ()) #undef SELECT_APPLICATION_MAIN_TYPES #undef SELECT_APPLICATION_MAIN diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 123aea0b9ed0e..99252d9882427 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -22,6 +22,7 @@ #include "TypeCheckObjC.h" #include "TypeCheckType.h" #include "TypeChecker.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/AvailabilityInference.h" #include "swift/AST/ClangModuleLoader.h" @@ -3081,6 +3082,36 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) { return std::make_pair(body, /*typechecked=*/false); } +static llvm::SmallString<128> +generateMainFunctionText(ASTContext &C, NominalTypeDecl *parentDecl, + bool isThrows, bool isAsync) { + StringRef ExtraIndent; + StringRef CurrentIndent = Lexer::getIndentationForLine( + C.SourceMgr, parentDecl->getStartLoc(), &ExtraIndent); + std::string MethodIndent = (CurrentIndent + ExtraIndent).str(); + + llvm::SmallString<128> Text; + llvm::raw_svector_ostream OS(Text); + ExtraIndentStreamPrinter Printer(OS, MethodIndent); + + Printer.printNewline(); + + Printer << "static func main() "; + if (isAsync) + Printer << "async "; + if (isThrows) + Printer << "throws "; + + // Print the "{ <#code#> }" placeholder body. + Printer << "{\n"; + Printer.printIndent(); + Printer << ExtraIndent << getCodePlaceholder(); + Printer.printNewline(); + Printer << "}\n"; + + return Text; +} + FuncDecl * SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, Decl *D) const { @@ -3210,9 +3241,43 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, const bool hasAsyncSupport = AvailabilityRange::forDeploymentTarget(context).isContainedIn( context.getBackDeployedConcurrencyAvailability()); - context.Diags.diagnose(attr->getLocation(), - diag::attr_MainType_without_main, - nominal, hasAsyncSupport); + + auto location = attr->getLocation(); + auto fixLocation = braces.Start; + + context.Diags.diagnose(location, diag::attr_MainType_without_main, nominal, + hasAsyncSupport); + + // Offer fix-its to add the `main` function for different combinations of + // effects, starting with no effects. + + context.Diags.diagnose(location, diag::note_add_main_sync) + .fixItInsertAfter(fixLocation, generateMainFunctionText( + context, nominal, /*isThrows*/ false, + /*isAsync*/ false) + .str()); + + context.Diags.diagnose(location, diag::note_add_main_sync_throws) + .fixItInsertAfter(fixLocation, generateMainFunctionText( + context, nominal, /*isThrows*/ true, + /*isAsync*/ false) + .str()); + + if (hasAsyncSupport) { + context.Diags.diagnose(location, diag::note_add_main_async) + .fixItInsertAfter(fixLocation, + generateMainFunctionText(context, nominal, + /*isThrows*/ false, + /*isAsync*/ true) + .str()); + + context.Diags.diagnose(location, diag::note_add_main_async_throws) + .fixItInsertAfter(fixLocation, + generateMainFunctionText(context, nominal, + /*isThrows*/ true, + /*isAsync*/ true) + .str()); + } attr->setInvalid(); return nullptr; } diff --git a/test/attr/ApplicationMain/attr_main_arguments.swift b/test/attr/ApplicationMain/attr_main_arguments.swift index c42f4bb1d42a0..592aee5b76cd7 100644 --- a/test/attr/ApplicationMain/attr_main_arguments.swift +++ b/test/attr/ApplicationMain/attr_main_arguments.swift @@ -1,8 +1,11 @@ // RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s @main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{8:16-16=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{8:16-16=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{8:16-16=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{8:16-16=\n static func main() async throws {\n <#code#>\n }\n}} struct MyBase { static func main(_ argc: Int, _ argv: [String]) { } } - diff --git a/test/attr/ApplicationMain/attr_main_dynamicCallable.swift b/test/attr/ApplicationMain/attr_main_dynamicCallable.swift index 9f298c4719c3c..efc3cb49a800e 100644 --- a/test/attr/ApplicationMain/attr_main_dynamicCallable.swift +++ b/test/attr/ApplicationMain/attr_main_dynamicCallable.swift @@ -1,6 +1,10 @@ // RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s @main // expected-error{{'Foo' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{8:13-13=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{8:13-13=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{8:13-13=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{8:13-13=\n static func main() async throws {\n <#code#>\n }\n}} struct Foo { @dynamicCallable struct main { diff --git a/test/attr/ApplicationMain/attr_main_dynamicMemberLookup.swift b/test/attr/ApplicationMain/attr_main_dynamicMemberLookup.swift index 7ab3eebbaedcf..daa35621e6a67 100644 --- a/test/attr/ApplicationMain/attr_main_dynamicMemberLookup.swift +++ b/test/attr/ApplicationMain/attr_main_dynamicMemberLookup.swift @@ -1,10 +1,13 @@ // RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s @main @dynamicMemberLookup // expected-error{{'Main' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{8:14-14=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{8:14-14=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{8:14-14=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{8:14-14=\n static func main() async throws {\n <#code#>\n }\n}} struct Main { subscript(dynamicMember member: String) -> () -> Void { return { } } } - diff --git a/test/attr/ApplicationMain/attr_main_extension_nofunc.swift b/test/attr/ApplicationMain/attr_main_extension_nofunc.swift index 4df2d0870a8e9..5df0e1141d6c3 100644 --- a/test/attr/ApplicationMain/attr_main_extension_nofunc.swift +++ b/test/attr/ApplicationMain/attr_main_extension_nofunc.swift @@ -4,7 +4,9 @@ class EntryPoint { } @main // expected-error{{'EntryPoint' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{11:23-23=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{11:23-23=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{11:23-23=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{11:23-23=\n static func main() async throws {\n <#code#>\n }\n}} extension EntryPoint { } - - diff --git a/test/attr/ApplicationMain/attr_main_instance.swift b/test/attr/ApplicationMain/attr_main_instance.swift index 0c672b2a8c4ea..13d6a417ea8c6 100644 --- a/test/attr/ApplicationMain/attr_main_instance.swift +++ b/test/attr/ApplicationMain/attr_main_instance.swift @@ -1,8 +1,23 @@ // RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s @main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{8:15-15=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{8:15-15=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{8:15-15=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{8:15-15=\n static func main() async throws {\n <#code#>\n }\n}} class MyBase { func main() { } } +enum Nested { + @main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}} + // expected-note@-1{{add 'static func main()'}} {{19:17-17=\n static func main() {\n <#code#>\n }\n}} + // expected-note@-2{{add 'static func main() throws'}} {{19:17-17=\n static func main() throws {\n <#code#>\n }\n}} + // expected-note@-3{{add 'static func main() async'}} {{19:17-17=\n static func main() async {\n <#code#>\n }\n}} + // expected-note@-4{{add 'static func main() async throws'}} {{19:17-17=\n static func main() async throws {\n <#code#>\n }\n}} + class MyBase { + func main() { + } + } +} diff --git a/test/attr/ApplicationMain/attr_main_instance_without_async_support.swift b/test/attr/ApplicationMain/attr_main_instance_without_async_support.swift new file mode 100644 index 0000000000000..c9d1a635d4485 --- /dev/null +++ b/test/attr/ApplicationMain/attr_main_instance_without_async_support.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-frontend -typecheck -parse-as-library -target %target-swift-5.0-abi-triple -verify %s +// REQUIRES: OS=macosx && CPU=x86_64 + +@main // expected-error{{'MyBaseWithoutAsyncSupport' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{7:34-34=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{7:34-34=\n static func main() throws {\n <#code#>\n }\n}} +class MyBaseWithoutAsyncSupport { + func main() { + } +} diff --git a/test/attr/ApplicationMain/attr_main_return.swift b/test/attr/ApplicationMain/attr_main_return.swift index b8e8a80a364c9..ef7439217bef5 100644 --- a/test/attr/ApplicationMain/attr_main_return.swift +++ b/test/attr/ApplicationMain/attr_main_return.swift @@ -1,8 +1,11 @@ // RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s @main // expected-error{{'MyBase' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{8:16-16=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{8:16-16=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{8:16-16=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{8:16-16=\n static func main() async throws {\n <#code#>\n }\n}} struct MyBase { static func main() -> Int { } } - diff --git a/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift b/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift index c672a0648b37c..75d06b22f02f5 100644 --- a/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift +++ b/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift @@ -16,8 +16,11 @@ extension Runnable where Self : OtherThing { } @main //expected-error{{'EntryPoint' is annotated with '@main' and must provide a main static function}} +// expected-note@-1{{add 'static func main()'}} {{23:31-31=\n static func main() {\n <#code#>\n }\n}} +// expected-note@-2{{add 'static func main() throws'}} {{23:31-31=\n static func main() throws {\n <#code#>\n }\n}} +// expected-note@-3{{add 'static func main() async'}} {{23:31-31=\n static func main() async {\n <#code#>\n }\n}} +// expected-note@-4{{add 'static func main() async throws'}} {{23:31-31=\n static func main() async throws {\n <#code#>\n }\n}} struct EntryPoint : Runnable { func run() { } } -