Skip to content

[Diagnostics] Add fix-it to @main struct without main static function. #82989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 59 additions & 3 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -3081,6 +3082,37 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
return std::make_pair(body, /*typechecked=*/false);
}

llvm::SmallString<128> generateMainFunctionText(ASTContext &C, Decl *decl,
bool isThrows, bool isAsync) {
// Prepare the indent (same as `printRequirementStub`)
StringRef ExtraIndent;
StringRef CurrentIndent = Lexer::getIndentationForLine(
C.SourceMgr, decl->getStartLoc(), &ExtraIndent);

llvm::SmallString<128> Text;
llvm::raw_svector_ostream OS(Text);
ExtraIndentStreamPrinter Printer(OS, CurrentIndent);

Printer.printNewline();
Printer.printIndent();

Printer << ExtraIndent << "static func main() ";
if (isAsync)
Printer << "async ";
if (isThrows)
Printer << "throws ";

/// Print the "{ <#code#> }" placeholder body
Printer << "{\n";
Printer.printIndent();
Printer << ExtraIndent << ExtraIndent << getCodePlaceholder();
Printer.printNewline();
Printer.printIndent();
Printer << ExtraIndent << "}\n";

return Text;
}

FuncDecl *
SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
Decl *D) const {
Expand Down Expand Up @@ -3210,9 +3242,33 @@ 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);

context.Diags.diagnose(location, diag::note_add_main_sync)
.fixItInsertAfter(
fixLocation,
generateMainFunctionText(context, nominal, false, false).str());
context.Diags.diagnose(location, diag::note_add_main_sync_throws)
.fixItInsertAfter(
fixLocation,
generateMainFunctionText(context, nominal, true, false).str());

if (hasAsyncSupport) {
context.Diags.diagnose(location, diag::note_add_main_async)
.fixItInsertAfter(
fixLocation,
generateMainFunctionText(context, nominal, false, true).str());
context.Diags.diagnose(location, diag::note_add_main_async_throws)
.fixItInsertAfter(
fixLocation,
generateMainFunctionText(context, nominal, true, true).str());
}

attr->setInvalid();
return nullptr;
}
Expand Down
4 changes: 4 additions & 0 deletions test/attr/ApplicationMain/attr_main_instance.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// 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() {
}
Expand Down