Merge pull request #82989 from CrazyFanFan/feature/fix-it-add-static-main

[Diagnostics] Add fix-it to `@main` struct without main static function.
This commit is contained in:
Anthony Latsis
2025-07-31 01:25:37 +01:00
committed by GitHub
10 changed files with 125 additions and 9 deletions

View File

@@ -4276,6 +4276,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

View File

@@ -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"
@@ -3104,6 +3105,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 {
@@ -3233,9 +3264,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;
}

View File

@@ -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]) {
}
}

View File

@@ -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 {

View File

@@ -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 {
}
}
}

View File

@@ -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 {
}

View File

@@ -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() {
}
}
}

View File

@@ -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() {
}
}

View File

@@ -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 {
}
}

View File

@@ -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() {
}
}