Skip to content
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

Async main codegen #18472

Merged
merged 60 commits into from
Apr 28, 2017
Merged

Conversation

TyOverby
Copy link
Contributor

@TyOverby TyOverby commented Apr 5, 2017

No description provided.

@TyOverby TyOverby added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Apr 5, 2017
@TyOverby TyOverby requested a review from jaredpar April 5, 2017 18:02
@TyOverby TyOverby removed the request for review from jaredpar April 5, 2017 18:02
@TyOverby TyOverby removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Apr 6, 2017
@TyOverby
Copy link
Contributor Author

TyOverby commented Apr 6, 2017

@dotnet/roslyn-compiler

This bit should be ready for a general review.

I've added some pretty minimal tests that I plan on fleshing out in another PR. #Resolved

@@ -221,7 +241,10 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul
moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody);
}

Debug.Assert((object)entryPoint != null || entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty);
if (addedDefinition && moduleBeingBuilt != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Braces on new lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -196,8 +197,27 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul
diagnostics.AddRange(entryPointAndDiagnostics.Diagnostics);

var entryPoint = entryPointAndDiagnostics.MethodSymbol;
var synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol;
if (((object)synthesizedEntryPoint != null) &&

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a comment to the top of the function noting that this returns the real entry point vs. the user defined one. The name of the function is ambiguous with this feature now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

bool addedDefinition = false;
if (entryPoint is SynthesizedEntryPointSymbol s)
{
synthesizedEntryPoint = s;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What case hits this branch of the if?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other two other subclasses of SynthesizedEntryPointSymbol are ScriptEntryPoint and SubmissionEntryPoint both of which (I think) are for scripting support.


// Task -> Void
// Task<_> -> Int32
private static TypeSymbol TranslateReturnType(CSharpCompilation compilation, TypeSymbol returnType) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Braces on new lines. Other occurences in this review that should be fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double blank line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

_paramKind = userMain.ParameterCount == 0 ? ParamKind.NoArgs : ParamKind.StringArrayArgs;
_returnKind = GetReturnKind(compilation, userMain.ReturnType);


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double blank line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

type: _userMain.ReturnType)
{ WasCompilerGenerated = true };


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double blank line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still see this in latest source.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still see this in the latest source.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TyOverby still present

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually Done. I think I fixed another instance of this problem earlier...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearly I need to update the auto-formatter in order to accommodate this

addedDefinition = true;
}

if ((synthesizedEntryPoint != null) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to use the ((object)synthesizedEntryPoint) != null style of comparison here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
else if (entryPoint.HasAsyncMainReturnType(compilation) && compilation.LanguageVersion >= LanguageVersion.CSharp7_1)
{
synthesizedEntryPoint = new AsyncForwardEntryPoint(compilation, diagnostics, entryPoint.ContainingType, entryPoint);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd that we are attaching the diagnostics to only the async sythesized method symbol. What is the rationale for this?

In the case the diagnostics parameter had existing diagnostics in it, possible for a bag, it would be attaching those errors to the generated main as well which seems incorrect. As say using entryPointAndDiagnostics.Diagnostics. Even that though feels like we are double tracking diagnostics here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the only place that the diagnosticsBag is used is during the GetRequiredMethod lookup for GetAwaiter and GetResult.

// Task<_> -> Int32
private static TypeSymbol TranslateReturnType(CSharpCompilation compilation, TypeSymbol returnType)
{
if (returnType.IsGenericTaskType(compilation)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: more code formatting issues all through this file. Please review the entire PR and correct these.

// The user-defined asyncrhonous main method.
private MethodSymbol _userMain;
private DiagnosticBag _diagnosticBag;
private CSharpCompilation _compilation;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this stored as a field? It seems to only be used in the constructor of the type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this reference must have survived some refactors.

internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol
{
// The user-defined asyncrhonous main method.
private MethodSymbol _userMain;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

private CSharpCompilation _compilation;

private ReturnKind _returnKind;
private ParamKind _paramKind;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both of these can be readonly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

private ReturnKind _returnKind;
private ParamKind _paramKind;

private ImmutableArray<ParameterSymbol> _parameters;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var syntax = this.GetSyntax();

var refKinds = Parameters.Length == 0 ? ImmutableArray<RefKind>.Empty : ImmutableArray.Create(RefKind.None);
var arguments = new List<BoundExpression>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be optimized by using new List<BoundExpression>(capacity: Parameters.Length)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

receiverOpt: null,
method: _userMain,
arguments: arguments.ToImmutableArray(),
argumentNamesOpt: ImmutableArray<string>.Empty,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we aren't replicated the names on the user authored Main method? True it's legal to generate source without parameter names but some tools still don't handle it gracefully.

Copy link
Contributor Author

@TyOverby TyOverby Apr 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh fine :P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that you meant for this comment to be on another line though. Our main method parameter names is defined in the call to SynthesizedParameterSymbol.Create inside the constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah you're correct. The use of ImmutableArray<string>.Empty is correct in this location.

type: _userMain.ReturnType)
{ WasCompilerGenerated = true };

// PROTOTYPE(async-main): Errors should likely be reported here instead of bailing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Likely we can just move this up into the creation of the type. This would also avoid the need to store the bag on the type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


SynthesizedEntryPointSymbol synthesizedEntryPoint = null;
bool addedDefinition = false;
if (entryPoint is SynthesizedEntryPointSymbol s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is SynthesizedEntryPointSymbol s followed by synthesizedEntryPoint = s; seems more convoluted (to me at least) than:

var synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol;
if ((object)synthesizedEntryPoint == null)
{
    // other cases
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doen.

else
{
return compilation.GetSpecialType(SpecialType.System_Void);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using conditional operator here and in method below:

return returnType.IsGenericTaskType(compilation) ? ... : ...;

Copy link
Contributor Author

@TyOverby TyOverby Apr 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the style formatting for breaking this across multiple lines?

I'm partial to

return returnType.IsGenericTaskType(compilation) 
    ? compilation.GetSpecialType(SpecialType.System_Int32)
    : compilation.GetSpecialType(SpecialType.System_Void);

foreach (var parameterSymbol in Parameters)
{
arguments.Add(new BoundParameter(syntax, parameterSymbol, parameterSymbol.Type));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var arguments = Parameters.SelectAsArray((p, s) => new BoundParameter(s, p, p.Type), syntax);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually had this code originally but removed it because I thought that LINQ was more-or-less banned in the compiler


internal AsyncForwardEntryPoint(CSharpCompilation compilation, DiagnosticBag diagnosticBag, NamedTypeSymbol containingType, MethodSymbol userMain):
base(containingType, TranslateReturnType(compilation, userMain.ReturnType)) {
Debug.Assert(userMain != null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert seems unnecessary since userMain was dereferenced in base call, and also below. (NullReferenceException seems sufficient.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{
var syntax = this.GetSyntax();

var refKinds = Parameters.Length == 0 ? ImmutableArray<RefKind>.Empty : ImmutableArray.Create(RefKind.None);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems strange to assume there is exactly zero or one parameter for refKinds but not for arguments below. Consider generalizing this. Perhaps: var refKinds = Parameters.SelectAsArray(p => RefKind.None);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better yet, argumentRefKindsOpt is an optional parameter so we can pass in default(ImmutableArray<RefKind>) instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
argsToParamsOpt: ImmutableArray<int>.Empty,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe argumentNamesOpt and argsToParamsOpt should be default(ImmutableArray<T>) rather than Empty if not set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -334,14 +334,13 @@ private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundE
internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol
{
// The user-defined asyncrhonous main method.
private MethodSymbol _userMain;
private readonly MethodSymbol _userMain;
private DiagnosticBag _diagnosticBag;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be readonly as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we figured out my earlier questionts about this bag in general? It seems like it's opening the door for duplicate / unnecessary errors here. Feel like they should be resolved at the point of creating this symbol, not later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved this bag to the constructor and provide the userMain location as the location for the diagnostic to go.

}
return returnType.IsGenericTaskType(compilation)
? compilation.GetSpecialType(SpecialType.System_Int32)
: compilation.GetSpecialType(SpecialType.System_Void);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for GetSpecialType to return null here or are we sufficiently protected by this point? If it can return null then we need to account for that as the return here is used in places where null isn't acceptable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be protected at this point, as this code is only executed if an async-main entrypoint was found, which can't happen if GetSpecialType is broken.

{
if ((object)location == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like your formatting is off here. I heard there was this neat tool that formatted the code you changed in a git commit 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the user still needs to remember to use the tool...

var userMainLocation = userMain.DeclaringSyntaxReferences.SingleOrDefault()?.GetLocation() ?? NoLocation.Singleton;

_getAwaiterMethod = GetRequiredMethod(_userMain.ReturnType, WellKnownMemberNames.GetAwaiter, diagnosticBag, userMainLocation);
_getResultMethod = GetRequiredMethod(_getAwaiterMethod.ReturnType, WellKnownMemberNames.GetResult, diagnosticBag);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When GetRequiredMethod fails does it return null or does it fake a symbol? Curious to know what happens if this fails and then CreateBody is called (if that is possible).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns null.

CreateBody is not called if there are any error diagnostics in the bag. (GetRequiredMethod adds error diagnostics if it can't find anything).

BoundCall getAwaiterGetResult =
CreateParameterlessCall(
syntax: syntax,
method: _getResultMethod,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can CreateBody be called if the constructor for AsyncForwardEntryPoint added an error to the diagnostic bag? If so this would end up passing a null value for _getResultMethod.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateBody is called from MethodCompiler.GetEntryPoint only if there are no error diagnostics.

@TyOverby
Copy link
Contributor Author

TyOverby commented Apr 8, 2017

@jaredpar: I think I've addressed everything that you've mentioned. #Resolved

@@ -2059,28 +2059,31 @@ private AssemblySymbol GetForwardedToAssembly(string fullName, int arity, Diagno
return null;
}

internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID feature, DiagnosticBag diagnostics, Location locationOpt = null)
internal static bool CheckFeatureAvailability(SyntaxNode syntax, MessageID feature, DiagnosticBag diagnostics, Location locationOpt = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like the return value is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yep, not used anymore.

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}
[Fact]

public void MainCanReturnTaskAndGenericOnInt_NoAsync()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public void MainCanReturnTaskAndGenericOnInt_NoAsync() [](start = 8, length = 54)

There is an empty line after the [Fact] attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


if (expectedOutput.Trim() != actualOutput.Trim())
if (expectedOutput.Trim() != Output.Trim())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (expectedOutput.Trim() != Output.Trim()) [](start = 12, length = 43)

Is this line safe for expectedOutput == null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol method)
{
method = method.PartialDefinitionPart ?? method;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure there are tests for partial methods:

  • there is only definition
  • there is only implementation
  • definition is syntactically first
  • implementation is syntactically first

Result produced by this method should be observable in the tests, probably through location of an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial methods must be void returning, so they won't hit any of these new code paths.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed offline, let's add direct tests for this function then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlekseyTs: I've added tests for this API on non-main methods.

@AlekseyTs
Copy link
Contributor

Done with review pass (iteration 52)



var syntax = method.ExtractReturnTypeSyntax();
var dumbInstance = new BoundLiteral(syntax, ConstantValue.Null, method.ReturnType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BoundDefaultExpression(syntax, method.ReturnType)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, swore I fixed this earlier.

Done.

if (method.RefKind != RefKind.None)
{
return false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@cston
Copy link
Member

cston commented Apr 26, 2017

LGTM

}

var syntax = method.ExtractReturnTypeSyntax();
var dumbInstance = new BoundLiteral(syntax, ConstantValue.Null, method.ReturnType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var dumbInstance = new BoundLiteral(syntax, ConstantValue.Null, method.ReturnType); [](start = 12, length = 83)

Were you going to change this to BoundDefaultExpression?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was, but the synthesized expression was emitting warnings about using "dot" after default, so I reverted it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@AlekseyTs
Copy link
Contributor

Done with review pass (iteration 54)

Assert.Equal(1, m.Length);
var returnSyntax = ((MethodSymbol) m.First()).ExtractReturnTypeSyntax();
var newLineCount = Environment.NewLine.Length * 1;
Assert.Equal(returnSyntax.Location.SourceSpan, TextSpan.FromBounds(34 + newLineCount, 38 + newLineCount));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.Equal(returnSyntax.Location.SourceSpan, TextSpan.FromBounds(34 + newLineCount, 38 + newLineCount)); [](start = 12, length = 106)

I think it would be better to find the node that you expect directly from the syntax tree. This way I cannot really tell if your expectations are correct. Also, I believe expected value should be first, otherwise failure message is confusing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you find the node, simply assert that the nodes are the same.


In reply to: 113774955 [](ancestors = 113774955)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to other new tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, and Done

var comp = CreateStandardCompilation(text);
var global = comp.GlobalNamespace;
var a = global.GetTypeMembers("A", 0).Single();
var m = a.GetMembers("M");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var m = a.GetMembers("M"); [](start = 12, length = 26)

I assume this is definition, please assert that for clarity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -203,6 +204,84 @@ public partial class A {
}

[Fact]
public void PartialExtractSyntaxLocation_DeclBeforeDef()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PartialExtractSyntaxLocation_DeclBeforeDef [](start = 20, length = 42)

Please also test what the API returns when called off of implementation.

}

[Fact]
public void PartialExtractSyntaxLocation_DefBeforeDecl()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PartialExtractSyntaxLocation_DefBeforeDecl [](start = 20, length = 42)

Please also test what the API returns when called off of implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I use the API to get the non-declaration method symbol?

@AlekseyTs
Copy link
Contributor

Done with review pass (iteration 56)

@@ -72,23 +72,18 @@ public CoreCLRRuntimeEnvironment(IEnumerable<ModuleData> additionalDependencies
}
}

public (int ExitCode, string Output) Execute(string moduleName, int expectedOutputLength)
public int Execute(string moduleName, string[] args, string expectedOutput)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like args is just being dropped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

errorOutputWriter = new CappedStringWriter(exLength);
outputWriter = new CappedStringWriter(exLength);
}
else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because there's no expected output doesn't mean we shouldn't capture the incorrect output. I would keep always using CappedStringWriter.

Copy link
Contributor Author

@TyOverby TyOverby Apr 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CappedStringWriter fails if the capacity is exceeded, if you don't fill out expectedOutput, then it would be like saying "this program can not produce output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file has been reverted.

@@ -11,25 +11,35 @@ internal static class SharedConsole
private static TextWriter s_savedConsoleOut;
private static TextWriter s_savedConsoleError;

private static AsyncLocal<StringWriter> s_currentOut;
private static AsyncLocal<StringWriter> s_currentError;
private static AsyncLocal<TextWriter> s_currentOut;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given my comment below this can stay as StringWriter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file has been reverted.

@@ -95,7 +95,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray<byte> mainImage)
}
}

internal (int ExitCode, string Output) Execute(ImmutableArray<byte> mainImage, int expectedOutputLength)
internal (int ExitCode, string Output) Execute(ImmutableArray<byte> mainImage, int? expectedOutputLength)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason for the int?. If they don't expect output, just put in 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 would mean "this program must produce no output"

{
try
{
var emitData = GetEmitData();
emitData.RuntimeData.ExecuteRequested = true;
return emitData.Manager.Execute(moduleName, expectedOutputLength, out processOutput);
var resultCode = emitData.Manager.Execute(moduleName, args, expectedOutput?.Length, out var output);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just pass expectedOutput?.Length ?? 0 and keep the arg as a non-nullable int.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file has been reverted.

@@ -377,10 +371,20 @@ CompilationTestData IInternalRuntimeEnvironment.GetCompilationTestData()

private static readonly object s_consoleGuard = new object();

internal static void Capture(Action action, int expectedLength, out string output, out string errorOutput)
internal static void Capture(Action action, int? expectedLength, out string output, out string errorOutput)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the int?. The comparison doesn't happen until the top level and that helper can use expectedOutput == null as a sentinel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed and defaulted with 0.

@@ -136,6 +142,8 @@ public abstract partial class CommonTestBase : TestBase
manifestResources,
expectedSignatures,
expectedOutput,
expectedReturnCode,
args ?? new string[] { },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.Empty<string>()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@TyOverby TyOverby merged commit 0539155 into dotnet:features/async-main Apr 28, 2017
var node = tree.GetRoot().FindNode(token.Span);

var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList();
var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[0].Span));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[0].Span)); [](start = 12, length = 108)

This is way too complicated than it should be.

var tree = comp.SyntaxTrees.Single();
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var node = tree.GetRoot().FindNode(token.Span); [](start = 12, length = 47)

This is way too complicated than it should be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants