-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Entrypoint detection stages #19093
Entrypoint detection stages #19093
Conversation
fc3946f
to
42b3d19
Compare
d0473dc
to
83c2f0d
Compare
Ready for initial review @dotnet/roslyn-compiler @VSadov |
var intOrVoidEntryPoints = ArrayBuilder<MethodSymbol>.GetInstance(); | ||
// Validity and diagnostics are also tracked because they must be conditionally handled | ||
// if there are not any "traditional" entrypoints found. | ||
var taskEntryPoints = ArrayBuilder<(bool IsValid, MethodSymbol Candidate, DiagnosticBag SpecificDiagnostics)>.GetInstance(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intOrVoidEntryPoints
and taskEntryPoints
are not freed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
foreach (var candidate in entryPointCandidates) | ||
{ | ||
if (!HasEntryPointSignature(candidate, warnings)) | ||
var perCandidateBag = DiagnosticBag.GetInstance(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perCandidateBag
is not freed. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
else | ||
{ | ||
noMainFoundDiagnostics.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); | ||
noMainFoundDiagnostics.AddRange(perCandidateBag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perCandidateBag
is not freed, in if
or else
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
CC @dotnet/roslyn-compiler Don't think the edit of an existing comment to add that alias produces new emails. At least I didn't see one. |
viableEntryPoints.Add(candidate); | ||
} | ||
|
||
if ((object)mainType == null || viableEntryPoints.Count == 0) | ||
if (viableEntryPoints.IsEmpty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does IsEmpty
require an IEnumerable
? If so, consider using .Count == 0
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
af46feb
to
dad384c
Compare
@cston: This PR implements a discussion that we had earlier about splitting the found entrypoints into different collections. Thanks for taking the time to review it! |
viableEntryPoints.Add(candidate); | ||
} | ||
|
||
if ((object)mainType == null || viableEntryPoints.Count == 0) | ||
if (viableEntryPoints.Count() == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Count
is a property. Is this binding to an IEnumerable
extension method instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yep, you're right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
dad384c
to
ee1a204
Compare
@dotnet-bot test ubuntu_16_debug_prtest please |
@cston: I don't know what code review tool you are using, but I can't "reply" to your comment on |
{ | ||
noMainFoundDiagnostics.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); | ||
noMainFoundDiagnostics.AddRange(perCandidateBag); | ||
perCandidateBag.Free(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps check IsTaskLike
first. That would simplify the IsCandidate
check and perCandidateBag
could be freed in one place rather than two.
if (IsTaskLike)
{
taskEntryPoints.Add(...);
}
else
{
if (IsCandidate)
{
intOrVoidEntryPoints.Add(candidate);
}
else
{
noMainFoundDiagnostics.Add(...);
}
perCandidatesBag.Free();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Done
@@ -5074,4 +5074,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="ERR_VoidInTuple" xml:space="preserve"> | |||
<value>A tuple may not contain a value of type 'void'.</value> | |||
</data> | |||
</root> | |||
<data name="ERR_NonTaskMainCantBeAsync" xml:space="preserve"> | |||
<value>Async Main methods must return Task or Task<int></value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Task<int> [](start = 50, length = 15)
Does it have to be Task<int>
though? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not technically, but I think this is fine for an error message.
I'd be willing to change it, but "Async Main methods must return either Task or Task whereby the return type of GetAwaiter().GetResult() is either void
or int
" seems a bit verbose for an error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can simply say that int or void returning Main cannot be async. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Fact] | ||
[CompilerTrait(CompilerFeature.AsyncMain)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[CompilerTrait(CompilerFeature.AsyncMain)] [](start = 8, length = 42)
I think this can simply be applied to the test type instead. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
var model = comp.GetSemanticModel(tree); | ||
var token = tree.GetRoot().DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).Skip(1).First(); | ||
var node = tree.GetRoot().FindNode(token.Span); | ||
var node = tree.GetRoot().DescendantNodes().OfType<PredefinedTypeSyntax>().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).ToList()[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ToList()[1] [](start = 142, length = 11)
Last()
, or ElementAt(1)
? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); | ||
continue; | ||
intOrVoidEntryPoints.Add(candidate); | ||
perCandidateBag.Free(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perCandidateBag.Free(); [](start = 24, length = 23)
Consider asserting that the bag is empty. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -1479,5 +1479,6 @@ internal enum ErrorCode | |||
ERR_BadDynamicMethodArgDefaultLiteral = 9000, | |||
ERR_DefaultLiteralNotValid = 9001, | |||
WRN_DefaultInSwitch = 9002, | |||
ERR_NonTaskMainCantBeAsync = 9003, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERR_NonTaskMainCantBeAsync [](start = 8, length = 26)
Can we simply reuse ERR_MainCantBeAsync slot? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure about this. Are error numbers relevant to back-compat?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm reusing the old number, but with a new name and message.
continue; | ||
} | ||
|
||
if (!IsValid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!IsValid) [](start = 24, length = 13)
It feels like this if
should be first in this loop (for consistency with int/void entry points). #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these two if
blocks be extracted to a helper method used in both the non-task-like and task-like candidates? (It would mean performing both checks for non-task-like candidates in the same loop, perhaps the first foreach
, so the candidate is never added to intOrVoidEntryPoints
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs: Yep, that makes sense. Done.
@cston: Unless I'm misunderstanding you, I don't think that there would be much to gain from pulling this out into a local function and performing the check earlier would break the diagnostic guarantees
// We can't add those Errors to the general diagnostics bag because it would break previously-working programs. | ||
// The fact that these warnings are not added when csc is invoked with /main is possibly a bug, and is tracked at | ||
// https://github.com/dotnet/roslyn/issues/18964 | ||
diagnostics.AddRange(noMainFoundDiagnostics.AsEnumerable().Where(d => d.Severity == DiagnosticSeverity.Warning)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noMainFoundDiagnostics.AsEnumerable().Where(d => d.Severity == DiagnosticSeverity.Warning) [](start = 41, length = 90)
Linq is banned in compilers. Also, if the point of this code is to preserve old behavior, then we should probably be more precise and add only WRN_MainCantBeGeneric and WRN_InvalidMainSig warnings. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linq is banned in compilers.
Fixed.
Also, if the point of this code is to preserve old behavior
Nah, the point is the emit errors where there were errors before, and emit warnings where there were warnings before. I don't care about emitting the same ones because it's possible to add more helpful diagnostics now.
}").WithArguments("async main", "7.1").WithLocation(6, 5) | ||
); | ||
}").WithArguments("async main", "7.1").WithLocation(6, 5), | ||
Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1) [](start = 16, length = 57)
Please add corresponding message as a comment. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
return 0.0f; | ||
} | ||
}"; | ||
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7) [](start = 101, length = 80)
If we don't expect the behavior to be version specific, we probably shouldn't use specific version for this test. Perhaps we should test it against default version and the latest version, latest will give us coverage for 7.1. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
return 0.0f; | ||
} | ||
}"; | ||
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe.WithMainTypeName("A"), parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7) [](start = 123, length = 80)
If we don't expect the behavior to be version specific, we probably shouldn't use specific version for this test. Perhaps we should test it against default version and the latest version, latest will give us coverage for 7.1. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
}"; | ||
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( | ||
// (6,23): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// (6,23): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point [](start = 16, length = 90)
Why don't we report the same error as in the previous test? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed in person. Fixed.
Done with review pass. #Closed |
0b2b378
to
ba23bae
Compare
Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task<int>").WithArguments("", "System.Threading.Tasks.Task<T>", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task<TResult>"), | ||
// (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point | ||
// static Task<int> Main() { | ||
Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()")); [](start = 16, length = 82)
It looks like we lost locations in the base-line again. Please restore and check other tests too. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -657,14 +746,14 @@ class A | |||
}"; | |||
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); | |||
compilation.VerifyDiagnostics( | |||
// (6,22): error CS1983: The return type of an async method must be void, Task or Task<T> | |||
// (6,22): error CS1983: The return type of an async method must be void, Task or Task<T> | |||
// async static int Main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like formatting is off. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, fixed.
return 1; | ||
} | ||
}"; | ||
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7) [](start = 103, length = 78)
Do you expect the behavior to change in the next version? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
} | ||
}"; | ||
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( | ||
// (6,23): error CS4009: A void or int returning entry point cannot be async |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like formatting is off. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Done with review pass. #Closed |
@AlekseyTs Ping for another review |
// (11,12): error CS1986: 'await' requires that the type Task<int> have a suitable GetAwaiter method | ||
// static Task<int> Main() { | ||
Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task<int>").WithArguments("System.Threading.Tasks.Task<int>").WithLocation(11, 12), | ||
Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task<int>").WithArguments("", "System.Threading.Tasks.Task<T>", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task<TResult>"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task"), [](start = 16, length = 233)
It looks like we lost locations in the base-line again. Please restore and check other tests too. #Closed
Done with review pass. #Closed |
@AlekseyTs done |
retest windows_debug_unit64_prtest please |
ping @AlekseyTs |
Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task<int>").WithArguments("", "System.Threading.Tasks.Task<T>", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task<TResult>"), | ||
// (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point | ||
// static Task<int> Main() { | ||
Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()")) [](start = 16, length = 81)
Location is not verified. #Closed
@@ -3434,6 +3434,8 @@ class Test | |||
// (5,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. | |||
// async public static Task Main() | |||
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main"), | |||
// error CS5001: Program does not contain a static 'Main' method suitable for an entry point | |||
Diagnostic(ErrorCode.ERR_NoEntryPoint), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostic(ErrorCode.ERR_NoEntryPoint), [](start = 16, length = 39)
Location is not verified. And above as well. #Closed
Done with review pass. |
@AlekseyTs I temporarily made it so that missing WithLocation was a test failure, then I fixed all the failing tests. Assuming that my failure-inducing code was correct, I've cleaned up the remainder of the missing WithLocation errors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No description provided.