From a56b8351a35f8389048bf623b8910637d44ca708 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 6 Dec 2016 23:21:00 +0330 Subject: [PATCH 001/284] Clarify error message when this is returned by reference --- .../Portable/Binder/Binder_Statements.cs | 10 ++- .../Semantics/RefLocalsAndReturnsTests.cs | 65 +++++++++++-------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index c06786850b85a..3e095c8e05818 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1147,8 +1147,6 @@ static private ErrorCode GetThisLvalueError(BindValueKind kind) return ErrorCode.ERR_AddrOnReadOnlyLocal; case BindValueKind.IncrementDecrement: return ErrorCode.ERR_IncrementLvalueExpected; - case BindValueKind.RefReturn: - return ErrorCode.ERR_RefReturnStructThis; } } @@ -1572,9 +1570,15 @@ private bool CheckIsVariable(SyntaxNode node, BoundExpression expr, BindValueKin var thisref = expr as BoundThisReference; if (thisref != null) { + if (kind == BindValueKind.RefReturn) + { + Error(diagnostics, thisref.Type.IsValueType ? ErrorCode.ERR_RefReturnStructThis : ErrorCode.ERR_RefReadonlyLocal, node, ThisParameterSymbol.SymbolName); + return false; + } + // We will already have given an error for "this" used outside of a constructor, // instance method, or instance accessor. Assume that "this" is a variable if it is in a struct. - if (!thisref.Type.IsValueType || kind == BindValueKind.RefReturn) + if (!thisref.Type.IsValueType) { // CONSIDER: the Dev10 name has angle brackets (i.e. "") Error(diagnostics, GetThisLvalueError(kind), node, ThisParameterSymbol.SymbolName); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index cabae1fcb9d95..24fb2cb88268c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -595,37 +595,46 @@ static ref char Test1() } } - + public class C + { + public ref C M() + { + return ref this; + } + } }"; var comp = CreateCompilationRef(text); comp.VerifyDiagnostics( - // (10,24): error CS8170: Struct members cannot return 'this' or other instance members by reference - // return ref this; - Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this").WithArguments("this").WithLocation(10, 24), - // (15,24): error CS8170: Struct members cannot return 'this' or other instance members by reference - // return ref x; - Diagnostic(ErrorCode.ERR_RefReturnStructThis, "x").WithArguments("this").WithLocation(15, 24), - // (20,24): error CS8170: Struct members cannot return 'this' or other instance members by reference - // return ref this.x; - Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this.x").WithArguments("this").WithLocation(20, 24), - // (36,32): error CS8168: Cannot return local 'M1' by reference because it is not a ref local - // return ref Foo(ref M1); - Diagnostic(ErrorCode.ERR_RefReturnLocal, "M1").WithArguments("M1").WithLocation(36, 32), - // (36,24): error CS8164: Cannot return by reference a result of 'Test.Foo(ref char)' because the argument passed to parameter 'arg' cannot be returned by reference - // return ref Foo(ref M1); - Diagnostic(ErrorCode.ERR_RefReturnCall, "Foo(ref M1)").WithArguments("Test.Foo(ref char)", "arg").WithLocation(36, 24), - // (41,32): error CS8169: Cannot return a member of local 'M2' by reference because it is not a ref local - // return ref Foo(ref M2.x); - Diagnostic(ErrorCode.ERR_RefReturnLocal2, "M2").WithArguments("M2").WithLocation(41, 32), - // (41,24): error CS8164: Cannot return by reference a result of 'Test.Foo(ref char)' because the argument passed to parameter 'arg' cannot be returned by reference - // return ref Foo(ref M2.x); - Diagnostic(ErrorCode.ERR_RefReturnCall, "Foo(ref M2.x)").WithArguments("Test.Foo(ref char)", "arg").WithLocation(41, 24), - // (46,32): error CS8168: Cannot return local 'M2' by reference because it is not a ref local - // return ref Foo(ref M2).x; - Diagnostic(ErrorCode.ERR_RefReturnLocal, "M2").WithArguments("M2").WithLocation(46, 32), - // (46,24): error CS8165: Cannot return by reference a member of result of 'Test.Foo(ref Test.S1)' because the argument passed to parameter 'arg' cannot be returned by reference - // return ref Foo(ref M2).x; - Diagnostic(ErrorCode.ERR_RefReturnCall2, "Foo(ref M2)").WithArguments("Test.Foo(ref Test.S1)", "arg").WithLocation(46, 24)); + // (10,24): error CS8170: Struct members cannot return 'this' or other instance members by reference + // return ref this; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this").WithArguments("this").WithLocation(10, 24), + // (15,24): error CS8170: Struct members cannot return 'this' or other instance members by reference + // return ref x; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "x").WithArguments("this").WithLocation(15, 24), + // (20,24): error CS8170: Struct members cannot return 'this' or other instance members by reference + // return ref this.x; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this.x").WithArguments("this").WithLocation(20, 24), + // (36,32): error CS8168: Cannot return local 'M1' by reference because it is not a ref local + // return ref Foo(ref M1); + Diagnostic(ErrorCode.ERR_RefReturnLocal, "M1").WithArguments("M1").WithLocation(36, 32), + // (36,24): error CS8164: Cannot return by reference a result of 'Test.Foo(ref char)' because the argument passed to parameter 'arg' cannot be returned by reference + // return ref Foo(ref M1); + Diagnostic(ErrorCode.ERR_RefReturnCall, "Foo(ref M1)").WithArguments("Test.Foo(ref char)", "arg").WithLocation(36, 24), + // (41,32): error CS8169: Cannot return a member of local 'M2' by reference because it is not a ref local + // return ref Foo(ref M2.x); + Diagnostic(ErrorCode.ERR_RefReturnLocal2, "M2").WithArguments("M2").WithLocation(41, 32), + // (41,24): error CS8164: Cannot return by reference a result of 'Test.Foo(ref char)' because the argument passed to parameter 'arg' cannot be returned by reference + // return ref Foo(ref M2.x); + Diagnostic(ErrorCode.ERR_RefReturnCall, "Foo(ref M2.x)").WithArguments("Test.Foo(ref char)", "arg").WithLocation(41, 24), + // (46,32): error CS8168: Cannot return local 'M2' by reference because it is not a ref local + // return ref Foo(ref M2).x; + Diagnostic(ErrorCode.ERR_RefReturnLocal, "M2").WithArguments("M2").WithLocation(46, 32), + // (46,24): error CS8165: Cannot return by reference a member of result of 'Test.Foo(ref Test.S1)' because the argument passed to parameter 'arg' cannot be returned by reference + // return ref Foo(ref M2).x; + Diagnostic(ErrorCode.ERR_RefReturnCall2, "Foo(ref M2)").WithArguments("Test.Foo(ref Test.S1)", "arg").WithLocation(46, 24), + // (58,24): error CS1605: Cannot use 'this' as a ref or out value because it is read-only + // return ref this; + Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(58, 24)); } [Fact] From e09ae57e055941efcd5b475439054424384bac72 Mon Sep 17 00:00:00 2001 From: Steve Melia Date: Mon, 5 Sep 2016 17:01:52 +0100 Subject: [PATCH 002/284] Issue 10428: Formatting of empty loops now respects CSharp spacing options --- .../Formatting/Rules/SpacingFormattingRule.cs | 36 ++++--- .../CSharpTest/Formatting/FormattingTests.cs | 98 ++++++++++++++++++- 2 files changed, 116 insertions(+), 18 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs b/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs index e69848ebb4bed..44f079743278b 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/Rules/SpacingFormattingRule.cs @@ -124,26 +124,24 @@ public override AdjustSpacesOperation GetAdjustSpacesOperation(SyntaxToken previ return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpaceWithinCastParentheses); } - // For spacing between the parenthesis and the expression inside the control flow expression - if (previousKind == SyntaxKind.OpenParenToken && IsControlFlowLikeKeywordStatementKind(previousParentKind)) - { - return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpaceWithinOtherParentheses); - } - // Semicolons in an empty for statement. i.e. for(;;) - if (previousKind == SyntaxKind.OpenParenToken || previousKind == SyntaxKind.SemicolonToken) + if (previousParentKind == SyntaxKind.ForStatement + && this.IsEmptyForStatement((ForStatementSyntax)previousToken.Parent)) { - if (previousToken.Parent.Kind() == SyntaxKind.ForStatement) + if (currentKind == SyntaxKind.SemicolonToken + && (previousKind != SyntaxKind.SemicolonToken + || optionSet.GetOption(CSharpFormattingOptions.SpaceBeforeSemicolonsInForStatement))) { - var forStatement = (ForStatementSyntax)previousToken.Parent; - if (forStatement.Initializers.Count == 0 && - forStatement.Declaration == null && - forStatement.Condition == null && - forStatement.Incrementors.Count == 0) - { - return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces); - } + return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpaceBeforeSemicolonsInForStatement); } + + return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpaceAfterSemicolonsInForStatement); + } + + // For spacing between the parenthesis and the expression inside the control flow expression + if (previousKind == SyntaxKind.OpenParenToken && IsControlFlowLikeKeywordStatementKind(previousParentKind)) + { + return AdjustSpacesOperationZeroOrOne(optionSet, CSharpFormattingOptions.SpaceWithinOtherParentheses); } if (currentKind == SyntaxKind.CloseParenToken && IsControlFlowLikeKeywordStatementKind(currentParentKind)) @@ -337,6 +335,12 @@ public override void AddSuppressOperations(List list, SyntaxN SuppressVariableDeclaration(list, node, optionSet); } + private bool IsEmptyForStatement(ForStatementSyntax forStatement) => + forStatement.Initializers.Count == 0 + && forStatement.Declaration == null + && forStatement.Condition == null + && forStatement.Incrementors.Count == 0; + private void SuppressVariableDeclaration(List list, SyntaxNode node, OptionSet optionSet) { if (node.IsKind(SyntaxKind.FieldDeclaration) || node.IsKind(SyntaxKind.EventDeclaration) || diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index 94bacf0aa785c..2f9b7012dbaab 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -6792,6 +6792,32 @@ public async Task SpacingForForStatementInfiniteLoop() { var code = @" class Program +{ + void Main() + { + for ( ;;) + { + } + } +}"; + var expected = @" +class Program +{ + void Main() + { + for (; ; ) + { + } + } +}"; + await AssertFormatAsync(expected, code); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public async Task SpacingForForStatementInfiniteLoopWithNoSpaces() + { + var code = @" +class Program { void Main() { @@ -6810,7 +6836,75 @@ void Main() } } }"; - await AssertFormatAsync(expected, code); + var optionSet = new Dictionary + { + { CSharpFormattingOptions.SpaceAfterSemicolonsInForStatement, false }, + }; + + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public async Task SpacingForForStatementInfiniteLoopWithSpacesBefore() + { + var code = @" +class Program +{ + void Main() + { + for (;; ) + { + } + } +}"; + var expected = @" +class Program +{ + void Main() + { + for ( ; ;) + { + } + } +}"; + var optionSet = new Dictionary + { + { CSharpFormattingOptions.SpaceBeforeSemicolonsInForStatement, true }, + { CSharpFormattingOptions.SpaceAfterSemicolonsInForStatement, false }, + }; + + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public async Task SpacingForForStatementInfiniteLoopWithSpacesBeforeAndAfter() + { + var code = @" +class Program +{ + void Main() + { + for (;;) + { + } + } +}"; + var expected = @" +class Program +{ + void Main() + { + for ( ; ; ) + { + } + } +}"; + var optionSet = new Dictionary + { + { CSharpFormattingOptions.SpaceBeforeSemicolonsInForStatement, true }, + }; + + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); } [WorkItem(4421, "https://github.com/dotnet/roslyn/issues/4421")] @@ -7434,7 +7528,7 @@ await AssertFormatBodyAsync(expected, @" private Task AssertFormatBodyAsync(string expected, string input) { - Func transform = s => + Func transform = s => { var lines = s.Split(new[] { Environment.NewLine }, StringSplitOptions.None); for (int i = 0; i < lines.Length; i++) From 235acb36141b98f5025ff1bf74eec510e1ceae5d Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 21 Mar 2017 11:55:09 -0700 Subject: [PATCH 003/284] Produce ref assemblies from command-line and msbuild (#17558) * refonly and refout on command-line and msbuild * corresponding change to Emit API * Metadata-only assembly use `throw null` bodies * Can no longer produce netmodule with metadata only --- .../Portable/CSharpResources.Designer.cs | 18 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CommandLine/CSharpCommandLineParser.cs | 38 +- .../Portable/Compilation/CSharpCompilation.cs | 2 - .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../Test/CommandLine/CommandLineTests.cs | 186 +++++++ .../Test/Emit/Emit/CompilationEmitTests.cs | 203 ++++++- .../Test/Emit/Emit/DeterministicTests.cs | 4 +- .../CSharp/Test/Emit/Emit/ResourceTests.cs | 506 +++++++++--------- .../CSharp/Test/Emit/PDB/PDBTests.cs | 3 + .../Core/MSBuildTask/ManagedCompiler.cs | 15 + .../Core/MSBuildTaskTests/CscTests.cs | 18 + .../Core/MSBuildTaskTests/VbcTests.cs | 18 + .../CodeAnalysisResources.Designer.cs | 27 + .../Core/Portable/CodeAnalysisResources.resx | 9 + .../CommandLine/CommonCommandLineArguments.cs | 5 + .../Portable/CommandLine/CommonCompiler.cs | 104 ++-- .../Core/Portable/Compilation/Compilation.cs | 78 +++ .../EditAndContinue/DeltaMetadataWriter.cs | 2 +- .../Portable/PEWriter/FullMetadataWriter.cs | 8 +- .../Core/Portable/PEWriter/MetadataWriter.cs | 118 ++-- .../Core/Portable/PEWriter/PeWriter.cs | 55 +- .../Portable/PEWriter/ReferenceIndexer.cs | 6 +- .../Core/Portable/PublicAPI.Unshipped.txt | 4 +- .../VisualBasicCommandLineParser.vb | 33 +- .../Compilation/VisualBasicCompilation.vb | 2 - .../VisualBasic/Portable/Errors/Errors.vb | 2 + .../Portable/VBResources.Designer.vb | 18 + .../VisualBasic/Portable/VBResources.resx | 6 + .../Test/CommandLine/CommandLineTests.vb | 195 +++++++ .../Test/Emit/Emit/CompilationEmitTests.vb | 236 +++++++- .../ExpressionCompiler/EvaluationContext.cs | 6 +- .../ExpressionCompilerTestHelpers.cs | 2 +- .../ExpressionCompiler/EvaluationContext.vb | 6 +- .../Utilities/Portable/Assert/AssertEx.cs | 18 + .../Compilation/CompilationExtensions.cs | 1 + .../Compilation/IRuntimeEnvironment.cs | 1 + .../Portable/Metadata/MetadataReaderUtils.cs | 138 +++++ 38 files changed, 1715 insertions(+), 384 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 05f0ae7abe233..6625a2fac4a1f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -6766,6 +6766,15 @@ internal static string ERR_NoNamespacePrivate { } } + /// + /// Looks up a localized string similar to Cannot compile net modules when using /refout or /refonly.. + /// + internal static string ERR_NoNetModuleOutputWhenRefOutOrRefOnly { + get { + return ResourceManager.GetString("ERR_NoNetModuleOutputWhenRefOutOrRefOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot create an instance of the abstract class or interface '{0}'. /// @@ -6829,6 +6838,15 @@ internal static string ERR_NoPIANestedType { } } + /// + /// Looks up a localized string similar to Do not use refout when using refonly.. + /// + internal static string ERR_NoRefOutWhenRefOnly { + get { + return ResourceManager.GetString("ERR_NoRefOutWhenRefOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}': cannot override because '{1}' does not have an overridable set accessor. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f5ced08a92dff..148e41e166805 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2288,6 +2288,12 @@ If such a class is used as a base class and if the deriving class defines a dest Merge conflict marker encountered + + Do not use refout when using refonly. + + + Cannot compile net modules when using /refout or /refonly. + Overloadable operator expected diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index f83230991b3f1..eea38cd266404 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -67,6 +67,8 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar string outputDirectory = baseDirectory; ImmutableArray> pathMap = ImmutableArray>.Empty; string outputFileName = null; + string outputRefFilePath = null; + bool metadataOnly = false; string documentationPath = null; string errorLogPath = null; bool parseDocumentationComments = false; //Don't just null check documentationFileName because we want to do this even if the file name is invalid. @@ -379,6 +381,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar } continue; + case "out": if (string.IsNullOrWhiteSpace(value)) { @@ -391,6 +394,26 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar continue; + case "refout": + value = RemoveQuotesAndSlashes(value); + if (string.IsNullOrEmpty(value)) + { + AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, arg); + } + else + { + outputRefFilePath = ParseGenericPathToFile(value, diagnostics, baseDirectory); + } + + continue; + + case "refonly": + if (value != null) + break; + + metadataOnly = true; + continue; + case "t": case "target": if (value == null) @@ -1141,6 +1164,16 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar diagnosticOptions[o.Key] = o.Value; } + if (metadataOnly && outputRefFilePath != null) + { + AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly); + } + + if (outputKind == OutputKind.NetModule && (metadataOnly || outputRefFilePath != null)) + { + AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly); + } + if (!IsScriptRunner && !sourceFilesSpecified && (outputKind.IsNetModule() || !resourcesOrModulesSpecified)) { AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.WRN_NoSources); @@ -1257,7 +1290,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar var emitOptions = new EmitOptions ( - metadataOnly: false, + metadataOnly: metadataOnly, debugInformationFormat: debugInformationFormat, pdbFilePath: null, // to be determined later outputNameOverride: null, // to be determined later @@ -1282,8 +1315,9 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar Utf8Output = utf8output, CompilationName = compilationName, OutputFileName = outputFileName, + OutputRefFilePath = outputRefFilePath, PdbPath = pdbPath, - EmitPdb = emitPdb, + EmitPdb = emitPdb && !metadataOnly, SourceLink = sourceLink, OutputDirectory = outputDirectory, DocumentationPath = documentationPath, diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index f1c43315e2751..c6a3c6f9668f9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2358,8 +2358,6 @@ internal override bool GenerateResourcesAndDocumentationComments( DiagnosticBag diagnostics, CancellationToken cancellationToken) { - Debug.Assert(!moduleBuilder.EmitOptions.EmitMetadataOnly); - // Use a temporary bag so we don't have to refilter pre-existing diagnostics. var resourceDiagnostics = DiagnosticBag.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 3036a614c69cd..8788809a58138 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1461,5 +1461,7 @@ internal enum ErrorCode #endregion more stragglers for C# 7 ERR_Merge_conflict_marker_encountered = 8300, + ERR_NoRefOutWhenRefOnly = 8301, + ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8302, } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 930f4710de55c..d2534145ba37e 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -2786,6 +2786,34 @@ public void ParseOut() // error CS2005: Missing file specification for '/out:' option Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/out:")); + parsedArgs = DefaultParse(new[] { @"/refout:", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS2005: Missing file specification for '/refout:' option + Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/refout:")); + + parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/refonly", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8301: Do not use refout when using refonly. + Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)); + + parsedArgs = DefaultParse(new[] { "/refonly:incorrect", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS2007: Unrecognized option: '/refonly:incorrect' + Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/refonly:incorrect").WithLocation(1, 1) + ); + + parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/target:module", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8302: Cannot compile net modules when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + + parsedArgs = DefaultParse(new[] { @"/refonly", "/target:module", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8302: Cannot compile net modules when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + // Dev11 reports CS2007: Unrecognized option: '/out' parsedArgs = DefaultParse(new[] { @"/out", "a.cs" }, baseDirectory); parsedArgs.Errors.Verify( @@ -8883,6 +8911,164 @@ public void Version() } } + [Fact] + public void RefOut() + { + var dir = Temp.CreateDirectory(); + dir.CreateDirectory("ref"); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@" +class C +{ + /// Main method + public static void Main() + { + System.Console.Write(""Hello""); + } +}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:a.exe", "/refout:ref/a.dll", "/doc:doc.xml", "/deterministic", "a.cs" }); + + int exitCode = csc.Run(outWriter); + Assert.Equal(0, exitCode); + + var exe = Path.Combine(dir.Path, "a.exe"); + Assert.True(File.Exists(exe)); + + MetadataReaderUtils.VerifyPEMetadata(exe, + new[] { "TypeDef:", "TypeDef:C" }, + new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + ); + + var doc = Path.Combine(dir.Path, "doc.xml"); + Assert.True(File.Exists(doc)); + + var content = File.ReadAllText(doc); + var expectedDoc = +@" + + + a + + + + Main method + + +"; + Assert.Equal(expectedDoc, content.Trim()); + + var output = ProcessUtilities.RunAndGetOutput(exe, startFolder: dir.Path); + Assert.Equal("Hello", output.Trim()); + + var refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")); + Assert.True(File.Exists(refDll)); + + // The types and members that are included needs further refinement. + // ReferenceAssemblyAttribute is missing. + // See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + new[] { "TypeDef:", "TypeDef:C" }, + new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + ); + + output = ProcessUtilities.RunAndGetOutput(refDll, startFolder: dir.ToString(), expectedRetCode: -532462766); + + // Clean up temp files + CleanupAllGeneratedFiles(dir.Path); + } + + [Fact] + public void RefOutWithError() + { + var dir = Temp.CreateDirectory(); + dir.CreateDirectory("ref"); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@"class C { public static void Main() { error(); } }"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "a.cs" }); + int exitCode = csc.Run(outWriter); + Assert.Equal(1, exitCode); + + var dll = Path.Combine(dir.Path, "a.dll"); + Assert.False(File.Exists(dll)); + + var refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")); + Assert.False(File.Exists(refDll)); + + Assert.Equal("a.cs(1,39): error CS0103: The name 'error' does not exist in the current context", outWriter.ToString().Trim()); + + // Clean up temp files + CleanupAllGeneratedFiles(dir.Path); + } + + [Fact] + public void RefOnly() + { + var dir = Temp.CreateDirectory(); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@" +class C +{ + /// Main method + public static void Main() + { + error(); // semantic error in method body + } +}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "/doc:doc.xml", "a.cs" }); + int exitCode = csc.Run(outWriter); + Assert.Equal(0, exitCode); + + var refDll = Path.Combine(dir.Path, "a.dll"); + Assert.True(File.Exists(refDll)); + + // The types and members that are included needs further refinement. + // ReferenceAssemblyAttribute is missing. + // See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + new[] { "TypeDef:", "TypeDef:C" }, + new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + ); + + var pdb = Path.Combine(dir.Path, "a.pdb"); + Assert.False(File.Exists(pdb)); + + var doc = Path.Combine(dir.Path, "doc.xml"); + Assert.True(File.Exists(doc)); + + var content = File.ReadAllText(doc); + var expectedDoc = +@" + + + a + + + + Main method + + +"; + Assert.Equal(expectedDoc, content.Trim()); + + // Clean up temp files + CleanupAllGeneratedFiles(dir.Path); + } + public class QuotedArgumentTests { private void VerifyQuotedValid(string name, string value, T expected, Func getValue) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 222d01b278298..bd115c7776b04 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -40,7 +41,7 @@ public void Main() EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } emitResult.Diagnostics.Verify( @@ -148,7 +149,7 @@ namespace N.Foo; EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = comp.Emit(output, null, null, null); + emitResult = comp.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.False(emitResult.Success); @@ -236,6 +237,194 @@ public static void Main() } } + [Theory] + [InlineData("public int M() { return 1; }", "public int M() { return 2; }", true)] + [InlineData("public int M() { return 1; }", "public int M() { error(); }", true)] + [InlineData("", "private void M() { }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("internal void M() { }", "", false)] + [InlineData("public struct S { private int i; }", "public struct S { }", false)] + public void RefAssembly_InvariantToSomeChanges(string change1, string change2, bool expectMatch) + { + string sourceTemplate = @" +public class C +{ + CHANGE +} +"; + string name = GetUniqueName(); + string source1 = sourceTemplate.Replace("CHANGE", change1); + CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source1), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + ImmutableArray image1 = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true)); + + var source2 = sourceTemplate.Replace("CHANGE", change2); + Compilation comp2 = CreateCompilationWithMscorlib(Parse(source2), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + ImmutableArray image2 = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true)); + + if (expectMatch) + { + AssertEx.Equal(image1, image2); + } + else + { + AssertEx.NotEqual(image1, image2); + } + } + + [Theory] + [InlineData("public int M() { error(); }", true)] + [InlineData("public int M() { error() }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public Error M() { return null; }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + public void RefAssembly_IgnoresSomeDiagnostics(string change, bool expectSuccess) + { + string sourceTemplate = @" +public class C +{ + CHANGE +} +"; + string source = sourceTemplate.Replace("CHANGE", change); + string name = GetUniqueName(); + CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + + using (var output = new MemoryStream()) + { + var emitResult = comp1.Emit(output, options: EmitOptions.Default.WithEmitMetadataOnly(true)); + Assert.Equal(expectSuccess, emitResult.Success); + Assert.Equal(!expectSuccess, emitResult.Diagnostics.Any()); + } + } + + [Fact] + public void VerifyRefAssembly() + { + string source = @" +public abstract class PublicClass +{ + public void PublicMethod() { System.Console.Write(""Hello""); } + private void PrivateMethod() { System.Console.Write(""Hello""); } + protected void ProtectedMethod() { System.Console.Write(""Hello""); } + internal void InternalMethod() { System.Console.Write(""Hello""); } + public abstract void AbstractMethod(); +} +"; + CSharpCompilation comp = CreateCompilation(source, references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + var emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(true); + + var verifier = CompileAndVerify(comp, emitOptions: emitRefOnly, verify: true); + + // verify metadata (types, members, attributes) + var image = comp.EmitToImageReference(emitRefOnly); + var comp2 = CreateCompilation("", references: new[] { MscorlibRef, image }, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + AssemblySymbol assembly = comp2.SourceModule.GetReferencedAssemblySymbols().Last(); + var members = assembly.GlobalNamespace.GetMembers(); + AssertEx.SetEqual(members.Select(m => m.ToDisplayString()), + new[] { "", "PublicClass" }); + + AssertEx.SetEqual( + ((NamedTypeSymbol)assembly.GlobalNamespace.GetMember("PublicClass")).GetMembers() + .Select(m => m.ToTestDisplayString()), + new[] { "void PublicClass.PublicMethod()", "void PublicClass.PrivateMethod()", + "void PublicClass.InternalMethod()", "void PublicClass.ProtectedMethod()", + "void PublicClass.AbstractMethod()", "PublicClass..ctor()" }); + + AssertEx.SetEqual(assembly.GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute" }); + + var peImage = comp.EmitToArray(emitRefOnly); + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull: true); + } + [Fact] + public void EmitMetadataOnly_DisallowPdbs() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var pdbOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, pdbOutput, + options: EmitOptions.Default.WithEmitMetadataOnly(true))); + } + } + + [Fact] + public void EmitMetadataOnly_DisallowMetadataPeStream() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var metadataPeOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, metadataPeStream: metadataPeOutput, + options: EmitOptions.Default.WithEmitMetadataOnly(true))); + } + } + + [Fact] + public void EmitMetadata_DisallowOutputtingNetModule() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true).WithOutputKind(OutputKind.NetModule)); + + using (var output = new MemoryStream()) + using (var metadataPeOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, metadataPeStream: metadataPeOutput, + options: EmitOptions.Default)); + } + } + + [Fact] + public void EmitMetadataOnly_DisallowOutputtingNetModule() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true).WithOutputKind(OutputKind.NetModule)); + + using (var output = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + options: EmitOptions.Default.WithEmitMetadataOnly(true))); + } + } + + [Fact] + public void EmitMetadata() + { + string source = @" +public abstract class PublicClass +{ + public void PublicMethod() { System.Console.Write(""Hello""); } +} +"; + CSharpCompilation comp = CreateCompilation(source, references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var pdbOutput = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + var result = comp.Emit(output, pdbOutput, metadataPeStream: metadataOutput); + Assert.True(result.Success); + Assert.NotEqual(0, output.Position); + Assert.NotEqual(0, pdbOutput.Position); + Assert.NotEqual(0, metadataOutput.Position); + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(output.GetBuffer()), expectEmptyOrThrowNull: false); + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(metadataOutput.GetBuffer()), expectEmptyOrThrowNull: true); + } + + var peImage = comp.EmitToArray(); + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull: false); + } + /// /// Check that when we emit metadata only, we include metadata for /// compiler generate methods (e.g. the ones for implicit interface @@ -939,7 +1128,7 @@ public class Test EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.False(emitResult.Success); @@ -972,7 +1161,7 @@ public static void Main() EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.True(emitResult.Success); @@ -1010,7 +1199,7 @@ public static void Main() EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.True(emitResult.Success); @@ -2726,7 +2915,7 @@ public void BrokenPDBStream() var output = new MemoryStream(); var pdb = new BrokenStream(); pdb.BreakHow = BrokenStream.BreakHowType.ThrowOnSetLength; - var result = compilation.Emit(output, pdb); + var result = compilation.Emit(output, pdbStream: pdb); // error CS0041: Unexpected error writing debug information -- 'Exception from HRESULT: 0x806D0004' var err = result.Diagnostics.Single(); @@ -2737,7 +2926,7 @@ public void BrokenPDBStream() Assert.Equal(ioExceptionMessage, (string)err.Arguments[0]); pdb.Dispose(); - result = compilation.Emit(output, pdb); + result = compilation.Emit(output, pdbStream: pdb); // error CS0041: Unexpected error writing debug information -- 'Exception from HRESULT: 0x806D0004' err = result.Diagnostics.Single(); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs index 7af8a4edd7fee..12c92566cb4d3 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs @@ -66,8 +66,8 @@ public void BanVersionWildcards() references: new[] { MscorlibRef }, options: TestOptions.DebugDll.WithDeterministic(false)); - var resultDeterministic = compilationDeterministic.Emit(Stream.Null, Stream.Null); - var resultNonDeterministic = compilationNonDeterministic.Emit(Stream.Null, Stream.Null); + var resultDeterministic = compilationDeterministic.Emit(Stream.Null, pdbStream: Stream.Null); + var resultNonDeterministic = compilationNonDeterministic.Emit(Stream.Null, pdbStream: Stream.Null); Assert.False(resultDeterministic.Success); Assert.True(resultNonDeterministic.Success); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs index dc95a75b61541..a416a2b5979c8 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs @@ -432,323 +432,321 @@ public void AddManagedResource() c1 = null; } + [Fact] public void AddResourceToModule() { - for (int metadataOnlyIfNonzero = 0; metadataOnlyIfNonzero < 2; metadataOnlyIfNonzero++) - { - var metadataOnly = metadataOnlyIfNonzero != 0; - Func emit; - emit = (c, s, r) => c.Emit(s, manifestResources: r, options: new EmitOptions(metadataOnly: metadataOnly)); + bool metadataOnly = false; + Func emit; + emit = (c, s, r) => c.Emit(s, manifestResources: r, options: new EmitOptions(metadataOnly: metadataOnly)); - var sourceTree = SyntaxFactory.ParseSyntaxTree(""); + var sourceTree = SyntaxFactory.ParseSyntaxTree(""); - // Do not name the compilation, a unique guid is used as a name by default. It prevents conflicts with other assemblies loaded via Assembly.ReflectionOnlyLoad. - var c1 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + // Do not name the compilation, a unique guid is used as a name by default. It prevents conflicts with other assemblies loaded via Assembly.ReflectionOnlyLoad. + var c1 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); - var resourceFileName = "RoslynResourceFile.foo"; - var output = new MemoryStream(); + var resourceFileName = "RoslynResourceFile.foo"; + var output = new MemoryStream(); - const string r1Name = "some.dotted.NAME"; - const string r2Name = "another.DoTtEd.NAME"; + const string r1Name = "some.dotted.NAME"; + const string r2Name = "another.DoTtEd.NAME"; - var arrayOfEmbeddedData = new byte[] { 1, 2, 3, 4, 5 }; - var resourceFileData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var arrayOfEmbeddedData = new byte[] { 1, 2, 3, 4, 5 }; + var resourceFileData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - var result = emit(c1, output, - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), - new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) - }); + var result = emit(c1, output, + new ResourceDescription[] + { + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), + new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) + }); - Assert.False(result.Success); - Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); - result = emit(c1, output, - new ResourceDescription[] - { - new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false), - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) - }); + result = emit(c1, output, + new ResourceDescription[] + { + new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false), + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) + }); - Assert.False(result.Success); - Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); - result = emit(c1, output, - new ResourceDescription[] - { - new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) - }); + result = emit(c1, output, + new ResourceDescription[] + { + new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) + }); - Assert.False(result.Success); - Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); - var c_mod1 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + var c_mod1 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); - var output_mod1 = new MemoryStream(); - result = emit(c_mod1, output_mod1, - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) - }); + var output_mod1 = new MemoryStream(); + result = emit(c_mod1, output_mod1, + new ResourceDescription[] + { + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) + }); - Assert.True(result.Success); - var mod1 = ModuleMetadata.CreateFromImage(output_mod1.ToImmutable()); - var ref_mod1 = mod1.GetReference(); - Assert.Equal(ManifestResourceAttributes.Public, mod1.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); + Assert.True(result.Success); + var mod1 = ModuleMetadata.CreateFromImage(output_mod1.ToImmutable()); + var ref_mod1 = mod1.GetReference(); + Assert.Equal(ManifestResourceAttributes.Public, mod1.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); - { - var c2 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1 }, TestOptions.ReleaseDll); - var output2 = new MemoryStream(); - var result2 = c2.Emit(output2); + { + var c2 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1 }, TestOptions.ReleaseDll); + var output2 = new MemoryStream(); + var result2 = c2.Emit(output2); - Assert.True(result2.Success); - var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output2.ToArray()); + Assert.True(result2.Success); + var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output2.ToArray()); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + if (e.Name.Equals(c_mod1.SourceModule.Name)) { - if (e.Name.Equals(c_mod1.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod1.ToArray()); - } + return assembly.LoadModule(e.Name, output_mod1.ToArray()); + } - return null; - }; + return null; + }; - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(1, resourceNames.Length); + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(1, resourceNames.Length); - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(System.Reflection.ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(System.Reflection.ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); - } + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); + } - var c_mod2 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + var c_mod2 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); - var output_mod2 = new MemoryStream(); - result = emit(c_mod2, output_mod2, - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), true) - }); + var output_mod2 = new MemoryStream(); + result = emit(c_mod2, output_mod2, + new ResourceDescription[] + { + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), true) + }); - Assert.True(result.Success); - var ref_mod2 = ModuleMetadata.CreateFromImage(output_mod2.ToImmutable()).GetReference(); + Assert.True(result.Success); + var ref_mod2 = ModuleMetadata.CreateFromImage(output_mod2.ToImmutable()).GetReference(); - { - var c3 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod2 }, TestOptions.ReleaseDll); - var output3 = new MemoryStream(); - var result3 = c3.Emit(output3); + { + var c3 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod2 }, TestOptions.ReleaseDll); + var output3 = new MemoryStream(); + var result3 = c3.Emit(output3); - Assert.True(result3.Success); - var assembly = Assembly.ReflectionOnlyLoad(output3.ToArray()); + Assert.True(result3.Success); + var assembly = Assembly.ReflectionOnlyLoad(output3.ToArray()); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + if (e.Name.Equals(c_mod2.SourceModule.Name)) { - if (e.Name.Equals(c_mod2.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod2.ToArray()); - } - - return null; - }; - - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(2, resourceNames.Length); - - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); - - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); - - rInfo = assembly.GetManifestResourceInfo(r2Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); - - rData = assembly.GetManifestResourceStream(r2Name); - rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(resourceFileData, rBytes); - } + return assembly.LoadModule(e.Name, output_mod2.ToArray()); + } - var c_mod3 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + return null; + }; - var output_mod3 = new MemoryStream(); - result = emit(c_mod3, output_mod3, - new ResourceDescription[] - { - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) - }); + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(2, resourceNames.Length); + + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); - Assert.True(result.Success); - var mod3 = ModuleMetadata.CreateFromImage(output_mod3.ToImmutable()); - var ref_mod3 = mod3.GetReference(); - Assert.Equal(ManifestResourceAttributes.Private, mod3.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); + rInfo = assembly.GetManifestResourceInfo(r2Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); + + rData = assembly.GetManifestResourceStream(r2Name); + rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(resourceFileData, rBytes); + } + + var c_mod3 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); + + var output_mod3 = new MemoryStream(); + result = emit(c_mod3, output_mod3, + new ResourceDescription[] { - var c4 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod3 }, TestOptions.ReleaseDll); - var output4 = new MemoryStream(); - var result4 = c4.Emit(output4, manifestResources: - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), false) - }); + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) + }); - Assert.True(result4.Success); - var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output4.ToArray()); + Assert.True(result.Success); + var mod3 = ModuleMetadata.CreateFromImage(output_mod3.ToImmutable()); + var ref_mod3 = mod3.GetReference(); + Assert.Equal(ManifestResourceAttributes.Private, mod3.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + var c4 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod3 }, TestOptions.ReleaseDll); + var output4 = new MemoryStream(); + var result4 = c4.Emit(output4, manifestResources: + new ResourceDescription[] { - if (e.Name.Equals(c_mod3.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod3.ToArray()); - } + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), false) + }); - return null; - }; + Assert.True(result4.Success); + var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output4.ToArray()); - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(2, resourceNames.Length); + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + if (e.Name.Equals(c_mod3.SourceModule.Name)) + { + return assembly.LoadModule(e.Name, output_mod3.ToArray()); + } - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile, rInfo.ResourceLocation); + return null; + }; - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(2, resourceNames.Length); - rInfo = assembly.GetManifestResourceInfo(r2Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile, rInfo.ResourceLocation); - rData = assembly.GetManifestResourceStream(r2Name); - rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(resourceFileData, rBytes); - } + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); - { - var c5 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod3 }, TestOptions.ReleaseDll); - var output5 = new MemoryStream(); - var result5 = emit(c5, output5, null); + rInfo = assembly.GetManifestResourceInfo(r2Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); + + rData = assembly.GetManifestResourceStream(r2Name); + rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(resourceFileData, rBytes); + } - Assert.True(result5.Success); - var assembly = Assembly.ReflectionOnlyLoad(output5.ToArray()); + { + var c5 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod3 }, TestOptions.ReleaseDll); + var output5 = new MemoryStream(); + var result5 = emit(c5, output5, null); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => - { - if (e.Name.Equals(c_mod1.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod1.ToArray()); - } - else if (e.Name.Equals(c_mod3.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod3.ToArray()); - } - - return null; - }; - - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(2, resourceNames.Length); - - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); - - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); - - rInfo = assembly.GetManifestResourceInfo(r2Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); - - rData = assembly.GetManifestResourceStream(r2Name); - rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(resourceFileData, rBytes); - } + Assert.True(result5.Success); + var assembly = Assembly.ReflectionOnlyLoad(output5.ToArray()); + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => { - var c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseDll); - var output6 = new MemoryStream(); - var result6 = emit(c6, output6, null); - - if (metadataOnly) + if (e.Name.Equals(c_mod1.SourceModule.Name)) { - Assert.True(result6.Success); + return assembly.LoadModule(e.Name, output_mod1.ToArray()); } - else + else if (e.Name.Equals(c_mod3.SourceModule.Name)) { - Assert.False(result6.Success); - result6.Diagnostics.Verify( - // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly - Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME") - ); + return assembly.LoadModule(e.Name, output_mod3.ToArray()); } - result6 = emit(c6, output6, - new ResourceDescription[] - { - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) - }); + return null; + }; - if (metadataOnly) - { - Assert.True(result6.Success); - } - else - { - Assert.False(result6.Success); - result6.Diagnostics.Verify( - // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly - Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME"), - // error CS1508: Resource identifier 'another.DoTtEd.NAME' has already been used in this assembly - Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("another.DoTtEd.NAME") - ); - } + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(2, resourceNames.Length); + + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); + + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); + + rInfo = assembly.GetManifestResourceInfo(r2Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); + + rData = assembly.GetManifestResourceStream(r2Name); + rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(resourceFileData, rBytes); + } - c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseModule); + { + var c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseDll); + var output6 = new MemoryStream(); + var result6 = emit(c6, output6, null); + + if (metadataOnly) + { + Assert.True(result6.Success); + } + else + { + Assert.False(result6.Success); + result6.Diagnostics.Verify( + // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly + Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME") + ); + } - result6 = emit(c6, output6, - new ResourceDescription[] - { - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) - }); + result6 = emit(c6, output6, + new ResourceDescription[] + { + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) + }); + if (metadataOnly) + { Assert.True(result6.Success); } + else + { + Assert.False(result6.Success); + result6.Diagnostics.Verify( + // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly + Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME"), + // error CS1508: Resource identifier 'another.DoTtEd.NAME' has already been used in this assembly + Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("another.DoTtEd.NAME") + ); + } + + c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseModule); + + result6 = emit(c6, output6, + new ResourceDescription[] + { + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) + }); + + Assert.True(result6.Success); } } diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index a0fa5bba7f79f..97817fcf5278a 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -133,6 +133,7 @@ public void SymWriterErrors() { var result = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), @@ -166,6 +167,7 @@ public void SymWriterErrors2() { var result = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), @@ -199,6 +201,7 @@ public void SymWriterErrors3() { var result = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), diff --git a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index b137cf9c561e6..a72cdd6b1281c 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -227,6 +227,13 @@ public ITaskItem OutputAssembly get { return (ITaskItem)_store[nameof(OutputAssembly)]; } } + [Output] + public ITaskItem OutputRefAssembly + { + set { _store[nameof(OutputRefAssembly)] = value; } + get { return (ITaskItem)_store[nameof(OutputRefAssembly)]; } + } + public string Platform { set { _store[nameof(Platform)] = value; } @@ -251,6 +258,12 @@ public ITaskItem[] References get { return (ITaskItem[])_store[nameof(References)]; } } + public bool RefOnly + { + set { _store[nameof(RefOnly)] = value; } + get { return _store.GetOrDefault(nameof(RefOnly), false); } + } + public bool ReportAnalyzer { set { _store[nameof(ReportAnalyzer)] = value; } @@ -705,6 +718,8 @@ protected internal virtual void AddResponseFileCommands(CommandLineBuilderExtens commandLine.AppendPlusOrMinusSwitch("/optimize", _store, nameof(Optimize)); commandLine.AppendSwitchIfNotNull("/pathmap:", PathMap); commandLine.AppendSwitchIfNotNull("/out:", OutputAssembly); + commandLine.AppendSwitchIfNotNull("/refout:", OutputRefAssembly); + commandLine.AppendWhenTrue("/refonly", _store, nameof(RefOnly)); commandLine.AppendSwitchIfNotNull("/ruleset:", CodeAnalysisRuleSet); commandLine.AppendSwitchIfNotNull("/errorlog:", ErrorLog); commandLine.AppendSwitchIfNotNull("/subsystemversion:", SubsystemVersion); diff --git a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs index 3f538e72d9d7e..bd3e3dd55c145 100644 --- a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs @@ -286,5 +286,23 @@ public void Embed() csc.EmbeddedFiles = MSBuildUtil.CreateTaskItems(); Assert.Equal(@"/debug:portable /out:test.exe test.cs", csc.GenerateResponseFileContents()); } + + [Fact] + public void RefOut() + { + var csc = new Csc(); + csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); + csc.OutputRefAssembly = MSBuildUtil.CreateTaskItem("ref\\test.dll"); + Assert.Equal("/out:test.exe /refout:ref\\test.dll test.cs", csc.GenerateResponseFileContents()); + } + + [Fact] + public void RefOnly() + { + var csc = new Csc(); + csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); + csc.RefOnly = true; + Assert.Equal("/out:test.exe /refonly test.cs", csc.GenerateResponseFileContents()); + } } } diff --git a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs index 554cc2b11043e..dd0b5ca23fca7 100644 --- a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs @@ -283,5 +283,23 @@ public void Embed() vbc.EmbeddedFiles = MSBuildUtil.CreateTaskItems(); Assert.Equal(@"/optionstrict:custom /debug:portable /out:test.exe test.vb", vbc.GenerateResponseFileContents()); } + + [Fact] + public void RefOut() + { + var vbc = new Vbc(); + vbc.Sources = MSBuildUtil.CreateTaskItems("test.vb"); + vbc.OutputRefAssembly = MSBuildUtil.CreateTaskItem("ref\\test.dll"); + Assert.Equal("/optionstrict:custom /out:test.exe /refout:ref\\test.dll test.vb", vbc.GenerateResponseFileContents()); + } + + [Fact] + public void RefOnly() + { + var vbc = new Vbc(); + vbc.Sources = MSBuildUtil.CreateTaskItems("test.vb"); + vbc.RefOnly = true; + Assert.Equal("/optionstrict:custom /out:test.exe /refonly test.vb", vbc.GenerateResponseFileContents()); + } } } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index f7d3bbd3e231d..7dd8597b0b3b5 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -269,6 +269,15 @@ internal static string CannotEmbedInteropTypesFromModule { } } + /// + /// Looks up a localized string similar to Cannot target net module when emitting ref assembly.. + /// + internal static string CannotTargetNetModuleWhenEmittingRefAssembly { + get { + return ResourceManager.GetString("CannotTargetNetModuleWhenEmittingRefAssembly", resourceCulture); + } + } + /// /// Looks up a localized string similar to Can't create a module reference to an assembly.. /// @@ -847,6 +856,15 @@ internal static string KeyInPathMapEndsWithSeparator { } } + /// + /// Looks up a localized string similar to Metadata PE stream should not be given when emitting metadata only.. + /// + internal static string MetadataPeStreamUnexpectedWhenEmittingMetadataOnly { + get { + return ResourceManager.GetString("MetadataPeStreamUnexpectedWhenEmittingMetadataOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to MetadataReference '{0}' not found to remove.. /// @@ -1027,6 +1045,15 @@ internal static string PdbStreamUnexpectedWhenEmbedding { } } + /// + /// Looks up a localized string similar to PDB stream should not be given when emitting metadata only.. + /// + internal static string PdbStreamUnexpectedWhenEmittingMetadataOnly { + get { + return ResourceManager.GetString("PdbStreamUnexpectedWhenEmittingMetadataOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to PE image doesn't contain managed metadata.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 3af8d6f2de563..928a36ef445d3 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -330,6 +330,15 @@ PDB stream should not be given when embedding PDB into the PE stream. + + PDB stream should not be given when emitting metadata only. + + + Metadata PE stream should not be given when emitting metadata only. + + + Cannot target net module when emitting ref assembly. + Invalid hash. diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs b/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs index 75c37feaf7c6e..ee5f34e2d4b71 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs @@ -85,6 +85,11 @@ public abstract class CommandLineArguments /// public string OutputFileName { get; internal set; } + /// + /// Path of the output ref assembly or null if not specified. + /// + public string OutputRefFilePath { get; internal set; } + /// /// Path of the PDB file or null if same as output binary path with .pdb extension. /// diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs index 062041c9eb965..21449fc7c2419 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @@ -253,7 +253,7 @@ private static FileStream OpenFileForReadWithSmallBufferOptimization(string file // size, FileStream.Read still allocates the internal buffer. return new FileStream( filePath, - FileMode.Open, + FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize: 1, @@ -561,7 +561,7 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella { return Failed; } - + bool reportAnalyzer = false; CancellationTokenSource analyzerCts = null; AnalyzerManager analyzerManager = null; @@ -751,11 +751,15 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella var peStreamProvider = new CompilerEmitStreamProvider(this, finalPeFilePath); var pdbStreamProviderOpt = emitPdbFile ? new CompilerEmitStreamProvider(this, finalPdbFilePath) : null; + string finalRefPeFilePath = Arguments.OutputRefFilePath; + var refPeStreamProviderOpt = finalRefPeFilePath != null ? new CompilerEmitStreamProvider(this, finalRefPeFilePath) : null; + try { success = compilation.SerializeToPeStream( moduleBeingBuilt, peStreamProvider, + refPeStreamProviderOpt, pdbStreamProviderOpt, testSymWriterFactory: null, diagnostics: diagnosticBag, @@ -765,6 +769,7 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella finally { peStreamProvider.Close(diagnosticBag); + refPeStreamProviderOpt?.Close(diagnosticBag); pdbStreamProviderOpt?.Close(diagnosticBag); } @@ -774,6 +779,10 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella { touchedFilesLogger.AddWritten(finalPdbFilePath); } + if (refPeStreamProviderOpt != null) + { + touchedFilesLogger.AddWritten(finalRefPeFilePath); + } touchedFilesLogger.AddWritten(finalPeFilePath); } } @@ -818,48 +827,9 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella } cancellationToken.ThrowIfCancellationRequested(); - - if (Arguments.TouchedFilesPath != null) + if (!WriteTouchedFiles(consoleOutput, touchedFilesLogger, finalXmlFilePath)) { - Debug.Assert(touchedFilesLogger != null); - - if (finalXmlFilePath != null) - { - touchedFilesLogger.AddWritten(finalXmlFilePath); - } - - string readFilesPath = Arguments.TouchedFilesPath + ".read"; - string writtenFilesPath = Arguments.TouchedFilesPath + ".write"; - - var readStream = OpenFile(readFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); - var writtenStream = OpenFile(writtenFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); - - if (readStream == null || writtenStream == null) - { - return Failed; - } - - string filePath = null; - try - { - filePath = readFilesPath; - using (var writer = new StreamWriter(readStream)) - { - touchedFilesLogger.WriteReadPaths(writer); - } - - filePath = writtenFilesPath; - using (var writer = new StreamWriter(writtenStream)) - { - touchedFilesLogger.WriteWrittenPaths(writer); - } - } - catch (Exception e) - { - Debug.Assert(filePath != null); - MessageProvider.ReportStreamWriteException(e, filePath, consoleOutput); - return Failed; - } + return Failed; } } finally @@ -888,6 +858,54 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella return Succeeded; } + private bool WriteTouchedFiles(TextWriter consoleOutput, TouchedFileLogger touchedFilesLogger, string finalXmlFilePath) + { + if (Arguments.TouchedFilesPath != null) + { + Debug.Assert(touchedFilesLogger != null); + + if (finalXmlFilePath != null) + { + touchedFilesLogger.AddWritten(finalXmlFilePath); + } + + string readFilesPath = Arguments.TouchedFilesPath + ".read"; + string writtenFilesPath = Arguments.TouchedFilesPath + ".write"; + + var readStream = OpenFile(readFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); + var writtenStream = OpenFile(writtenFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); + + if (readStream == null || writtenStream == null) + { + return false; + } + + string filePath = null; + try + { + filePath = readFilesPath; + using (var writer = new StreamWriter(readStream)) + { + touchedFilesLogger.WriteReadPaths(writer); + } + + filePath = writtenFilesPath; + using (var writer = new StreamWriter(writtenStream)) + { + touchedFilesLogger.WriteWrittenPaths(writer); + } + } + catch (Exception e) + { + Debug.Assert(filePath != null); + MessageProvider.ReportStreamWriteException(e, filePath, consoleOutput); + return false; + } + } + + return true; + } + protected virtual ImmutableArray ResolveAdditionalFilesFromArguments(List diagnostics, CommonMessageProvider messageProvider, TouchedFileLogger touchedFilesLogger) { var builder = ImmutableArray.CreateBuilder(); diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 4eeb2bfd0298b..a9ffb9d65fe35 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1964,10 +1964,38 @@ public EmitResult Emit( cancellationToken); } + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public EmitResult Emit( + Stream peStream, + Stream pdbStream, + Stream xmlDocumentationStream, + Stream win32Resources, + IEnumerable manifestResources, + EmitOptions options, + IMethodSymbol debugEntryPoint, + Stream sourceLinkStream, + IEnumerable embeddedTexts, + CancellationToken cancellationToken) + { + return Emit( + peStream, + pdbStream, + xmlDocumentationStream, + win32Resources, + manifestResources, + options, + debugEntryPoint, + sourceLinkStream, + embeddedTexts, + metadataPeStream: null, + cancellationToken: cancellationToken); + } + /// /// Emit the IL for the compiled source code into the specified stream. /// /// Stream to which the compilation will be written. + /// Stream to which the metadata-only output will be written. /// Stream to which the compilation's debug info will be written. Null to forego PDB generation. /// Stream to which the compilation's XML documentation will be written. Null to forego XML generation. /// Stream from which the compilation's Win32 resources will be read (in RES format). @@ -2008,6 +2036,7 @@ public EmitResult Emit( IMethodSymbol debugEntryPoint = null, Stream sourceLinkStream = null, IEnumerable embeddedTexts = null, + Stream metadataPeStream = null, CancellationToken cancellationToken = default(CancellationToken)) { if (peStream == null) @@ -2031,6 +2060,28 @@ public EmitResult Emit( { throw new ArgumentException(CodeAnalysisResources.StreamMustSupportWrite, nameof(pdbStream)); } + + if (options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.PdbStreamUnexpectedWhenEmittingMetadataOnly, nameof(pdbStream)); + } + } + + if (metadataPeStream != null && options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.MetadataPeStreamUnexpectedWhenEmittingMetadataOnly, nameof(metadataPeStream)); + } + + if (this.Options.OutputKind == OutputKind.NetModule) + { + if (metadataPeStream != null) + { + throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(metadataPeStream)); + } + else if (options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(options.EmitMetadataOnly)); + } } if (win32Resources != null) @@ -2055,6 +2106,7 @@ public EmitResult Emit( throw new ArgumentException(CodeAnalysisResources.StreamMustSupportRead, nameof(sourceLinkStream)); } } + if (embeddedTexts != null && !embeddedTexts.IsEmpty()) { if (options == null || @@ -2067,6 +2119,7 @@ public EmitResult Emit( return Emit( peStream, + metadataPeStream, pdbStream, xmlDocumentationStream, win32Resources, @@ -2085,6 +2138,7 @@ public EmitResult Emit( /// internal EmitResult Emit( Stream peStream, + Stream metadataPeStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, @@ -2152,6 +2206,7 @@ internal EmitResult Emit( success = SerializeToPeStream( moduleBeingBuilt, new SimpleEmitStreamProvider(peStream), + (metadataPeStream != null) ? new SimpleEmitStreamProvider(metadataPeStream) : null, (pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null, testData?.SymWriterFactory, diagnostics, @@ -2318,6 +2373,7 @@ internal CommonPEModuleBuilder CheckOptionsAndCreateModuleBuilder( internal bool SerializeToPeStream( CommonPEModuleBuilder moduleBeingBuilt, EmitStreamProvider peStreamProvider, + EmitStreamProvider metadataPeStreamProvider, EmitStreamProvider pdbStreamProvider, Func testSymWriterFactory, DiagnosticBag diagnostics, @@ -2331,6 +2387,7 @@ internal bool SerializeToPeStream( DiagnosticBag metadataDiagnostics = null; DiagnosticBag pdbBag = null; Stream peStream = null; + Stream refPeStream = null; Stream portablePdbStream = null; bool deterministic = IsEmitDeterministic; @@ -2429,12 +2486,33 @@ internal bool SerializeToPeStream( return retStream; }; + Func getRefPeStream; + if (metadataPeStreamProvider != null) + { + getRefPeStream = () => + { + if (metadataDiagnostics.HasAnyErrors()) + { + return null; + } + + refPeStream = metadataPeStreamProvider.GetOrCreateStream(metadataDiagnostics); + Debug.Assert(refPeStream != null || metadataDiagnostics.HasAnyErrors()); + return refPeStream; + }; + } + else + { + getRefPeStream = null; + } + try { if (Cci.PeWriter.WritePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics), this.MessageProvider, getPeStream, + getRefPeStream, getPortablePdbStream, nativePdbWriter, pePdbPath, diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs index 8cc8d4d317326..7c8b2fdb0ec67 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs @@ -55,7 +55,7 @@ public DeltaMetadataWriter( dynamicAnalysisDataWriterOpt: null, context: context, messageProvider: messageProvider, - allowMissingMethodBodies: false, + metadataOnly: false, deterministic: false, cancellationToken: cancellationToken) { diff --git a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs index ddca0955c2b60..47e8694c2b927 100644 --- a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs @@ -37,7 +37,7 @@ internal sealed class FullMetadataWriter : MetadataWriter public static MetadataWriter Create( EmitContext context, CommonMessageProvider messageProvider, - bool allowMissingMethodBodies, + bool metadataOnly, bool deterministic, bool hasPdbStream, CancellationToken cancellationToken) @@ -63,7 +63,7 @@ public static MetadataWriter Create( new DynamicAnalysisDataWriter(context.Module.DebugDocumentCount, context.Module.HintNumberOfMethodDefinitions) : null; - return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, allowMissingMethodBodies, deterministic, cancellationToken); + return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, metadataOnly, deterministic, cancellationToken); } private FullMetadataWriter( @@ -72,10 +72,10 @@ private FullMetadataWriter( MetadataBuilder debugBuilderOpt, DynamicAnalysisDataWriter dynamicAnalysisDataWriterOpt, CommonMessageProvider messageProvider, - bool allowMissingMethodBodies, + bool metadataOnly, bool deterministic, CancellationToken cancellationToken) - : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, allowMissingMethodBodies, deterministic, cancellationToken) + : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, metadataOnly, deterministic, cancellationToken) { // EDMAURER make some intelligent guesses for the initial sizes of these things. int numMethods = this.module.HintNumberOfMethodDefinitions; diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index c65fe050a3595..d54aee2c74a85 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -65,25 +65,27 @@ internal abstract partial class MetadataWriter private readonly int _numTypeDefsEstimate; private readonly bool _deterministic; - // If true, it is allowed to have methods not have bodies (for emitting metadata-only assembly) - internal readonly bool allowMissingMethodBodies; + internal readonly bool MetadataOnly; // A map of method body before token translation to RVA. Used for deduplication of small bodies. private readonly Dictionary, int> _smallMethodBodies; + private static readonly ImmutableArray ThrowNullIL = ImmutableArray.Create((byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw); + private const int ThrowNullMaxStack = 1; + protected MetadataWriter( MetadataBuilder metadata, MetadataBuilder debugMetadataOpt, DynamicAnalysisDataWriter dynamicAnalysisDataWriterOpt, EmitContext context, CommonMessageProvider messageProvider, - bool allowMissingMethodBodies, + bool metadataOnly, bool deterministic, CancellationToken cancellationToken) { this.module = context.Module; _deterministic = deterministic; - this.allowMissingMethodBodies = allowMissingMethodBodies; + this.MetadataOnly = metadataOnly; // EDMAURER provide some reasonable size estimates for these that will avoid // much of the reallocation that would occur when growing these from empty. @@ -2831,22 +2833,30 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr if (method.HasBody()) { - body = method.GetBody(Context); - Debug.Assert(body != null || allowMissingMethodBodies); - - if (body != null) + if (MetadataOnly) { - localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body); - - // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation) - bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup); - - nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter); + bodyOffset = SerializeThrowNullMethodBody(encoder); + localSignatureHandleOpt = default(StandaloneSignatureHandle); + body = null; } else { - bodyOffset = 0; - localSignatureHandleOpt = default(StandaloneSignatureHandle); + body = method.GetBody(Context); + + if (body != null) + { + localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body); + + // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation) + bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup); + + nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter); + } + else + { + bodyOffset = 0; + localSignatureHandleOpt = default(StandaloneSignatureHandle); + } } } else @@ -2863,7 +2873,7 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr } _dynamicAnalysisDataWriterOpt?.SerializeMethodDynamicAnalysisData(body); - + bodyOffsets[methodRid - 1] = bodyOffset; methodRid++; @@ -2872,40 +2882,84 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr return bodyOffsets; } - private int SerializeMethodBody(MethodBodyStreamEncoder encoder, IMethodBody methodBody, StandaloneSignatureHandle localSignatureHandleOpt, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup) - { - int ilLength = methodBody.IL.Length; - var exceptionRegions = methodBody.ExceptionRegions; - bool isSmallBody = ilLength < 64 && methodBody.MaxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0; - - // Check if an identical method body has already been serialized. + private int SerializeThrowNullMethodBody(MethodBodyStreamEncoder encoder) + { + UserStringHandle mvidStringHandle = default(UserStringHandle); + Blob mvidStringFixup = default(Blob); + + int offset = SerializeMethodBody( + encoder, + ThrowNullIL, + ThrowNullMaxStack, + ImmutableArray.Empty, + default(StandaloneSignatureHandle), + MethodBodyAttributes.None, + ref mvidStringHandle, + ref mvidStringFixup); + + Debug.Assert(mvidStringHandle.Equals(default(UserStringHandle)) && mvidStringFixup.Equals(default(Blob))); + return offset; + } + + private int SerializeMethodBody( + MethodBodyStreamEncoder encoder, + IMethodBody methodBody, + StandaloneSignatureHandle localSignatureHandleOpt, + ref UserStringHandle mvidStringHandle, + ref Blob mvidStringFixup) + { + return SerializeMethodBody( + encoder, + methodBody.IL, + methodBody.MaxStack, + methodBody.ExceptionRegions, + localSignatureHandleOpt, + (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : MethodBodyAttributes.None), + ref mvidStringHandle, + ref mvidStringFixup); + } + + private int SerializeMethodBody( + MethodBodyStreamEncoder encoder, + ImmutableArray il, + ushort maxStack, + ImmutableArray exceptionRegions, + StandaloneSignatureHandle localSignatureHandleOpt, + MethodBodyAttributes methodBodyAttributes, + ref UserStringHandle mvidStringHandle, + ref Blob mvidStringFixup) + { + int ilLength = il.Length; + bool isSmallBody = ilLength < 64 && maxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0; + + // Check if an identical method body has already been serialized. // If so, use the RVA of the already serialized one. // Note that we don't need to rewrite the fake tokens in the body before looking it up. // Don't do small body method caching during deterministic builds until this issue is fixed // https://github.com/dotnet/roslyn/issues/7595 int bodyOffset; - if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(methodBody.IL, out bodyOffset)) + if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(il, out bodyOffset)) { return bodyOffset; } var encodedBody = encoder.AddMethodBody( - codeSize: methodBody.IL.Length, - maxStack: methodBody.MaxStack, - exceptionRegionCount: exceptionRegions.Length, + codeSize: ilLength, + maxStack: maxStack, + exceptionRegionCount: exceptionRegions.Length, hasSmallExceptionRegions: MayUseSmallExceptionHeaders(exceptionRegions), - localVariablesSignature: localSignatureHandleOpt, - attributes: (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : 0)); + localVariablesSignature: localSignatureHandleOpt, + attributes: methodBodyAttributes); // Don't do small body method caching during deterministic builds until this issue is fixed // https://github.com/dotnet/roslyn/issues/7595 if (isSmallBody && !_deterministic) { - _smallMethodBodies.Add(methodBody.IL, encodedBody.Offset); + _smallMethodBodies.Add(il, encodedBody.Offset); } - WriteInstructions(encodedBody.Instructions, methodBody.IL, ref mvidStringHandle, ref mvidStringFixup); + WriteInstructions(encodedBody.Instructions, il, ref mvidStringHandle, ref mvidStringFixup); SerializeMethodBodyExceptionHandlerTable(encodedBody.ExceptionRegions, exceptionRegions); return encodedBody.Offset; diff --git a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs index b2d4a571f38ac..ab65cf637ea89 100644 --- a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs @@ -26,21 +26,70 @@ public PeWritingException(Exception inner) internal static class PeWriter { - public static bool WritePeToStream( + internal static bool WritePeToStream( + EmitContext context, + CommonMessageProvider messageProvider, + Func getPeStream, + Func getMetadataPeStreamOpt, + Func getPortablePdbStreamOpt, + PdbWriter nativePdbWriterOpt, + string pdbPathOpt, + bool metadataOnly, + bool isDeterministic, + CancellationToken cancellationToken) + { + if (!WritePeToStream( + context, + messageProvider, + getPeStream, + getPortablePdbStreamOpt, + nativePdbWriterOpt, + pdbPathOpt, + metadataOnly, + isDeterministic, + cancellationToken)) + { + return false; + } + + // produce the secondary output (ref assembly) if needed + if (getMetadataPeStreamOpt != null) + { + Debug.Assert(!metadataOnly); + + if (!WritePeToStream( + context, + messageProvider, + getMetadataPeStreamOpt, + getPortablePdbStreamOpt: null, + nativePdbWriterOpt: null, + pdbPathOpt: null, + metadataOnly: true, + isDeterministic: isDeterministic, + cancellationToken: cancellationToken)) + { + return false; + } + } + + return true; + } + + internal static bool WritePeToStream( EmitContext context, CommonMessageProvider messageProvider, Func getPeStream, Func getPortablePdbStreamOpt, PdbWriter nativePdbWriterOpt, string pdbPathOpt, - bool allowMissingMethodBodies, + bool metadataOnly, bool isDeterministic, CancellationToken cancellationToken) { // If PDB writer is given, we have to have PDB path. Debug.Assert(nativePdbWriterOpt == null || pdbPathOpt != null); - var mdWriter = FullMetadataWriter.Create(context, messageProvider, allowMissingMethodBodies, isDeterministic, getPortablePdbStreamOpt != null, cancellationToken); + var mdWriter = FullMetadataWriter.Create(context, messageProvider, metadataOnly, isDeterministic, getPortablePdbStreamOpt != null, cancellationToken); var properties = context.Module.SerializationProperties; diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs index 8e4b4e3c166ff..b871a1565f161 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs @@ -99,7 +99,7 @@ protected override void RecordAssemblyReference(IAssemblyReference assemblyRefer protected override void ProcessMethodBody(IMethodDefinition method) { - if (method.HasBody()) + if (method.HasBody() && !metadataWriter.MetadataOnly) { var body = method.GetBody(Context); @@ -119,10 +119,6 @@ protected override void ProcessMethodBody(IMethodDefinition method) } } } - else if (!metadataWriter.allowMissingMethodBodies) - { - throw ExceptionUtilities.Unreachable; - } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 04a4963d5f804..8e0ebfab4b46c 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -9,12 +9,14 @@ *REMOVED*static Microsoft.CodeAnalysis.Text.SourceText.From(byte[] buffer, int length, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1, bool throwIfBinaryDetected = false) -> Microsoft.CodeAnalysis.Text.SourceText Microsoft.CodeAnalysis.CommandLineArguments.DisplayVersion.get -> bool Microsoft.CodeAnalysis.CommandLineArguments.EmbeddedFiles.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.CommandLineArguments.OutputRefFilePath.get -> string Microsoft.CodeAnalysis.CommandLineArguments.SourceLink.get -> string Microsoft.CodeAnalysis.Compilation.CreateAnonymousTypeSymbol(System.Collections.Immutable.ImmutableArray memberTypes, System.Collections.Immutable.ImmutableArray memberNames, System.Collections.Immutable.ImmutableArray memberIsReadOnly = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray memberLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateErrorNamespaceSymbol(Microsoft.CodeAnalysis.INamespaceSymbol container, string name) -> Microsoft.CodeAnalysis.INamespaceSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray elementLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray elementTypes, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray elementLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol -Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream = null, System.IO.Stream xmlDocumentationStream = null, System.IO.Stream win32Resources = null, System.Collections.Generic.IEnumerable manifestResources = null, Microsoft.CodeAnalysis.Emit.EmitOptions options = null, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint = null, System.IO.Stream sourceLinkStream = null, System.Collections.Generic.IEnumerable embeddedTexts = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitResult +Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream = null, System.IO.Stream xmlDocumentationStream = null, System.IO.Stream win32Resources = null, System.Collections.Generic.IEnumerable manifestResources = null, Microsoft.CodeAnalysis.Emit.EmitOptions options = null, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint = null, System.IO.Stream sourceLinkStream = null, System.Collections.Generic.IEnumerable embeddedTexts = null, System.IO.Stream metadataPeStream = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitResult +Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream, System.IO.Stream xmlDocumentationStream, System.IO.Stream win32Resources, System.Collections.Generic.IEnumerable manifestResources, Microsoft.CodeAnalysis.Emit.EmitOptions options, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint, System.IO.Stream sourceLinkStream, System.Collections.Generic.IEnumerable embeddedTexts, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream, System.IO.Stream xmlDocumentationStream, System.IO.Stream win32Resources, System.Collections.Generic.IEnumerable manifestResources, Microsoft.CodeAnalysis.Emit.EmitOptions options, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.GetUnreferencedAssemblyIdentities(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.CompilationOptions.WithConcurrentBuild(bool concurrent) -> Microsoft.CodeAnalysis.CompilationOptions diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 0ff31b101c10c..0b8d40df5a51d 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -96,6 +96,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim noStdLib As Boolean = False Dim utf8output As Boolean = False Dim outputFileName As String = Nothing + Dim outputRefFileName As String = Nothing + Dim metadataOnly As Boolean = False Dim outputDirectory As String = baseDirectory Dim documentationPath As String = Nothing Dim errorLogPath As String = Nothing @@ -455,6 +457,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Continue For + Case "refout" + Dim unquoted = RemoveQuotesAndSlashes(value) + If String.IsNullOrEmpty(unquoted) Then + AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":") + Else + outputRefFileName = ParseGenericPathToFile(unquoted, diagnostics, baseDirectory) + End If + Continue For + + Case "refonly", "refonly+" + If value IsNot Nothing Then + AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "refonly") + End If + + metadataOnly = True + Continue For + + Case "t", "target" value = RemoveQuotesAndSlashes(value) outputKind = ParseTarget(name, value, diagnostics) @@ -1194,6 +1214,14 @@ lVbRuntimePlus: specificDiagnosticOptions(item.Key) = item.Value Next + If metadataOnly AndAlso outputRefFileName IsNot Nothing Then + AddDiagnostic(diagnostics, ERRID.ERR_NoRefOutWhenRefOnly) + End If + + If outputKind = OutputKind.NetModule AndAlso (metadataOnly OrElse outputRefFileName IsNot Nothing) Then + AddDiagnostic(diagnostics, ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly) + End If + If Not IsScriptRunner AndAlso Not hasSourceFiles AndAlso managedResources.IsEmpty() Then ' VB displays help when there is nothing specified on the command line If flattenedArgs.Any Then @@ -1344,7 +1372,7 @@ lVbRuntimePlus: reportSuppressedDiagnostics:=reportSuppressedDiagnostics) Dim emitOptions = New EmitOptions( - metadataOnly:=False, + metadataOnly:=metadataOnly, debugInformationFormat:=debugInformationFormat, pdbFilePath:=Nothing, ' to be determined later outputNameOverride:=Nothing, ' to be determined later @@ -1376,6 +1404,7 @@ lVbRuntimePlus: .Utf8Output = utf8output, .CompilationName = compilationName, .OutputFileName = outputFileName, + .OutputRefFilePath = outputRefFileName, .OutputDirectory = outputDirectory, .DocumentationPath = documentationPath, .ErrorLogPath = errorLogPath, @@ -1403,7 +1432,7 @@ lVbRuntimePlus: .ScriptArguments = scriptArgs.AsImmutableOrEmpty(), .TouchedFilesPath = touchedFilesPath, .OutputLevel = outputLevel, - .EmitPdb = emitPdb, + .EmitPdb = emitPdb AndAlso Not metadataOnly, .SourceLink = sourceLink, .DefaultCoreLibraryReference = defaultCoreLibraryReference, .PreferredUILang = preferredUILang, diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 499cd9b6671f3..5f6cf70ab5568 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2270,8 +2270,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic diagnostics As DiagnosticBag, cancellationToken As CancellationToken) As Boolean - Debug.Assert(Not moduleBuilder.EmitOptions.EmitMetadataOnly) - ' Use a temporary bag so we don't have to refilter pre-existing diagnostics. Dim resourceDiagnostics = DiagnosticBag.GetInstance() diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 1d21dcb6380ec..b3e8649f35eb1 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1725,6 +1725,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_BadAssemblyName = 37283 ERR_Merge_conflict_marker_encountered = 37284 + ERR_NoRefOutWhenRefOnly = 37285 + ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 37286 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index d0a6f6a7876b2..1bc0c8a864e7d 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -8070,6 +8070,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Cannot compile net modules when using /refout or /refonly.. + ''' + Friend ReadOnly Property ERR_NoNetModuleOutputWhenRefOutOrRefOnly() As String + Get + Return ResourceManager.GetString("ERR_NoNetModuleOutputWhenRefOutOrRefOnly", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Member '{0}' cannot be initialized in an object initializer expression because it is not a field or property.. ''' @@ -8142,6 +8151,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Do not use refout when using refonly.. + ''' + Friend ReadOnly Property ERR_NoRefOutWhenRefOnly() As String + Get + Return ResourceManager.GetString("ERR_NoRefOutWhenRefOnly", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to unable to open response file '{0}'. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 766722e4338af..edebd886374e4 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5467,4 +5467,10 @@ Merge conflict marker encountered + + Do not use refout when using refonly. + + + Cannot compile net modules when using /refout or /refonly. + \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 63e30a52155f2..72541ce5230b7 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -2787,6 +2787,26 @@ print Goodbye, World" Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName) Assert.Equal("C:\My Folder", parsedArgs.OutputDirectory) + parsedArgs = DefaultParse({"/refout:", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("refout", ":").WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refout:ref.dll", "/refonly", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refonly:incorrect", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_SwitchNeedsBool).WithArguments("refonly").WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refout:ref.dll", "/target:module", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refonly", "/target:module", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/out:C:\""My Folder""\MyBinary.dll", "/t:library", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify( Diagnostic(ERRID.FTL_InputFileNameTooLong).WithArguments("C:""My Folder\MyBinary.dll").WithLocation(1, 1)) @@ -2923,6 +2943,12 @@ print Goodbye, World" parsedArgs = DefaultParse({"/OUT:", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("out", ":")) + parsedArgs = DefaultParse({"/REFOUT:", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("refout", ":")) + + parsedArgs = DefaultParse({"/refout:ref.dll", "/refonly", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/out+", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify(Diagnostic(ERRID.WRN_BadSwitch).WithArguments("/out+")) ' TODO: Dev11 reports ERR_ArgumentRequired @@ -7990,6 +8016,175 @@ End Class Next End Sub + + Public Sub RefOut() + Dim dir = Temp.CreateDirectory() + dir.CreateDirectory("ref") + + Dim src = dir.CreateFile("a.vb") + src.WriteAllText(" +Class C + ''' Main method + Public Shared Sub Main() + System.Console.Write(""Hello"") + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim vbc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:a.exe", "/refout:ref/a.dll", "/doc:doc.xml", "/deterministic", "a.vb"}) + + Dim exitCode = vbc.Run(outWriter) + Assert.Equal(0, exitCode) + + Dim exe = Path.Combine(dir.Path, "a.exe") + Assert.True(File.Exists(exe)) + + MetadataReaderUtils.VerifyPEMetadata(exe, + {"TypeDef:", "TypeDef:C"}, + {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + ) + + Dim doc = Path.Combine(dir.Path, "doc.xml") + Assert.True(File.Exists(doc)) + + Dim content = File.ReadAllText(doc) + Dim expectedDoc = +" + + + +a + + + + + Main method + + +" + Assert.Equal(expectedDoc, content.Trim()) + + Dim output = ProcessUtilities.RunAndGetOutput(exe, startFolder:=dir.Path) + Assert.Equal("Hello", output.Trim()) + + Dim refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")) + Assert.True(File.Exists(refDll)) + + ' The types and members that are included needs further refinement. + ' ReferenceAssemblyAttribute is missing. + ' See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + {"TypeDef:", "TypeDef:C"}, + {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + ) + + output = ProcessUtilities.RunAndGetOutput(refDll, startFolder:=dir.ToString(), expectedRetCode:=-532462766) + + ' Clean up temp files + CleanupAllGeneratedFiles(dir.Path) + End Sub + + + Public Sub RefOutWithError() + Dim dir = Temp.CreateDirectory() + dir.CreateDirectory("ref") + + Dim src = dir.CreateFile("a.vb") + src.WriteAllText( +"Class C + Public Shared Sub Main() + Bad() + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim csc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "a.vb"}) + + Dim exitCode = csc.Run(outWriter) + Assert.Equal(1, exitCode) + + Dim vb = Path.Combine(dir.Path, "a.vb") + + Dim dll = Path.Combine(dir.Path, "a.dll") + Assert.False(File.Exists(dll)) + + Dim refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")) + Assert.False(File.Exists(refDll)) + + Assert.Equal( +$"{vb}(3) : error BC30451: 'Bad' is not declared. It may be inaccessible due to its protection level. + + Bad() + ~~~", +outWriter.ToString().Trim()) + + ' Clean up temp files + CleanupAllGeneratedFiles(dir.Path) + End Sub + + + Public Sub RefOnly() + Dim dir = Temp.CreateDirectory() + + Dim src = dir.CreateFile("a.vb") + src.WriteAllText( +"Class C + ''' Main method + Public Shared Sub Main() + Bad() + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim csc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "/doc:doc.xml", "a.vb"}) + + Dim exitCode = csc.Run(outWriter) + Assert.Equal(0, exitCode) + + Dim refDll = Path.Combine(dir.Path, "a.dll") + Assert.True(File.Exists(refDll)) + + ' The types and members that are included needs further refinement. + ' ReferenceAssemblyAttribute is missing. + ' See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + {"TypeDef:", "TypeDef:C"}, + {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + ) + + Dim pdb = Path.Combine(dir.Path, "a.pdb") + Assert.False(File.Exists(pdb)) + + Dim doc = Path.Combine(dir.Path, "doc.xml") + Assert.True(File.Exists(doc)) + + Dim content = File.ReadAllText(doc) + Dim expectedDoc = +" + + + +a + + + + + Main method + + +" + Assert.Equal(expectedDoc, content.Trim()) + + ' Clean up temp files + CleanupAllGeneratedFiles(dir.Path) + End Sub + 'Output with known but different extension diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index ff3349e10f719..775744fdbbce5 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -260,7 +260,7 @@ End Namespace ) - Dim emitResult As emitResult + Dim emitResult As EmitResult Dim mdOnlyImage As Byte() Using output = New MemoryStream() @@ -297,6 +297,208 @@ End Class End Using End Sub + + Public Sub RefAssembly_InvariantToSomeChanges() + + RefAssembly_InvariantToSomeChanges( +"Public Function M() As Integer + Return 1 +End Function", +"Public Function M() As Integer + Return 2 +End Function", True) + + RefAssembly_InvariantToSomeChanges( +"Public Function M() As Integer + Return 1 +End Function", +"Public Function M() As Integer +End Function", True) + + RefAssembly_InvariantToSomeChanges( +"", +"Private Sub M() +End Sub", False) ' Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + + RefAssembly_InvariantToSomeChanges( +"Friend Sub M() +End Sub", +"", False) + + RefAssembly_InvariantToSomeChanges( +"Public Structure S + Private Dim i As Integer +End Structure", +"Public Structure S +End Structure", False) + + End Sub + + Private Sub RefAssembly_InvariantToSomeChanges(change1 As String, change2 As String, expectMatch As Boolean) + Dim sourceTemplate As String = " +Public Class C + CHANGE +End Class" + + Dim name As String = GetUniqueName() + Dim source1 As String = sourceTemplate.Replace("CHANGE", change1) + Dim comp1 = CreateCompilationWithMscorlib(source1, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + Dim image1 As ImmutableArray(Of Byte) = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True)) + + Dim source2 = sourceTemplate.Replace("CHANGE", change2) + Dim comp2 = CreateCompilationWithMscorlib(source2, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + Dim image2 As ImmutableArray(Of Byte) = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True)) + + If expectMatch Then + AssertEx.Equal(image1, image2) + Else + AssertEx.NotEqual(image1, image2) + End If + End Sub + + + Public Sub RefAssembly_IgnoresSomeDiagnostics() + + RefAssembly_IgnoresSomeDiagnostics( +"Public Function M() As Integer +End Function", True) + + RefAssembly_IgnoresSomeDiagnostics( +"Public Function M() As Integer + Error( +End Function", False) ' Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + + RefAssembly_IgnoresSomeDiagnostics( +"Public Function M() As Error +End Function", False) ' This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + End Sub + + Private Sub RefAssembly_IgnoresSomeDiagnostics(change As String, expectSuccess As Boolean) + Dim sourceTemplate As String = " +Public Class C + CHANGE +End Class" + + Dim name As String = GetUniqueName() + Dim source As String = sourceTemplate.Replace("CHANGE", change) + Dim comp = CreateCompilationWithMscorlib(source, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + + Using output As New MemoryStream() + Dim EmitResult = comp.Emit(output, options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.Equal(expectSuccess, EmitResult.Success) + Assert.Equal(Not expectSuccess, EmitResult.Diagnostics.Any()) + End Using + + End Sub + + + Public Sub VerifyRefAssembly() + Dim source = " +Public MustInherit Class PublicClass + Public Sub PublicMethod() + System.Console.Write(""Hello"") + End Sub + Private Sub PrivateMethod() + System.Console.Write(""Hello"") + End Sub + Protected Sub ProtectedMethod() + System.Console.Write(""Hello"") + End Sub + Friend Sub InternalMethod() + System.Console.Write(""Hello"") + End Sub + Public MustOverride Sub AbstractMethod() +End Class" + Dim comp As Compilation = CreateCompilation(source, references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Dim emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(True) + + Dim verifier = CompileAndVerify(comp, emitOptions:=emitRefOnly, verify:=True) + + ' verify metadata (types, members, attributes) + Dim image = comp.EmitToImageReference(emitRefOnly) + Dim comp2 = CreateCompilation("", references:={MscorlibRef, image}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim assembly As AssemblySymbol = comp2.SourceModule.GetReferencedAssemblySymbols().Last() + Dim members = assembly.GlobalNamespace.GetMembers() + AssertEx.SetEqual(members.Select(Function(m) m.ToDisplayString()), + {"", "PublicClass"}) + + AssertEx.SetEqual( + DirectCast(assembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). + Select(Function(m) m.ToTestDisplayString()), + {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", + "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", + "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}) + + AssertEx.SetEqual(assembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute"}) + + Dim peImage = comp.EmitToArray(emitRefOnly) + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull:=True) + End Sub + + + Public Sub EmitMetadataOnly_DisallowPdbs() + Dim comp = CreateCompilation("", references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using pdbOutput = New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, pdbOutput, + options:=EmitOptions.Default.WithEmitMetadataOnly(True))) + End Using + End Using + End Sub + + + Public Sub EmitMetadataOnly_DisallowMetadataPeStream() + Dim comp = CreateCompilation("", references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using metadataPeOutput As New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, metadataPeStream:=metadataPeOutput, + options:=EmitOptions.Default.WithEmitMetadataOnly(True))) + End Using + End Using + End Sub + + + Public Sub EmitMetadata() + Dim source = +"Public MustInherit Class PublicClass + Public Sub PublicMethod + System.Console.Write(""Hello"") + End Sub +End Class " + Dim comp = CreateCompilation(source, references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using pdbOutput As New MemoryStream() + Using metadataOutput As New MemoryStream() + Dim result = comp.Emit(output, pdbOutput, metadataPeStream:=metadataOutput) + Assert.True(result.Success) + Assert.NotEqual(0, output.Position) + Assert.NotEqual(0, pdbOutput.Position) + Assert.NotEqual(0, metadataOutput.Position) + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(output.GetBuffer()), expectEmptyOrThrowNull:=False) + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(metadataOutput.GetBuffer()), expectEmptyOrThrowNull:=True) + End Using + End Using + End Using + + Dim peImage = comp.EmitToArray() + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull:=False) + End Sub + Public Sub Bug4344() @@ -573,21 +775,21 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=Nothing) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CorFlags.ILOnly, peHeaders.CorHeader.Flags) compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.X86)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CorFlags.ILOnly Or CorFlags.Requires32Bit, peHeaders.CorHeader.Flags) compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CorFlags.ILOnly, peHeaders.CorHeader.Flags) Assert.True(peHeaders.Requires64Bits) Assert.True(peHeaders.RequiresAmdInstructionSet) compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.AnyCpu32BitPreferred)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.False(peHeaders.Requires64Bits) Assert.False(peHeaders.RequiresAmdInstructionSet) Assert.Equal(CorFlags.ILOnly Or CorFlags.Requires32Bit Or CorFlags.Prefers32Bit, peHeaders.CorHeader.Flags) @@ -606,7 +808,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.AnyCpu)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -631,7 +833,7 @@ End Module ' test an exe as well: compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.AnyCpu)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -667,7 +869,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.Arm)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -692,7 +894,7 @@ End Module ' test an exe as well: compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.AnyCpu)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -728,7 +930,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.X64)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.True(peHeaders.Requires64Bits) @@ -748,7 +950,7 @@ End Module ' test an exe as well: compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.True(peHeaders.Requires64Bits) @@ -804,7 +1006,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.WindowsRuntimeApplication)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CType(&H9540, UShort), peHeaders.PEHeader.DllCharacteristics) 'DYNAMIC_BASE | NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE | HIGH_ENTROPY_VA (0x20) End Sub @@ -837,15 +1039,15 @@ End Module ' default for 32 bit compilation = CreateCompilationWithMscorlib(source, options:=TestOptions.ReleaseExe.WithPlatform(Platform.X86)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(&H400000UL, peHeaders.PEHeader.ImageBase) compilation = CreateCompilationWithMscorlib(source, options:=TestOptions.ReleaseExe.WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(&H140000000UL, peHeaders.PEHeader.ImageBase) compilation = CreateCompilationWithMscorlib(source, options:=TestOptions.ReleaseDll.WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(&H180000000UL, peHeaders.PEHeader.ImageBase) End Sub @@ -867,7 +1069,7 @@ End Module Assert.Equal(1024, peHeaders.PEHeader.FileAlignment) compilation = CreateCompilationWithMscorlib(source) - peHeaders = New peHeaders(compilation.EmitToStream(options:=New EmitOptions(fileAlignment:=4096))) + peHeaders = New PEHeaders(compilation.EmitToStream(options:=New EmitOptions(fileAlignment:=4096))) Assert.Equal(4096, peHeaders.PEHeader.FileAlignment) End Sub @@ -1743,7 +1945,7 @@ end namespace Dim sourceAssembly = DirectCast(assembly, SourceAssemblySymbol) Dim compilation = sourceAssembly.DeclaringCompilation - Dim emitOptions = New emitOptions(outputNameOverride:=sourceAssembly.Name) + Dim emitOptions = New EmitOptions(outputNameOverride:=sourceAssembly.Name) Dim assemblyBuilder = New PEAssemblyBuilder(sourceAssembly, emitOptions, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable(Of ResourceDescription), Nothing) Dim assemblySecurityAttributes As IEnumerable(Of Cci.SecurityAttribute) = assemblyBuilder.GetSourceAssemblySecurityAttributes() diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index cda9d235db5cc..cc1ba417828aa 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -263,7 +263,7 @@ internal override CompileResult CompileExpression( getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, - allowMissingMethodBodies: false, + metadataOnly: false, isDeterministic: false, cancellationToken: default(CancellationToken)); @@ -352,7 +352,7 @@ internal override CompileResult CompileAssignment( getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, - allowMissingMethodBodies: false, + metadataOnly: false, isDeterministic: false, cancellationToken: default(CancellationToken)); @@ -396,7 +396,7 @@ internal override ReadOnlyCollection CompileGetLocals( getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, - allowMissingMethodBodies: false, + metadataOnly: false, isDeterministic: false, cancellationToken: default(CancellationToken)); diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs index 2d4096440e450..64257ff58dd09 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs @@ -789,7 +789,7 @@ internal static void EmitCorLibWithAssemblyReferences( () => peStream, () => pdbStream, null, null, - allowMissingMethodBodies: true, + metadataOnly: true, isDeterministic: false, cancellationToken: default(CancellationToken)); diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb index 619b89fb23c8b..2dc724c6a37fd 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb @@ -398,7 +398,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator getPortablePdbStreamOpt:=Nothing, nativePdbWriterOpt:=Nothing, pdbPathOpt:=Nothing, - allowMissingMethodBodies:=False, + metadataOnly:=False, isDeterministic:=False, cancellationToken:=Nothing) @@ -442,7 +442,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator getPortablePdbStreamOpt:=Nothing, nativePdbWriterOpt:=Nothing, pdbPathOpt:=Nothing, - allowMissingMethodBodies:=False, + metadataOnly:=False, isDeterministic:=False, cancellationToken:=Nothing) @@ -486,7 +486,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator getPortablePdbStreamOpt:=Nothing, nativePdbWriterOpt:=Nothing, pdbPathOpt:=Nothing, - allowMissingMethodBodies:=False, + metadataOnly:=False, isDeterministic:=False, cancellationToken:=Nothing) diff --git a/src/Test/Utilities/Portable/Assert/AssertEx.cs b/src/Test/Utilities/Portable/Assert/AssertEx.cs index 55f36d0a7b1bd..b52bb6578990c 100644 --- a/src/Test/Utilities/Portable/Assert/AssertEx.cs +++ b/src/Test/Utilities/Portable/Assert/AssertEx.cs @@ -210,6 +210,24 @@ public static void Equal(IEnumerable expected, IEnumerable actual, IEqu } } + public static void NotEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null, string message = null, + string itemSeparator = null, Func itemInspector = null) + { + if (ReferenceEquals(expected, actual)) + { + Fail("expected and actual references are identical\r\n" + message); + } + + if (expected == null || actual == null) + { + return; + } + else if (SequenceEqual(expected, actual, comparer)) + { + Fail("expected and actual sequences match\r\n" + message); + } + } + private static bool SequenceEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null) { var enumerator1 = expected.GetEnumerator(); diff --git a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs index acb32e390b463..f3bcd50536fbf 100644 --- a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs +++ b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs @@ -45,6 +45,7 @@ internal static ImmutableArray EmitToArray( var emitResult = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, diff --git a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs index bee66b9519f56..b767804c589bb 100644 --- a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs +++ b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs @@ -235,6 +235,7 @@ EmitOptions emitOptions { result = compilation.Emit( executableStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, diff --git a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs index 025cf50f99b6d..5e1f8db49ddf5 100644 --- a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs +++ b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs @@ -265,9 +265,147 @@ public static string Dump(this MetadataReader reader, EntityHandle handle) { case HandleKind.AssemblyReference: return "AssemblyRef:" + reader.GetString(reader.GetAssemblyReference((AssemblyReferenceHandle)handle).Name); + case HandleKind.TypeDefinition: + return "TypeDef:" + reader.GetString(reader.GetTypeDefinition((TypeDefinitionHandle)handle).Name); + case HandleKind.MethodDefinition: + var method = reader.GetMethodDefinition((MethodDefinitionHandle)handle); + var blob = reader.GetBlobReader(method.Signature); + var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, reader, genericContext: null); + var signature = decoder.DecodeMethodSignature(ref blob); + var parameters = signature.ParameterTypes.Join(", "); + return $"MethodDef: {signature.ReturnType} {reader.GetString(method.Name)}({parameters})"; default: return handle.Kind.ToString(); } } + + private sealed class ConstantSignatureVisualizer : ISignatureTypeProvider + { + public static readonly ConstantSignatureVisualizer Instance = new ConstantSignatureVisualizer(); + + public string GetArrayType(string elementType, ArrayShape shape) + { + return elementType + "[" + new string(',', shape.Rank) + "]"; + } + + public string GetByReferenceType(string elementType) + { + return elementType + "&"; + } + + public string GetFunctionPointerType(MethodSignature signature) + { + return "method-ptr"; + } + + public string GetGenericInstantiation(string genericType, ImmutableArray typeArguments) + { + return genericType + "{" + string.Join(", ", typeArguments) + "}"; + } + + public string GetGenericMethodParameter(object genericContext, int index) + { + return "!!" + index; + } + + public string GetGenericTypeParameter(object genericContext, int index) + { + return "!" + index; + } + + public string GetModifiedType(string modifier, string unmodifiedType, bool isRequired) + { + return (isRequired ? "modreq" : "modopt") + "(" + modifier + ") " + unmodifiedType; + } + + public string GetPinnedType(string elementType) + { + return "pinned " + elementType; + } + + public string GetPointerType(string elementType) + { + return elementType + "*"; + } + + public string GetPrimitiveType(PrimitiveTypeCode typeCode) + { + return typeCode.ToString(); + } + + public string GetSZArrayType(string elementType) + { + return elementType + "[]"; + } + + public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) + { + var typeDef = reader.GetTypeDefinition(handle); + var name = reader.GetString(typeDef.Name); + return typeDef.Namespace.IsNil ? name : reader.GetString(typeDef.Namespace) + "." + name; + } + + public string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) + { + var typeRef = reader.GetTypeReference(handle); + var name = reader.GetString(typeRef.Name); + return typeRef.Namespace.IsNil ? name : reader.GetString(typeRef.Namespace) + "." + name; + } + + public string GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) + { + var sigReader = reader.GetBlobReader(reader.GetTypeSpecification(handle).Signature); + return new SignatureDecoder(Instance, reader, genericContext).DecodeType(ref sigReader); + } + } + + internal static void VerifyPEMetadata(string pePath, string[] types, string[] methods, string[] attributes) + { + using (var peStream = File.OpenRead(pePath)) + using (var refPeReader = new PEReader(peStream)) + { + var metadataReader = refPeReader.GetMetadataReader(); + + AssertEx.SetEqual(metadataReader.TypeDefinitions.Select(t => metadataReader.Dump(t)), types); + AssertEx.SetEqual(metadataReader.MethodDefinitions.Select(t => metadataReader.Dump(t)), methods); + + AssertEx.SetEqual( + metadataReader.CustomAttributes.Select(a => metadataReader.GetCustomAttribute(a).Constructor) + .Select(c => metadataReader.GetMemberReference((MemberReferenceHandle)c).Parent) + .Select(p => metadataReader.GetTypeReference((TypeReferenceHandle)p).Name) + .Select(n => metadataReader.GetString(n)), + attributes); + } + } + + /// + /// Asserts that all the method bodies in the PE are empty or `throw null`, if expectEmptyOrThrowNull is true. + /// Asserts that none of the methods bodies are empty or `throw null`, if expectEmptyOrThrowNull is false. + /// + internal static void VerifyMethodBodies(ImmutableArray peImage, bool expectEmptyOrThrowNull) + { + using (var peReader = new PEReader(peImage)) + { + var metadataReader = peReader.GetMetadataReader(); + foreach (var method in metadataReader.MethodDefinitions) + { + var rva = metadataReader.GetMethodDefinition(method).RelativeVirtualAddress; + if (rva != 0) + { + var il = peReader.GetMethodBody(rva).GetILBytes(); + var throwNull = new[] { (byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw }; + + if (expectEmptyOrThrowNull) + { + AssertEx.Equal(throwNull, il); + } + else + { + AssertEx.NotEqual(throwNull, il); + } + } + } + } + } } } From da63887be99377ca8387e87782031f896d21e75e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 22 Mar 2017 16:29:31 -0700 Subject: [PATCH 004/284] Allow building nuget package from features/refout branch (#18082) --- build/Targets/Versions.props | 3 ++- src/Tools/MicroBuild/publish-assets.ps1 | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/Targets/Versions.props b/build/Targets/Versions.props index 548467b0d76d0..5467a46929d4f 100644 --- a/build/Targets/Versions.props +++ b/build/Targets/Versions.props @@ -14,7 +14,8 @@ dev - beta1 + + refout $(RoslynFileVersionBase)-$(RoslynNuGetMoniker) diff --git a/src/Tools/MicroBuild/publish-assets.ps1 b/src/Tools/MicroBuild/publish-assets.ps1 index 671696f880cf1..cb04801db8162 100644 --- a/src/Tools/MicroBuild/publish-assets.ps1 +++ b/src/Tools/MicroBuild/publish-assets.ps1 @@ -43,6 +43,7 @@ try "dev15.0.x" { } "master" { } "post-dev15" { } + "features/refout" { } default { if (-not $test) From 2d8158d8f1968b3ea6e1579ede1beee762db3fd4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 23 Mar 2017 13:21:37 -0700 Subject: [PATCH 005/284] Make refout tests more resilient to environment differences (#18114) --- .../Test/CommandLine/CommandLineTests.cs | 42 +++++++++++++++++-- .../Test/CommandLine/CommandLineTests.vb | 37 ++++++++++++++-- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index d2534145ba37e..76b0111e77675 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -8915,11 +8915,11 @@ public void Version() public void RefOut() { var dir = Temp.CreateDirectory(); - dir.CreateDirectory("ref"); + var refDir = dir.CreateDirectory("ref"); var src = dir.CreateFile("a.cs"); src.WriteAllText(@" -class C +public class C { /// Main method public static void Main() @@ -8965,7 +8965,7 @@ public static void Main() var output = ProcessUtilities.RunAndGetOutput(exe, startFolder: dir.Path); Assert.Equal("Hello", output.Trim()); - var refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")); + var refDll = Path.Combine(refDir.Path, "a.dll"); Assert.True(File.Exists(refDll)); // The types and members that are included needs further refinement. @@ -8977,10 +8977,44 @@ public static void Main() new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } ); - output = ProcessUtilities.RunAndGetOutput(refDll, startFolder: dir.ToString(), expectedRetCode: -532462766); + VerifyNullReferenceException(refDir, "a.dll"); // Clean up temp files CleanupAllGeneratedFiles(dir.Path); + CleanupAllGeneratedFiles(refDir.Path); + } + + /// + /// Calls C.Main() on the program and verifies that a null reference exception is thrown. + /// + private static void VerifyNullReferenceException(TempDirectory dir, string programName) + { + var src = dir.CreateFile("verifier.cs"); + src.WriteAllText(@" +class Verifier +{ + public static void Main() + { + try + { + C.Main(); + } + catch(System.NullReferenceException) + { + System.Console.Write(""Got expected exception""); + } + } +}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:verifier.exe", $"/reference:{programName}", "verifier.cs" }); + + int exitCode = csc.Run(outWriter); + Assert.Equal(0, exitCode); + string dirPath = dir.ToString(); + string output = ProcessUtilities.RunAndGetOutput(Path.Combine(dirPath, "verifier.exe"), startFolder: dirPath, expectedRetCode: 0); + Assert.Equal("Got expected exception", output); } [Fact] diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 72541ce5230b7..7844775a85fd5 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -8019,11 +8019,11 @@ End Class Public Sub RefOut() Dim dir = Temp.CreateDirectory() - dir.CreateDirectory("ref") + Dim refDir = dir.CreateDirectory("ref") Dim src = dir.CreateFile("a.vb") src.WriteAllText(" -Class C +Public Class C ''' Main method Public Shared Sub Main() System.Console.Write(""Hello"") @@ -8069,7 +8069,7 @@ a Dim output = ProcessUtilities.RunAndGetOutput(exe, startFolder:=dir.Path) Assert.Equal("Hello", output.Trim()) - Dim refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")) + Dim refDll = Path.Combine(refDir.Path, "a.dll") Assert.True(File.Exists(refDll)) ' The types and members that are included needs further refinement. @@ -8081,10 +8081,39 @@ a {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} ) - output = ProcessUtilities.RunAndGetOutput(refDll, startFolder:=dir.ToString(), expectedRetCode:=-532462766) + VerifyNullReferenceException(refDir, "a.dll") ' Clean up temp files CleanupAllGeneratedFiles(dir.Path) + CleanupAllGeneratedFiles(refDir.Path) + End Sub + + ''' + ''' Calls C.Main() on the program and verifies that a null reference exception is thrown. + ''' + Private Shared Sub VerifyNullReferenceException(dir As TempDirectory, referenceName As String) + + Dim src = dir.CreateFile("verifier.vb") + src.WriteAllText(" +Class Verifier + Public Shared Sub Main() + Try + C.Main() + Catch ex As System.NullReferenceException + System.Console.Write(""Got expected exception"") + End Try + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim vbc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:verifier.exe", $"/reference:{referenceName}", "verifier.vb"}) + + Dim exitCode = vbc.Run(outWriter) + Assert.Equal(0, exitCode) + Dim dirPath As String = dir.ToString() + Dim output = ProcessUtilities.RunAndGetOutput(Path.Combine(dirPath, "verifier.exe"), startFolder:=dirPath, expectedRetCode:=0) + Assert.Equal("Got expected exception", output) End Sub From 5fef4b210ba576fb6ba70c69ddfeda05f7623a72 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 23 Mar 2017 16:35:02 -0700 Subject: [PATCH 006/284] Fix publish-assets.ps1 for refout feature branch (#18129) --- src/Tools/MicroBuild/publish-assets.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/MicroBuild/publish-assets.ps1 b/src/Tools/MicroBuild/publish-assets.ps1 index cb04801db8162..3e9b505aa03cc 100644 --- a/src/Tools/MicroBuild/publish-assets.ps1 +++ b/src/Tools/MicroBuild/publish-assets.ps1 @@ -43,7 +43,7 @@ try "dev15.0.x" { } "master" { } "post-dev15" { } - "features/refout" { } + "features_refout" { } default { if (-not $test) From 20aa0c2fae8a0326540328cb6d759d64cd891901 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 24 Mar 2017 19:29:03 -0700 Subject: [PATCH 007/284] Ref assemblies: Address more feedback on #17558 (#18091) --- .../CSharp/Portable/CSharpResources.resx | 2 + .../CommandLine/CSharpCommandLineParser.cs | 2 +- .../Test/Emit/Emit/CompilationEmitTests.cs | 54 ++++++- .../CSharp/Test/Emit/PDB/PDBTests.cs | 6 +- .../CodeAnalysisResources.Designer.cs | 9 ++ .../Core/Portable/CodeAnalysisResources.resx | 3 + .../Core/Portable/Compilation/Compilation.cs | 83 +++++++++-- .../Portable/PEWriter/FullMetadataWriter.cs | 2 +- .../Core/Portable/PEWriter/MetadataWriter.cs | 139 ++++++++---------- .../Core/Portable/PEWriter/PeWriter.cs | 49 ------ .../Core/Portable/PublicAPI.Unshipped.txt | 2 +- .../VisualBasicCommandLineParser.vb | 2 +- .../VisualBasic/Portable/VBResources.resx | 2 + .../Test/Emit/Emit/CompilationEmitTests.vb | 12 +- .../Compilation/CompilationExtensions.cs | 2 +- .../Compilation/IRuntimeEnvironment.cs | 2 +- .../Portable/Metadata/MetadataReaderUtils.cs | 89 ++++++----- 17 files changed, 249 insertions(+), 211 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index a0e2a83ab5045..84fd32a1ba6e3 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4402,6 +4402,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ /target:winmdobj Build a Windows Runtime intermediate file that is consumed by WinMDExp (Short form: /t:winmdobj) /doc:<file> XML Documentation file to generate + /refout:<file> Reference assembly output to generate /platform:<string> Limit which platforms this code can run on: x86, Itanium, x64, arm, anycpu32bitpreferred, or anycpu. The default is anycpu. @@ -4445,6 +4446,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ /optimize[+|-] Enable optimizations (Short form: /o) /deterministic Produce a deterministic assembly (including module version GUID and timestamp) + /refonly Produce a reference assembly in place of the main output /instrument:TestCoverage Produce an assembly instrumented to collect coverage information /sourcelink:<file> Source link info to embed into Portable PDB. diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index afbd776426d49..7d19a65d1ac3b 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -1322,7 +1322,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar OutputFileName = outputFileName, OutputRefFilePath = outputRefFilePath, PdbPath = pdbPath, - EmitPdb = emitPdb && !metadataOnly, + EmitPdb = emitPdb && !metadataOnly, // silently ignore emitPdb when metadataOnly is set SourceLink = sourceLink, OutputDirectory = outputDirectory, DocumentationPath = documentationPath, diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index bd115c7776b04..20b13f8ac0e1c 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -339,7 +339,7 @@ public abstract class PublicClass "System.Diagnostics.DebuggableAttribute" }); var peImage = comp.EmitToArray(emitRefOnly); - MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull: true); + MetadataReaderUtils.AssertEmptyOrThrowNull(peImage); } [Fact] public void EmitMetadataOnly_DisallowPdbs() @@ -364,7 +364,7 @@ public void EmitMetadataOnly_DisallowMetadataPeStream() using (var output = new MemoryStream()) using (var metadataPeOutput = new MemoryStream()) { - Assert.Throws(() => comp.Emit(output, metadataPeStream: metadataPeOutput, + Assert.Throws(() => comp.Emit(output, metadataPEStream: metadataPeOutput, options: EmitOptions.Default.WithEmitMetadataOnly(true))); } } @@ -378,7 +378,7 @@ public void EmitMetadata_DisallowOutputtingNetModule() using (var output = new MemoryStream()) using (var metadataPeOutput = new MemoryStream()) { - Assert.Throws(() => comp.Emit(output, metadataPeStream: metadataPeOutput, + Assert.Throws(() => comp.Emit(output, metadataPEStream: metadataPeOutput, options: EmitOptions.Default)); } } @@ -396,6 +396,46 @@ public void EmitMetadataOnly_DisallowOutputtingNetModule() } } + [Fact] + public void EmitMetadata_AllowEmbedding() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll); + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + var result = comp.Emit(output, metadataPEStream: metadataOutput, + options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded)); + + VerifyEmbeddedDebugInfo(output, new[] { DebugDirectoryEntryType.CodeView, DebugDirectoryEntryType.EmbeddedPortablePdb }); + VerifyEmbeddedDebugInfo(metadataOutput, new DebugDirectoryEntryType[] { }); + } + + void VerifyEmbeddedDebugInfo(MemoryStream stream, DebugDirectoryEntryType[] expected) + { + using (var peReader = new PEReader(stream.ToImmutable())) + { + var entries = peReader.ReadDebugDirectory(); + AssertEx.Equal(expected, entries.Select(e => e.Type)); + } + } + } + + [Fact] + public void EmitMetadataOnly_DisallowEmbedding() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll); + + using (var output = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + options: EmitOptions.Default.WithEmitMetadataOnly(true) + .WithDebugInformationFormat(DebugInformationFormat.Embedded))); + } + } + [Fact] public void EmitMetadata() { @@ -412,17 +452,17 @@ public abstract class PublicClass using (var pdbOutput = new MemoryStream()) using (var metadataOutput = new MemoryStream()) { - var result = comp.Emit(output, pdbOutput, metadataPeStream: metadataOutput); + var result = comp.Emit(output, pdbOutput, metadataPEStream: metadataOutput); Assert.True(result.Success); Assert.NotEqual(0, output.Position); Assert.NotEqual(0, pdbOutput.Position); Assert.NotEqual(0, metadataOutput.Position); - MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(output.GetBuffer()), expectEmptyOrThrowNull: false); - MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(metadataOutput.GetBuffer()), expectEmptyOrThrowNull: true); + MetadataReaderUtils.AssertNotThrowNull(ImmutableArray.CreateRange(output.GetBuffer())); + MetadataReaderUtils.AssertEmptyOrThrowNull(ImmutableArray.CreateRange(metadataOutput.GetBuffer())); } var peImage = comp.EmitToArray(); - MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull: false); + MetadataReaderUtils.AssertNotThrowNull(peImage); } /// diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index 0f58cbbc5e97a..e398a4a895d2c 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -134,7 +134,7 @@ public void SymWriterErrors() { var result = compilation.Emit( peStream: peStream, - metadataPeStream: null, + metadataPEStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), @@ -168,7 +168,7 @@ public void SymWriterErrors2() { var result = compilation.Emit( peStream: peStream, - metadataPeStream: null, + metadataPEStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), @@ -202,7 +202,7 @@ public void SymWriterErrors3() { var result = compilation.Emit( peStream: peStream, - metadataPeStream: null, + metadataPEStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 7dd8597b0b3b5..376bf146c519b 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -468,6 +468,15 @@ internal static string EmbeddedTextsRequirePortablePdb { } } + /// + /// Looks up a localized string similar to Embedding PDB is not allowed when emitting metadata.. + /// + internal static string EmbeddingPdbUnexpectedWhenEmittingMetadata { + get { + return ResourceManager.GetString("EmbeddingPdbUnexpectedWhenEmittingMetadata", resourceCulture); + } + } + /// /// Looks up a localized string similar to A key in the pathMap is empty.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 928a36ef445d3..54d4a21da6f75 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -336,6 +336,9 @@ Metadata PE stream should not be given when emitting metadata only. + + Embedding PDB is not allowed when emitting metadata. + Cannot target net module when emitting ref assembly. diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index bd987941fff36..daf5c278c980c 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1987,7 +1987,7 @@ public EmitResult Emit( debugEntryPoint, sourceLinkStream, embeddedTexts, - metadataPeStream: null, + metadataPEStream: null, cancellationToken: cancellationToken); } @@ -1995,7 +1995,7 @@ public EmitResult Emit( /// Emit the IL for the compiled source code into the specified stream. /// /// Stream to which the compilation will be written. - /// Stream to which the metadata-only output will be written. + /// Stream to which the metadata-only output will be written. /// Stream to which the compilation's debug info will be written. Null to forego PDB generation. /// Stream to which the compilation's XML documentation will be written. Null to forego XML generation. /// Stream from which the compilation's Win32 resources will be read (in RES format). @@ -2036,7 +2036,7 @@ public EmitResult Emit( IMethodSymbol debugEntryPoint = null, Stream sourceLinkStream = null, IEnumerable embeddedTexts = null, - Stream metadataPeStream = null, + Stream metadataPEStream = null, CancellationToken cancellationToken = default(CancellationToken)) { if (peStream == null) @@ -2067,16 +2067,22 @@ public EmitResult Emit( } } - if (metadataPeStream != null && options?.EmitMetadataOnly == true) + if (metadataPEStream != null && options?.EmitMetadataOnly == true) { - throw new ArgumentException(CodeAnalysisResources.MetadataPeStreamUnexpectedWhenEmittingMetadataOnly, nameof(metadataPeStream)); + throw new ArgumentException(CodeAnalysisResources.MetadataPeStreamUnexpectedWhenEmittingMetadataOnly, nameof(metadataPEStream)); + } + + if (options?.DebugInformationFormat == DebugInformationFormat.Embedded && + options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.EmbeddingPdbUnexpectedWhenEmittingMetadata, nameof(metadataPEStream)); } if (this.Options.OutputKind == OutputKind.NetModule) { - if (metadataPeStream != null) + if (metadataPEStream != null) { - throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(metadataPeStream)); + throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(metadataPEStream)); } else if (options?.EmitMetadataOnly == true) { @@ -2119,7 +2125,7 @@ public EmitResult Emit( return Emit( peStream, - metadataPeStream, + metadataPEStream, pdbStream, xmlDocumentationStream, win32Resources, @@ -2138,7 +2144,7 @@ public EmitResult Emit( /// internal EmitResult Emit( Stream peStream, - Stream metadataPeStream, + Stream metadataPEStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, @@ -2206,7 +2212,7 @@ internal EmitResult Emit( success = SerializeToPeStream( moduleBeingBuilt, new SimpleEmitStreamProvider(peStream), - (metadataPeStream != null) ? new SimpleEmitStreamProvider(metadataPeStream) : null, + (metadataPEStream != null) ? new SimpleEmitStreamProvider(metadataPEStream) : null, (pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null, testData?.SymWriterFactory, diagnostics, @@ -2373,7 +2379,7 @@ internal CommonPEModuleBuilder CheckOptionsAndCreateModuleBuilder( internal bool SerializeToPeStream( CommonPEModuleBuilder moduleBeingBuilt, EmitStreamProvider peStreamProvider, - EmitStreamProvider metadataPeStreamProvider, + EmitStreamProvider metadataPEStreamProvider, EmitStreamProvider pdbStreamProvider, Func testSymWriterFactory, DiagnosticBag diagnostics, @@ -2487,7 +2493,7 @@ internal bool SerializeToPeStream( }; Func getRefPeStream; - if (metadataPeStreamProvider != null) + if (metadataPEStreamProvider != null) { getRefPeStream = () => { @@ -2496,7 +2502,7 @@ internal bool SerializeToPeStream( return null; } - refPeStream = metadataPeStreamProvider.GetOrCreateStream(metadataDiagnostics); + refPeStream = metadataPEStreamProvider.GetOrCreateStream(metadataDiagnostics); Debug.Assert(refPeStream != null || metadataDiagnostics.HasAnyErrors()); return refPeStream; }; @@ -2508,7 +2514,7 @@ internal bool SerializeToPeStream( try { - if (Cci.PeWriter.WritePeToStream( + if (SerializePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics), this.MessageProvider, getPeStream, @@ -2591,6 +2597,55 @@ internal bool SerializeToPeStream( return true; } + internal static bool SerializePeToStream( + EmitContext context, + CommonMessageProvider messageProvider, + Func getPeStream, + Func getMetadataPeStreamOpt, + Func getPortablePdbStreamOpt, + Cci.PdbWriter nativePdbWriterOpt, + string pdbPathOpt, + bool metadataOnly, + bool isDeterministic, + CancellationToken cancellationToken) + { + if (!Cci.PeWriter.WritePeToStream( + context, + messageProvider, + getPeStream, + getPortablePdbStreamOpt, + nativePdbWriterOpt, + pdbPathOpt, + metadataOnly, + isDeterministic, + cancellationToken)) + { + return false; + } + + // produce the secondary output (ref assembly) if needed + if (getMetadataPeStreamOpt != null) + { + Debug.Assert(!metadataOnly); + + if (!Cci.PeWriter.WritePeToStream( + context, + messageProvider, + getMetadataPeStreamOpt, + getPortablePdbStreamOpt: null, + nativePdbWriterOpt: null, + pdbPathOpt: null, + metadataOnly: true, + isDeterministic: isDeterministic, + cancellationToken: cancellationToken)) + { + return false; + } + } + + return true; + } + internal EmitBaseline SerializeToDeltaStreams( CommonPEModuleBuilder moduleBeingBuilt, EmitBaseline baseline, diff --git a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs index 47e8694c2b927..11cfb2027b637 100644 --- a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs @@ -51,7 +51,7 @@ public static MetadataWriter Create( break; case DebugInformationFormat.Embedded: - debugBuilderOpt = new MetadataBuilder(); + debugBuilderOpt = metadataOnly ? null : new MetadataBuilder(); break; default: diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index d54aee2c74a85..aeeb574e8dc7b 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -70,8 +70,13 @@ internal abstract partial class MetadataWriter // A map of method body before token translation to RVA. Used for deduplication of small bodies. private readonly Dictionary, int> _smallMethodBodies; - private static readonly ImmutableArray ThrowNullIL = ImmutableArray.Create((byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw); - private const int ThrowNullMaxStack = 1; + private const byte TinyFormat = 2; + private const int ThrowNullCodeSize = 2; + private static readonly ImmutableArray ThrowNullEncodedBody = + ImmutableArray.Create( + (byte)((ThrowNullCodeSize << 2) | TinyFormat), + (byte)ILOpCode.Ldnull, + (byte)ILOpCode.Throw); protected MetadataWriter( MetadataBuilder metadata, @@ -1785,7 +1790,9 @@ public void BuildMetadataAndIL( } } - int[] methodBodyOffsets = SerializeMethodBodies(ilBuilder, nativePdbWriterOpt, out mvidStringFixup); + int[] methodBodyOffsets = MetadataOnly ? + SerializeThrowNullMethodBodies(ilBuilder, out mvidStringFixup) : + SerializeMethodBodies(ilBuilder, nativePdbWriterOpt, out mvidStringFixup); _cancellationToken.ThrowIfCancellationRequested(); @@ -2808,6 +2815,35 @@ private void PopulateStandaloneSignatures() } } + private int[] SerializeThrowNullMethodBodies(BlobBuilder ilBuilder, out Blob mvidStringFixup) + { + var methods = this.GetMethodDefs(); + int[] bodyOffsets = new int[methods.Count]; + + int bodyOffsetCache = -1; + int methodRid = 0; + foreach (IMethodDefinition method in methods) + { + if (method.HasBody()) + { + if (bodyOffsetCache == -1) + { + bodyOffsetCache = ilBuilder.Count; + ilBuilder.WriteBytes(ThrowNullEncodedBody); + } + bodyOffsets[methodRid] = bodyOffsetCache; + } + else + { + bodyOffsets[methodRid] = -1; + } + methodRid++; + } + + mvidStringFixup = default(Blob); + return bodyOffsets; + } + private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWriterOpt, out Blob mvidStringFixup) { CustomDebugInfoWriter customDebugInfoWriter = (nativePdbWriterOpt != null) ? new CustomDebugInfoWriter(nativePdbWriterOpt) : null; @@ -2833,30 +2869,21 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr if (method.HasBody()) { - if (MetadataOnly) + body = method.GetBody(Context); + + if (body != null) { - bodyOffset = SerializeThrowNullMethodBody(encoder); - localSignatureHandleOpt = default(StandaloneSignatureHandle); - body = null; + localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body); + + // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation) + bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup); + + nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter); } else { - body = method.GetBody(Context); - - if (body != null) - { - localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body); - - // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation) - bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup); - - nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter); - } - else - { - bodyOffset = 0; - localSignatureHandleOpt = default(StandaloneSignatureHandle); - } + bodyOffset = 0; + localSignatureHandleOpt = default(StandaloneSignatureHandle); } } else @@ -2882,55 +2909,11 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr return bodyOffsets; } - private int SerializeThrowNullMethodBody(MethodBodyStreamEncoder encoder) - { - UserStringHandle mvidStringHandle = default(UserStringHandle); - Blob mvidStringFixup = default(Blob); - - int offset = SerializeMethodBody( - encoder, - ThrowNullIL, - ThrowNullMaxStack, - ImmutableArray.Empty, - default(StandaloneSignatureHandle), - MethodBodyAttributes.None, - ref mvidStringHandle, - ref mvidStringFixup); - - Debug.Assert(mvidStringHandle.Equals(default(UserStringHandle)) && mvidStringFixup.Equals(default(Blob))); - return offset; - } - - private int SerializeMethodBody( - MethodBodyStreamEncoder encoder, - IMethodBody methodBody, - StandaloneSignatureHandle localSignatureHandleOpt, - ref UserStringHandle mvidStringHandle, - ref Blob mvidStringFixup) - { - return SerializeMethodBody( - encoder, - methodBody.IL, - methodBody.MaxStack, - methodBody.ExceptionRegions, - localSignatureHandleOpt, - (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : MethodBodyAttributes.None), - ref mvidStringHandle, - ref mvidStringFixup); - } - - private int SerializeMethodBody( - MethodBodyStreamEncoder encoder, - ImmutableArray il, - ushort maxStack, - ImmutableArray exceptionRegions, - StandaloneSignatureHandle localSignatureHandleOpt, - MethodBodyAttributes methodBodyAttributes, - ref UserStringHandle mvidStringHandle, - ref Blob mvidStringFixup) - { - int ilLength = il.Length; - bool isSmallBody = ilLength < 64 && maxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0; + private int SerializeMethodBody(MethodBodyStreamEncoder encoder, IMethodBody methodBody, StandaloneSignatureHandle localSignatureHandleOpt, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup) + { + int ilLength = methodBody.IL.Length; + var exceptionRegions = methodBody.ExceptionRegions; + bool isSmallBody = ilLength < 64 && methodBody.MaxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0; // Check if an identical method body has already been serialized. // If so, use the RVA of the already serialized one. @@ -2939,27 +2922,27 @@ private int SerializeMethodBody( // Don't do small body method caching during deterministic builds until this issue is fixed // https://github.com/dotnet/roslyn/issues/7595 int bodyOffset; - if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(il, out bodyOffset)) + if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(methodBody.IL, out bodyOffset)) { return bodyOffset; } var encodedBody = encoder.AddMethodBody( - codeSize: ilLength, - maxStack: maxStack, + codeSize: methodBody.IL.Length, + maxStack: methodBody.MaxStack, exceptionRegionCount: exceptionRegions.Length, hasSmallExceptionRegions: MayUseSmallExceptionHeaders(exceptionRegions), localVariablesSignature: localSignatureHandleOpt, - attributes: methodBodyAttributes); + attributes: (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : 0)); // Don't do small body method caching during deterministic builds until this issue is fixed // https://github.com/dotnet/roslyn/issues/7595 if (isSmallBody && !_deterministic) { - _smallMethodBodies.Add(il, encodedBody.Offset); + _smallMethodBodies.Add(methodBody.IL, encodedBody.Offset); } - WriteInstructions(encodedBody.Instructions, il, ref mvidStringHandle, ref mvidStringFixup); + WriteInstructions(encodedBody.Instructions, methodBody.IL, ref mvidStringHandle, ref mvidStringFixup); SerializeMethodBodyExceptionHandlerTable(encodedBody.ExceptionRegions, exceptionRegions); return encodedBody.Offset; diff --git a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs index ab65cf637ea89..82fecdd4e60c6 100644 --- a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs @@ -26,55 +26,6 @@ public PeWritingException(Exception inner) internal static class PeWriter { - internal static bool WritePeToStream( - EmitContext context, - CommonMessageProvider messageProvider, - Func getPeStream, - Func getMetadataPeStreamOpt, - Func getPortablePdbStreamOpt, - PdbWriter nativePdbWriterOpt, - string pdbPathOpt, - bool metadataOnly, - bool isDeterministic, - CancellationToken cancellationToken) - { - if (!WritePeToStream( - context, - messageProvider, - getPeStream, - getPortablePdbStreamOpt, - nativePdbWriterOpt, - pdbPathOpt, - metadataOnly, - isDeterministic, - cancellationToken)) - { - return false; - } - - // produce the secondary output (ref assembly) if needed - if (getMetadataPeStreamOpt != null) - { - Debug.Assert(!metadataOnly); - - if (!WritePeToStream( - context, - messageProvider, - getMetadataPeStreamOpt, - getPortablePdbStreamOpt: null, - nativePdbWriterOpt: null, - pdbPathOpt: null, - metadataOnly: true, - isDeterministic: isDeterministic, - cancellationToken: cancellationToken)) - { - return false; - } - } - - return true; - } - internal static bool WritePeToStream( EmitContext context, CommonMessageProvider messageProvider, diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 0ab3897d2d10c..d3935e7a9c429 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -15,7 +15,7 @@ Microsoft.CodeAnalysis.Compilation.CreateAnonymousTypeSymbol(System.Collections. Microsoft.CodeAnalysis.Compilation.CreateErrorNamespaceSymbol(Microsoft.CodeAnalysis.INamespaceSymbol container, string name) -> Microsoft.CodeAnalysis.INamespaceSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray elementLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray elementTypes, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray elementLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol -Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream = null, System.IO.Stream xmlDocumentationStream = null, System.IO.Stream win32Resources = null, System.Collections.Generic.IEnumerable manifestResources = null, Microsoft.CodeAnalysis.Emit.EmitOptions options = null, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint = null, System.IO.Stream sourceLinkStream = null, System.Collections.Generic.IEnumerable embeddedTexts = null, System.IO.Stream metadataPeStream = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitResult +Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream = null, System.IO.Stream xmlDocumentationStream = null, System.IO.Stream win32Resources = null, System.Collections.Generic.IEnumerable manifestResources = null, Microsoft.CodeAnalysis.Emit.EmitOptions options = null, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint = null, System.IO.Stream sourceLinkStream = null, System.Collections.Generic.IEnumerable embeddedTexts = null, System.IO.Stream metadataPEStream = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream, System.IO.Stream xmlDocumentationStream, System.IO.Stream win32Resources, System.Collections.Generic.IEnumerable manifestResources, Microsoft.CodeAnalysis.Emit.EmitOptions options, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint, System.IO.Stream sourceLinkStream, System.Collections.Generic.IEnumerable embeddedTexts, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream, System.IO.Stream xmlDocumentationStream, System.IO.Stream win32Resources, System.Collections.Generic.IEnumerable manifestResources, Microsoft.CodeAnalysis.Emit.EmitOptions options, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.GetUnreferencedAssemblyIdentities(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> System.Collections.Immutable.ImmutableArray diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index b8488f24edd3b..2e59edc2fae68 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -1413,7 +1413,7 @@ lVbRuntimePlus: .ScriptArguments = scriptArgs.AsImmutableOrEmpty(), .TouchedFilesPath = touchedFilesPath, .OutputLevel = outputLevel, - .EmitPdb = emitPdb AndAlso Not metadataOnly, + .EmitPdb = emitPdb AndAlso Not metadataOnly, ' Silently ignore emitPdb when metadataOnly is set .SourceLink = sourceLink, .DefaultCoreLibraryReference = defaultCoreLibraryReference, .PreferredUILang = preferredUILang, diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 6be940646c748..4070f5e43e51e 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5061,6 +5061,7 @@ /target:winmdobj Create a Windows Metadata intermediate file /doc[+|-] Generates XML documentation file. /doc:<file> Generates XML documentation file to <file>. +/refout:<file> Reference assembly output to generate - INPUT FILES - /addmodule:<file_list> Reference metadata from the specified modules @@ -5105,6 +5106,7 @@ the target .dll or .exe. /deterministic Produce a deterministic assembly (including module version GUID and timestamp) +/refonly Produce a reference assembly in place of the main output /instrument:TestCoverage Produce an assembly instrumented to collect coverage information /sourcelink:<file> Source link info to embed into Portable PDB. diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index 775744fdbbce5..b94de992431c0 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -441,7 +441,7 @@ End Class" "System.Diagnostics.DebuggableAttribute"}) Dim peImage = comp.EmitToArray(emitRefOnly) - MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull:=True) + MetadataReaderUtils.AssertEmptyOrThrowNull(peImage) End Sub @@ -464,7 +464,7 @@ End Class" Using output As New MemoryStream() Using metadataPeOutput As New MemoryStream() - Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, metadataPeStream:=metadataPeOutput, + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, metadataPEStream:=metadataPeOutput, options:=EmitOptions.Default.WithEmitMetadataOnly(True))) End Using End Using @@ -484,19 +484,19 @@ End Class " Using output As New MemoryStream() Using pdbOutput As New MemoryStream() Using metadataOutput As New MemoryStream() - Dim result = comp.Emit(output, pdbOutput, metadataPeStream:=metadataOutput) + Dim result = comp.Emit(output, pdbOutput, metadataPEStream:=metadataOutput) Assert.True(result.Success) Assert.NotEqual(0, output.Position) Assert.NotEqual(0, pdbOutput.Position) Assert.NotEqual(0, metadataOutput.Position) - MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(output.GetBuffer()), expectEmptyOrThrowNull:=False) - MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(metadataOutput.GetBuffer()), expectEmptyOrThrowNull:=True) + MetadataReaderUtils.AssertNotThrowNull(ImmutableArray.CreateRange(output.GetBuffer())) + MetadataReaderUtils.AssertEmptyOrThrowNull(ImmutableArray.CreateRange(metadataOutput.GetBuffer())) End Using End Using End Using Dim peImage = comp.EmitToArray() - MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull:=False) + MetadataReaderUtils.AssertNotThrowNull(peImage) End Sub diff --git a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs index c978fe782407b..04f0259cd29e6 100644 --- a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs +++ b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs @@ -47,7 +47,7 @@ internal static ImmutableArray EmitToArray( var emitResult = compilation.Emit( peStream: peStream, - metadataPeStream: null, + metadataPEStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, diff --git a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs index b767804c589bb..11f8042172152 100644 --- a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs +++ b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs @@ -235,7 +235,7 @@ EmitOptions emitOptions { result = compilation.Emit( executableStream, - metadataPeStream: null, + metadataPEStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, diff --git a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs index 5e1f8db49ddf5..f5d30937337f8 100644 --- a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs +++ b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs @@ -284,59 +284,37 @@ private sealed class ConstantSignatureVisualizer : ISignatureTypeProvider elementType + "[" + new string(',', shape.Rank) + "]"; public string GetByReferenceType(string elementType) - { - return elementType + "&"; - } + => elementType + "&"; public string GetFunctionPointerType(MethodSignature signature) - { - return "method-ptr"; - } + => "method-ptr"; public string GetGenericInstantiation(string genericType, ImmutableArray typeArguments) - { - return genericType + "{" + string.Join(", ", typeArguments) + "}"; - } + => genericType + "{" + string.Join(", ", typeArguments) + "}"; public string GetGenericMethodParameter(object genericContext, int index) - { - return "!!" + index; - } + => "!!" + index; public string GetGenericTypeParameter(object genericContext, int index) - { - return "!" + index; - } + => "!" + index; public string GetModifiedType(string modifier, string unmodifiedType, bool isRequired) - { - return (isRequired ? "modreq" : "modopt") + "(" + modifier + ") " + unmodifiedType; - } + => (isRequired ? "modreq" : "modopt") + "(" + modifier + ") " + unmodifiedType; public string GetPinnedType(string elementType) - { - return "pinned " + elementType; - } + => "pinned " + elementType; public string GetPointerType(string elementType) - { - return elementType + "*"; - } + => elementType + "*"; public string GetPrimitiveType(PrimitiveTypeCode typeCode) - { - return typeCode.ToString(); - } + => typeCode.ToString(); public string GetSZArrayType(string elementType) - { - return elementType + "[]"; - } + => elementType + "[]"; public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { @@ -378,11 +356,7 @@ internal static void VerifyPEMetadata(string pePath, string[] types, string[] me } } - /// - /// Asserts that all the method bodies in the PE are empty or `throw null`, if expectEmptyOrThrowNull is true. - /// Asserts that none of the methods bodies are empty or `throw null`, if expectEmptyOrThrowNull is false. - /// - internal static void VerifyMethodBodies(ImmutableArray peImage, bool expectEmptyOrThrowNull) + internal static void VerifyMethodBodies(ImmutableArray peImage, Action ilValidator) { using (var peReader = new PEReader(peImage)) { @@ -393,19 +367,38 @@ internal static void VerifyMethodBodies(ImmutableArray peImage, bool expec if (rva != 0) { var il = peReader.GetMethodBody(rva).GetILBytes(); - var throwNull = new[] { (byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw }; - - if (expectEmptyOrThrowNull) - { - AssertEx.Equal(throwNull, il); - } - else - { - AssertEx.NotEqual(throwNull, il); - } + ilValidator(il); + } + else + { + ilValidator(null); } } } } + + static readonly byte[] ThrowNull = new[] { (byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw }; + + internal static void AssertEmptyOrThrowNull(ImmutableArray peImage) + { + VerifyMethodBodies(peImage, (il) => + { + if (il != null) + { + AssertEx.Equal(ThrowNull, il); + } + }); + } + + internal static void AssertNotThrowNull(ImmutableArray peImage) + { + VerifyMethodBodies(peImage, (il) => + { + if (il != null) + { + AssertEx.NotEqual(ThrowNull, il); + } + }); + } } } From 9d9de026c519d3414f99d1154beb860569ab8273 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 24 Mar 2017 23:05:58 -0700 Subject: [PATCH 008/284] Add ref output to CoreCompile target and add CopyRefAssembly task (#18058) --- .../Core/MSBuildTask/CopyRefAssembly.cs | 95 +++++++++++++++++++ .../Core/MSBuildTask/ErrorString.Designer.cs | 46 +++++++-- .../Core/MSBuildTask/ErrorString.resx | 10 ++ .../Core/MSBuildTask/MSBuildTask.csproj | 1 + .../Core/MSBuildTask/ManagedCompiler.cs | 7 +- .../MSBuildTask/Microsoft.CSharp.Core.targets | 2 + .../Microsoft.VisualBasic.Core.targets | 2 + src/Compilers/Core/MSBuildTask/project.json | 1 + .../MSBuildTaskTests/MSBuildTaskTests.csproj | 2 +- 9 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs diff --git a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs new file mode 100644 index 0000000000000..cdb32fa7adf59 --- /dev/null +++ b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.CodeAnalysis.BuildTasks +{ + /// + /// If the source and destination ref assemblies are different, + /// this task copies the source over to the destination. + /// + public class CopyRefAssembly : Task + { + [Required] + public string SourcePath { get; set; } + + [Output] + [Required] + public string DestinationPath { get; set; } + + public CopyRefAssembly() + { + TaskResources = ErrorString.ResourceManager; + } + + public override bool Execute() + { + if (!File.Exists(SourcePath)) + { + Log.LogErrorWithCodeFromResources("General_ExpectedFileMissing", SourcePath); + return false; + } + + if (File.Exists(DestinationPath)) + { + Guid source; + try + { + source = ExtractMvid(SourcePath); + } + catch (Exception e) + { + Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_BadSource3", SourcePath, e.Message, e.StackTrace); + } + + try + { + Guid destination = ExtractMvid(DestinationPath); + + if (source.Equals(destination)) + { + Log.LogMessageFromResources(MessageImportance.Low, "CopyRefAssembly_SkippingCopy1", DestinationPath); + return true; + } + } + catch (Exception) + { + Log.LogMessage(MessageImportance.High, "CopyRefAssembly_BadDestination1", DestinationPath); + } + } + + return Copy(); + } + + private bool Copy() + { + try + { + File.Copy(SourcePath, DestinationPath, overwrite: true); + } + catch (Exception e) + { + Log.LogErrorWithCodeFromResources("Compiler_UnexpectedException"); + ManagedCompiler.LogErrorOutput(e.ToString(), Log); + return false; + } + + return true; + } + + private Guid ExtractMvid(string path) + { + using (FileStream source = File.OpenRead(path)) + using (var reader = new PEReader(source)) + { + var metadataReader = reader.GetMetadataReader(); + return metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + } + } + } +} diff --git a/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs b/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs index 26a69b7f2a1c7..560a55decf577 100644 --- a/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs +++ b/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs @@ -70,6 +70,34 @@ internal static string Compiler_UnexpectedException { } } + /// + /// Looks up a localized string similar to Failed to check the content hash of the destination ref assembly '{0}'. It will be overwritten.. + /// + internal static string CopyRefAssembly_BadDestination1 { + get { + return ResourceManager.GetString("CopyRefAssembly_BadDestination1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to check the content hash of the source ref assembly '{0}': {1} + ///{2}. + /// + internal static string CopyRefAssembly_BadSource3 { + get { + return ResourceManager.GetString("CopyRefAssembly_BadSource3", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reference assembly "{0}" already has latest information. Leaving it untouched.. + /// + internal static string CopyRefAssembly_SkippingCopy1 { + get { + return ResourceManager.GetString("CopyRefAssembly_SkippingCopy1", resourceCulture); + } + } + /// /// Looks up a localized string similar to MSB3053: The assembly alias "{1}" on reference "{0}" contains illegal characters.. /// @@ -142,15 +170,6 @@ internal static string General_ExpectedFileMissing { } } - /// - /// Looks up a localized string similar to MSB3082: Task failed because "{0}" was not found.. - /// - internal static string General_ToolFileNotFound { - get { - return ResourceManager.GetString("General_ToolFileNotFound", resourceCulture); - } - } - /// /// Looks up a localized string similar to MSB3087: An incompatible host object was passed into the "{0}" task. The host object for this task must implement the "{1}" interface.. /// @@ -187,6 +206,15 @@ internal static string General_ReferenceDoesNotExist { } } + /// + /// Looks up a localized string similar to MSB3082: Task failed because "{0}" was not found.. + /// + internal static string General_ToolFileNotFound { + get { + return ResourceManager.GetString("General_ToolFileNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shared compilation failed; falling back to tool: {0}. /// diff --git a/src/Compilers/Core/MSBuildTask/ErrorString.resx b/src/Compilers/Core/MSBuildTask/ErrorString.resx index 66b362aa34636..9f6962ded7d75 100644 --- a/src/Compilers/Core/MSBuildTask/ErrorString.resx +++ b/src/Compilers/Core/MSBuildTask/ErrorString.resx @@ -151,6 +151,16 @@ Expected file "{0}" does not exist. + + Reference assembly "{0}" already has latest information. Leaving it untouched. + + + Failed to check the content hash of the source ref assembly '{0}': {1} +{2} + + + Failed to check the content hash of the destination ref assembly '{0}'. It will be overwritten. + MSB3082: Task failed because "{0}" was not found. {StrBegin="MSB3082: "} diff --git a/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj b/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj index d9b7f527e3756..cbced79b1b505 100644 --- a/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj +++ b/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj @@ -55,6 +55,7 @@ + diff --git a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index a72cdd6b1281c..908e82078ee6a 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -591,6 +591,11 @@ private int HandleResponse(BuildResponse response, string pathToTool, string res } private void LogErrorOutput(string output) + { + LogErrorOutput(output, Log); + } + + internal static void LogErrorOutput(string output, TaskLoggingHelper log) { string[] lines = output.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) @@ -598,7 +603,7 @@ private void LogErrorOutput(string output) string trimmedMessage = line.Trim(); if (trimmedMessage != "") { - Log.LogError(trimmedMessage); + log.LogError(trimmedMessage); } } } diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets index 67a2aeb9cbe05..5612c758c3d68 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets @@ -19,6 +19,7 @@ @(EmbeddedFiles)" Outputs="@(DocFileItem); @(IntermediateAssembly); + @(IntermediateRefAssembly); @(_DebugSymbolsIntermediatePath); $(NonExistentFile); @(CustomAdditionalCompileOutputs)" @@ -110,6 +111,7 @@ Deterministic="$(Deterministic)" PublicSign="$(PublicSign)" OutputAssembly="@(IntermediateAssembly)" + OutputRefAssembly="@(IntermediateRefAssembly)" PdbFile="$(PdbFile)" Platform="$(PlatformTarget)" Prefer32Bit="$(Prefer32Bit)" diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets index 4ead670434c75..eef272a4b8c59 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets @@ -19,6 +19,7 @@ @(EmbeddedFiles)" Outputs="@(DocFileItem); @(IntermediateAssembly); + @(IntermediateRefAssembly); @(_DebugSymbolsIntermediatePath); $(NonExistentFile); @(CustomAdditionalCompileOutputs)" @@ -102,6 +103,7 @@ OptionStrict="$(OptionStrict)" OptionStrictType="$(OptionStrictType)" OutputAssembly="@(IntermediateAssembly)" + OutputRefAssembly="@(IntermediateRefAssembly)" PdbFile="$(PdbFile)" Platform="$(PlatformTarget)" Prefer32Bit="$(Prefer32Bit)" diff --git a/src/Compilers/Core/MSBuildTask/project.json b/src/Compilers/Core/MSBuildTask/project.json index ac0c51a5b3082..c9efa1ac00175 100644 --- a/src/Compilers/Core/MSBuildTask/project.json +++ b/src/Compilers/Core/MSBuildTask/project.json @@ -15,6 +15,7 @@ "System.IO.Pipes": "4.3.0", "System.Linq": "4.3.0", "System.Reflection": "4.3.0", + "System.Reflection.Metadata": "1.4.2", "System.Security.AccessControl": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Principal.Windows": "4.3.0", diff --git a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj index 47f5b10b77665..73577163a49fe 100644 --- a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj +++ b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj @@ -77,4 +77,4 @@ - + \ No newline at end of file From b3243e8691ec2b97dc0b17f5fef76ad2626aa7a1 Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Fri, 31 Mar 2017 09:57:44 -0700 Subject: [PATCH 009/284] enable main methods to be asynchronous (#18157) * enable main methods to be asynchronous * add cref to documentation * fix doc comment syntax * remove message from resx * move comments * add async int test * add more test * re-add errors for pre-71 compilers * add tests for previous versions of csharp * add feature detection * Disallow ref returning Main method The Main method cannot have a ref return. closes #17923 * add ref tests, add prototype comment * change prototype comment --- .../Portable/CSharpResources.Designer.cs | 9 + .../CSharp/Portable/CSharpResources.resx | 3 + .../Portable/Compilation/CSharpCompilation.cs | 66 +-- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../CSharp/Portable/Errors/MessageID.cs | 5 + .../CSharp/Portable/Symbols/MethodSymbol.cs | 84 ++-- .../CSharp/Test/Emit/Emit/EntryPointTests.cs | 40 ++ .../Semantic/Semantics/BindingAsyncTests.cs | 389 ++++++++++++++++-- 8 files changed, 505 insertions(+), 93 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index b2d5452c58a30..474abc016d6cf 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -9872,6 +9872,15 @@ internal static string IDS_FeatureAsync { } } + /// + /// Looks up a localized string similar to async main. + /// + internal static string IDS_FeatureAsyncMain { + get { + return ResourceManager.GetString("IDS_FeatureAsyncMain", resourceCulture); + } + } + /// /// Looks up a localized string similar to automatically implemented properties. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index b97e92c32b452..c5f5ed764a3c4 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5041,4 +5041,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A value of type 'void' may not be assigned. + + async main + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index a163e9ecc6b49..dc84d30bf09d2 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Symbols; +using static Microsoft.CodeAnalysis.CSharp.Binder; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -40,7 +41,7 @@ public sealed partial class CSharpCompilation : Compilation // version. Do not make any changes to the public interface without making the corresponding // change to the VB version. // - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! internal static readonly ParallelOptions DefaultParallelOptions = new ParallelOptions(); @@ -93,9 +94,9 @@ internal Conversions Conversions /// /// Holds onto data related to reference binding. /// The manager is shared among multiple compilations that we expect to have the same result of reference binding. - /// In most cases this can be determined without performing the binding. If the compilation however contains a circular + /// In most cases this can be determined without performing the binding. If the compilation however contains a circular /// metadata reference (a metadata reference that refers back to the compilation) we need to avoid sharing of the binding results. - /// We do so by creating a new reference manager for such compilation. + /// We do so by creating a new reference manager for such compilation. /// private ReferenceManager _referenceManager; @@ -128,7 +129,7 @@ public override bool IsCaseSensitive } /// - /// The options the compilation was created with. + /// The options the compilation was created with. /// public new CSharpCompilationOptions Options { @@ -172,14 +173,14 @@ public LanguageVersion LanguageVersion protected override INamedTypeSymbol CommonCreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity) { return new ExtendedErrorTypeSymbol( - container.EnsureCSharpSymbolOrNull(nameof(container)), + container.EnsureCSharpSymbolOrNull(nameof(container)), name, arity, errorInfo: null); } protected override INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceSymbol container, string name) { return new MissingNamespaceSymbol( - container.EnsureCSharpSymbolOrNull(nameof(container)), + container.EnsureCSharpSymbolOrNull(nameof(container)), name); } @@ -408,8 +409,8 @@ private CSharpCompilation Update( /// public new CSharpCompilation WithAssemblyName(string assemblyName) { - // Can't reuse references since the source assembly name changed and the referenced symbols might - // have internals-visible-to relationship with this compilation or they might had a circular reference + // Can't reuse references since the source assembly name changed and the referenced symbols might + // have internals-visible-to relationship with this compilation or they might had a circular reference // to this compilation. return new CSharpCompilation( @@ -429,9 +430,9 @@ private CSharpCompilation Update( /// Creates a new compilation with the specified references. /// /// - /// The new will query the given for the underlying - /// metadata as soon as the are needed. - /// + /// The new will query the given for the underlying + /// metadata as soon as the are needed. + /// /// The new compilation uses whatever metadata is currently being provided by the . /// E.g. if the current compilation references a metadata file that has changed since the creation of the compilation /// the new compilation is going to use the updated version, while the current compilation will be using the previous (it doesn't change). @@ -684,7 +685,7 @@ internal override bool HasSubmissionResult() /// /// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees - /// added later. + /// added later. /// public new CSharpCompilation RemoveSyntaxTrees(params SyntaxTree[] trees) { @@ -693,7 +694,7 @@ internal override bool HasSubmissionResult() /// /// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees - /// added later. + /// added later. /// public new CSharpCompilation RemoveSyntaxTrees(IEnumerable trees) { @@ -746,7 +747,7 @@ internal override bool HasSubmissionResult() /// /// Creates a new compilation without any syntax trees. Preserves metadata info - /// from this compilation for use with trees added later. + /// from this compilation for use with trees added later. /// public new CSharpCompilation RemoveAllSyntaxTrees() { @@ -805,7 +806,7 @@ internal override bool HasSubmissionResult() } // TODO(tomat): Consider comparing #r's of the old and the new tree. If they are exactly the same we could still reuse. - // This could be a perf win when editing a script file in the IDE. The services create a new compilation every keystroke + // This could be a perf win when editing a script file in the IDE. The services create a new compilation every keystroke // that replaces the tree with a new one. var reuseReferenceManager = !oldTree.HasReferenceOrLoadDirectives() && !newTree.HasReferenceOrLoadDirectives(); syntaxAndDeclarations = syntaxAndDeclarations.ReplaceSyntaxTree(oldTree, newTree); @@ -872,7 +873,7 @@ internal IEnumerable ExternAliases /// /// or corresponding to the given reference or null if there is none. /// - /// Uses object identity when comparing two references. + /// Uses object identity when comparing two references. /// internal new Symbol GetAssemblyOrModuleSymbol(MetadataReference reference) { @@ -976,7 +977,7 @@ public MetadataReference GetDirectiveReference(ReferenceDirectiveTriviaSyntax di /// /// Get all modules in this compilation, including the source module, added modules, and all /// modules of referenced assemblies that do not come from an assembly with an extern alias. - /// Metadata imported from aliased assemblies is not visible at the source level except through + /// Metadata imported from aliased assemblies is not visible at the source level except through /// the use of an extern alias directive. So exclude them from this list which is used to construct /// the global namespace. /// @@ -1017,7 +1018,7 @@ internal void GetUnaliasedReferencedAssemblies(ArrayBuilder asse } /// - /// Gets the that corresponds to the assembly symbol. + /// Gets the that corresponds to the assembly symbol. /// public new MetadataReference GetMetadataReference(IAssemblySymbol assemblySymbol) { @@ -1065,7 +1066,7 @@ internal SourceAssemblySymbol SourceAssembly } /// - /// Gets the root namespace that contains all namespaces and types defined in source code or in + /// Gets the root namespace that contains all namespaces and types defined in source code or in /// referenced metadata, merged into a single namespace hierarchy. /// internal new NamespaceSymbol GlobalNamespace @@ -1075,7 +1076,7 @@ internal SourceAssemblySymbol SourceAssembly if ((object)_lazyGlobalNamespace == null) { // Get the root namespace from each module, and merge them all together - // Get all modules in this compilation, ones referenced directly by the compilation + // Get all modules in this compilation, ones referenced directly by the compilation // as well as those referenced by all referenced assemblies. var modules = ArrayBuilder.GetInstance(); @@ -1369,7 +1370,7 @@ internal bool DeclaresTheObjectClass internal new MethodSymbol GetEntryPoint(CancellationToken cancellationToken) { EntryPoint entryPoint = GetEntryPointAndDiagnostics(cancellationToken); - return entryPoint == null ? null : entryPoint.MethodSymbol; + return entryPoint?.MethodSymbol; } internal EntryPoint GetEntryPointAndDiagnostics(CancellationToken cancellationToken) @@ -1456,9 +1457,9 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm var viableEntryPoints = ArrayBuilder.GetInstance(); foreach (var candidate in entryPointCandidates) { - if (!candidate.HasEntryPointSignature()) + if (!candidate.HasEntryPointSignature(this)) { - // a single error for partial methods: + // a single error for partial methods warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); continue; } @@ -1472,7 +1473,8 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm if (candidate.IsAsync) { - diagnostics.Add(ErrorCode.ERR_MainCantBeAsync, candidate.Locations.First(), candidate); + // PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. + CheckFeatureAvailability(candidate.DeclaringSyntaxReferences.Single().GetSyntax(), MessageID.IDS_FeatureAsyncMain, diagnostics); } viableEntryPoints.Add(candidate); @@ -2171,8 +2173,8 @@ internal ImmutableArray GetDiagnosticsForSyntaxTree( { //remove some errors that don't have locations in the tree, like "no suitable main method." //Members in trees other than the one being examined are not compiled. This includes field - //initializers which can result in 'field is never initialized' warnings for fields in partial - //types when the field is in a different source file than the one for which we're getting diagnostics. + //initializers which can result in 'field is never initialized' warnings for fields in partial + //types when the field is in a different source file than the one for which we're getting diagnostics. //For that reason the bag must be also filtered by tree. IEnumerable methodBodyDiagnostics = GetDiagnosticsForMethodBodiesInTree(syntaxTree, filterSpanWithinTree, cancellationToken); @@ -2730,14 +2732,14 @@ protected override INamedTypeSymbol CommonCreateTupleTypeSymbol( return TupleTypeSymbol.Create( locationOpt: null, // no location for the type declaration elementTypes: typesBuilder.ToImmutableAndFree(), - elementLocations: elementLocations, - elementNames: elementNames, + elementLocations: elementLocations, + elementNames: elementNames, compilation: this, shouldCheckConstraints: false); } protected override INamedTypeSymbol CommonCreateTupleTypeSymbol( - INamedTypeSymbol underlyingType, + INamedTypeSymbol underlyingType, ImmutableArray elementNames, ImmutableArray elementLocations) { @@ -2757,7 +2759,7 @@ protected override INamedTypeSymbol CommonCreateTupleTypeSymbol( } protected override INamedTypeSymbol CommonCreateAnonymousTypeSymbol( - ImmutableArray memberTypes, + ImmutableArray memberTypes, ImmutableArray memberNames, ImmutableArray memberLocations, ImmutableArray memberIsReadOnly) @@ -2855,7 +2857,7 @@ internal override int CompareSourceLocations(Location loc1, Location loc2) #endregion /// - /// Returns if the compilation has all of the members necessary to emit metadata about + /// Returns if the compilation has all of the members necessary to emit metadata about /// dynamic types. /// /// @@ -2911,7 +2913,7 @@ internal bool EnableEnumArrayBlockInitialization return sustainedLowLatency != null && sustainedLowLatency.ContainingAssembly == Assembly.CorLibrary; } } - + internal override bool IsIOperationFeatureEnabled() { var options = (CSharpParseOptions)this.SyntaxTrees.FirstOrDefault()?.Options; diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 5814b2db8a06b..83890349f82bd 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1104,7 +1104,7 @@ internal enum ErrorCode ERR_VarargsAsync = 4006, ERR_ByRefTypeAndAwait = 4007, ERR_BadAwaitArgVoidCall = 4008, - ERR_MainCantBeAsync = 4009, + // ERR_MainCantBeAsync = 4009, ERR_CantConvAsyncAnonFuncReturns = 4010, ERR_BadAwaiterPattern = 4011, ERR_BadSpecialByRefLocal = 4012, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index db433a2c5b01e..2f736750f6f49 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -127,6 +127,7 @@ internal enum MessageID IDS_FeatureExpressionBodiedAccessor = MessageBase + 12715, IDS_FeatureExpressionBodiedDeOrConstructor = MessageBase + 12716, IDS_ThrowExpression = MessageBase + 12717, + IDS_FeatureAsyncMain = MessageBase + 12718, } // Message IDs may refer to strings that need to be localized. @@ -183,6 +184,10 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // Checks are in the LanguageParser unless otherwise noted. switch (feature) { + // C# 7.1 features. + case MessageID.IDS_FeatureAsyncMain: + return LanguageVersion.CSharp7_1; + // C# 7 features. case MessageID.IDS_FeatureBinaryLiteral: case MessageID.IDS_FeatureDigitSeparator: diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index a308c50a9554c..1a4322b09f4af 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -84,7 +84,7 @@ public virtual bool IsGenericMethod internal virtual bool IsDirectlyExcludedFromCodeCoverage { get => false; } /// - /// Returns true if this method is an extension method. + /// Returns true if this method is an extension method. /// public abstract bool IsExtensionMethod { get; } @@ -114,7 +114,7 @@ public virtual bool IsGenericMethod internal abstract IEnumerable GetSecurityInformation(); /// - /// Marshalling information for return value (FieldMarshal in metadata). + /// Marshalling information for return value (FieldMarshal in metadata). /// internal abstract MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation { get; } @@ -133,7 +133,7 @@ public virtual bool IsGenericMethod /// /// Returns true if this method hides base methods by name. This cannot be specified directly /// in the C# language, but can be true for methods defined in other languages imported from - /// metadata. The equivalent of the "hidebyname" flag in metadata. + /// metadata. The equivalent of the "hidebyname" flag in metadata. /// public abstract bool HidesBaseMethodsByName { get; } @@ -184,7 +184,7 @@ public virtual bool IsCheckedBuiltin public abstract TypeSymbol ReturnType { get; } /// - /// Returns the type arguments that have been substituted for the type parameters. + /// Returns the type arguments that have been substituted for the type parameters. /// If nothing has been substituted for a given type parameter, /// then the type parameter itself is consider the type argument. /// @@ -275,13 +275,13 @@ internal virtual bool IsExplicitInterfaceImplementation /// Returns interface methods explicitly implemented by this method. /// /// - /// Methods imported from metadata can explicitly implement more than one method, + /// Methods imported from metadata can explicitly implement more than one method, /// that is why return type is ImmutableArray. /// public abstract ImmutableArray ExplicitInterfaceImplementations { get; } /// - /// Returns the list of custom modifiers, if any, associated with the return type. + /// Returns the list of custom modifiers, if any, associated with the return type. /// public abstract ImmutableArray ReturnTypeCustomModifiers { get; } @@ -309,7 +309,7 @@ public virtual ImmutableArray GetReturnTypeAttributes() /// returns the property that this method is the getter or setter for. /// If this method has MethodKind of MethodKind.EventAdd or MethodKind.EventRemove, /// returns the event that this method is the adder or remover for. - /// Note, the set of possible associated symbols might be expanded in the future to + /// Note, the set of possible associated symbols might be expanded in the future to /// reflect changes in the languages. /// public abstract Symbol AssociatedSymbol { get; } @@ -327,19 +327,19 @@ internal MethodSymbol GetLeastOverriddenMethod(NamedTypeSymbol accessingTypeOpt) while (m.IsOverride && !m.HidesBaseMethodsByName) { // We might not be able to access the overridden method. For example, - // + // // .assembly A // { // InternalsVisibleTo("B") // public class A { internal virtual void M() { } } // } - // + // // .assembly B // { // InternalsVisibleTo("C") // public class B : A { internal override void M() { } } // } - // + // // .assembly C // { // public class C : B { ... new B().M ... } // A.M is not accessible from here @@ -374,9 +374,9 @@ internal MethodSymbol GetConstructedLeastOverriddenMethod(NamedTypeSymbol access /// /// If this method overrides another method (because it both had the override modifier /// and there correctly was a method to override), returns the overridden method. - /// Note that if an overriding method D.M overrides C.M, which in turn overrides + /// Note that if an overriding method D.M overrides C.M, which in turn overrides /// virtual method A.M, the "overridden method" of D.M is C.M, not the original virtual - /// method A.M. Note also that constructed generic methods are not considered to + /// method A.M. Note also that constructed generic methods are not considered to /// override anything. /// public MethodSymbol OverriddenMethod @@ -587,7 +587,7 @@ internal bool IsSubmissionInitializer } /// - /// Determines whether this method is a candidate for a default assembly entry point + /// Determines whether this method is a candidate for a default assembly entry point /// (i.e. it is a static method called "Main"). /// internal bool IsEntryPointCandidate @@ -597,18 +597,56 @@ internal bool IsEntryPointCandidate /// /// Checks if the method has an entry point compatible signature, i.e. - /// - the return type is either void or int + /// - the return type is either void, int, , + /// or where T is an int. /// - has either no parameter or a single parameter of type string[] /// - internal bool HasEntryPointSignature() + internal bool HasEntryPointSignature(CSharpCompilation compilation) { - if (this.IsVararg) + + bool IsAsyncMainReturnType(TypeSymbol type) + { + var namedType = type as NamedTypeSymbol; + if (namedType == null) + { + return false; + } + else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)) + { + // Change this to `namedType.IsNonGenericTaskType` if you want to support "task-like" objects. + return true; + } + else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) + { + // Change this to `namedType.IsGenericTaskType` if you want to support "task-like" objects. + return namedType.TypeArguments[0].SpecialType == SpecialType.System_Int32; + } + else + { + return false; + } + } + + if (IsVararg) { return false; } TypeSymbol returnType = ReturnType; - if (returnType.SpecialType != SpecialType.System_Int32 && returnType.SpecialType != SpecialType.System_Void) + bool isAsyncMainReturnType = IsAsyncMainReturnType(returnType); + if (returnType.SpecialType != SpecialType.System_Int32 && returnType.SpecialType != SpecialType.System_Void && !isAsyncMainReturnType) + { + return false; + } + + // Prior to 7.1, async methods were considered to "have the entrypoint signature". + // In order to keep back-compat, we need to let these through if compiling using a previous language version. + if (!isAsyncMainReturnType && IsAsync && compilation.LanguageVersion >= LanguageVersion.CSharp7_1) + { + return false; + } + + if (RefKind != RefKind.None) { return false; } @@ -733,7 +771,7 @@ public virtual TypeSymbol ReceiverType } /// - /// If this method is a reduced extension method, returns a type inferred during reduction process for the type parameter. + /// If this method is a reduced extension method, returns a type inferred during reduction process for the type parameter. /// /// Type parameter of the corresponding method. /// Inferred type or Nothing if nothing was inferred. @@ -883,7 +921,7 @@ internal bool CalculateUseSiteDiagnostic(ref DiagnosticInfo result) return true; } - // If the member is in an assembly with unified references, + // If the member is in an assembly with unified references, // we check if its definition depends on a type from a unified reference. if (this.ContainingModule.HasUnifiedReferences) { @@ -903,7 +941,7 @@ internal bool CalculateUseSiteDiagnostic(ref DiagnosticInfo result) } /// - /// Return error code that has highest priority while calculating use site error for this symbol. + /// Return error code that has highest priority while calculating use site error for this symbol. /// protected override int HighestPriorityUseSiteError { @@ -944,7 +982,7 @@ internal virtual TypeSymbol IteratorElementType /// /// Generates bound block representing method's body for methods in lowered form and adds it to - /// a collection of method bodies of the current module. This method is supposed to only be + /// a collection of method bodies of the current module. This method is supposed to only be /// called for method symbols which return SynthesizesLoweredBoundBody == true. /// internal virtual void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) @@ -975,7 +1013,7 @@ internal virtual bool SynthesizesLoweredBoundBody /// /// Syntax offset is a unique identifier for the local within the emitted method body. /// It's based on position of the local declarator. In single-part method bodies it's simply the distance - /// from the start of the method body syntax span. If a method body has multiple parts (such as a constructor + /// from the start of the method body syntax span. If a method body has multiple parts (such as a constructor /// comprising of code for member initializers and constructor initializer calls) the offset is calculated /// as if all source these parts were concatenated together and prepended to the constructor body. /// The resulting syntax offset is then negative for locals defined outside of the constructor body. @@ -1227,7 +1265,7 @@ public virtual bool IsTupleMethod /// /// If this is a method of a tuple type, return corresponding underlying method from the - /// tuple underlying type. Otherwise, null. + /// tuple underlying type. Otherwise, null. /// public virtual MethodSymbol TupleUnderlyingMethod { diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs index d9f218f2d9ef7..27a16b65d939e 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs @@ -1444,5 +1444,45 @@ public static void Main(string[] args) {} Diagnostic(ErrorCode.ERR_MultipleEntryPoints, "Main").WithLocation(4, 24) ); } + + [WorkItem(17923, "https://github.com/dotnet/roslyn/issues/17923")] + [Fact] + [CompilerTrait(CompilerFeature.RefLocalsReturns)] + public void RefIntReturnMainEmpty() + { + var source = @" +class Program +{ + public static ref int Main() { throw new System.Exception(); } +}"; + + var compilation = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + // (4,27): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // public static ref int Main() {} + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(4, 27), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [WorkItem(17923, "https://github.com/dotnet/roslyn/issues/17923")] + [Fact] + [CompilerTrait(CompilerFeature.RefLocalsReturns)] + public void RefIntReturnMainWithParams() + { + var source = @" +class Program +{ + public static ref int Main(string[] args) { throw new System.Exception(); } +}"; + + var compilation = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + // (4,27): warning CS0028: 'Program.Main(string[])' has the wrong signature to be an entry point + // public static ref int Main(string[] args) { throw new System.Exception(); } + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main(string[])"), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index 5fd8b1578b9a7..692b282000480 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; @@ -88,7 +89,7 @@ public void BadAsyncConstructor() { var source = @" class C { - async public C() { } + async public C() { } }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics( Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("async")); @@ -215,7 +216,7 @@ public void TaskRetNoObjectRequired() class C { static void InferTask(Func x) { } - + static void InferTaskOrTaskT(Func x) { } static void InferTaskOrTaskT(Func> x) { } @@ -939,7 +940,7 @@ public void BadAwaitInCatchFilter() class Test { - async static Task M1() + async static Task M1() { try { @@ -1067,7 +1068,7 @@ public static async void F() object o = new object(); lock(await Task.Factory.StartNew(() => o)) { - + } } @@ -1188,7 +1189,7 @@ public void AsyncExplicitInterfaceImplementation() interface IInterface { - void F(); + void F(); } class C : IInterface @@ -1200,7 +1201,7 @@ async void IInterface.F() static void Main() { - + } }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics(); @@ -1212,16 +1213,109 @@ public void AsyncInterfaceMember() var source = @" interface IInterface { - async void F(); + async void F(); }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics( // (4,16): error CS0106: The modifier 'async' is not valid for this item - // async void F(); + // async void F(); Diagnostic(ErrorCode.ERR_BadMemberFlag, "F").WithArguments("async")); } [Fact] - public void MainCantBeAsync() + public void MainCanBeAsyncWithArgs() + { + var origSource = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + } +}"; + var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "return") }; + foreach (var source in sources) + { + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); + } + } + + [Fact] + public void MainCantBeAsyncWithRefTask() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static ref Task Main(string[] args) + { + throw new System.Exception(); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics( + // (6,21): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point + // static ref Task Main(string[] args) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(6, 21), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void MainCantBeAsyncWithArgs_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main(string[] args) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + }").WithArguments("async main", "7.1").WithLocation(6, 5)); + } + + [Fact] + public void MainCanBeAsync() + { + var origSource = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + await Task.Factory.StartNew(() => { }); + } +}"; + var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "return") }; + foreach (var source in sources) + { + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); + } + } + + [Fact] + public void MainCantBeAsyncVoid() { var source = @" using System.Threading.Tasks; @@ -1233,10 +1327,211 @@ async static void Main() await Task.Factory.StartNew(() => { }); } }"; - CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( - // (4,23): error CS4009: 'A.Main()': an entry point cannot be marked with the 'async' modifier + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics( + // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // async static void Main() + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.Null(entry); + } + + [Fact] + public void MainCantBeAsyncVoid_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main() + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static void Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static void Main() + { + await Task.Factory.StartNew(() => { }); + }").WithArguments("async main", "7.1").WithLocation(6, 5)); + } + + [Fact] + public void MainCantBeAsyncInt() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static int Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + 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 + // async static int Main() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), + // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point // async static void Main() - Diagnostic(ErrorCode.ERR_MainCantBeAsync, "Main").WithArguments("A.Main()")); + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.Null(entry); + } + + [Fact] + public void MainCantBeAsyncInt_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static int Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,22): error CS1983: The return type of an async method must be void, Task or Task + // async static int Main() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main"), + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static int Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static int Main() + { + return await Task.Factory.StartNew(() => 5); + }").WithArguments("async main", "7.1").WithLocation(6, 5) +); + } + + [Fact] + public void MainCanBeAsyncAndGenericOnIntWithArgs() + { + var origSource = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "") }; + foreach (var source in sources) + { + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); + } + } + + [Fact] + public void MainCantBeAsyncAndGenericOnIntWithArgs_Csharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( +// (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main(string[] args) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) + { + return await Task.Factory.StartNew(() => 5); + }").WithArguments("async main", "7.1").WithLocation(6, 5) +); + } + + [Fact] + public void MainCanBeAsyncAndGenericOnInt() + { + var origSource = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "") }; + foreach (var source in sources) + { + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); + } + } + + [Fact] + public void MainCantBeAsyncAndGenericOnInt_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() + { + return await Task.Factory.StartNew(() => 5); + }").WithArguments("async main", "7.1").WithLocation(6, 5) + ); + } + + [Fact] + public void MainCantBeAsyncAndGenericOverFloats() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + await Task.Factory.StartNew(() => { }); + return 0; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics( + // (6,30): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // async static Task Main() + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 30), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1) ); } [Fact] @@ -1252,10 +1547,10 @@ async static void Main() await Task.Factory.StartNew(() => { }); } }"; - CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( // (4,23): warning CS0402: 'A.Main()': an entry point cannot be generic or in a generic type // async static void Main() - Diagnostic(ErrorCode.WRN_MainCantBeGeneric, "Main").WithArguments("A.Main()"), + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()"), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint)); } @@ -1829,7 +2124,7 @@ static Task Foo(T t) { return Task.Run(async () => { return t; }); } - + static int Main() { return 0; @@ -1865,7 +2160,7 @@ class Test static async Task Meth1() { throw new EntryPointNotFoundException(); - Foo(); + Foo(); return """"; } @@ -1891,10 +2186,10 @@ static int Main() // static async Task Meth1() Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth1"), // (23,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. - // Foo(); + // Foo(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Foo()"), // (23,9): warning CS0162: Unreachable code detected - // Foo(); + // Foo(); Diagnostic(ErrorCode.WRN_UnreachableCode, "Foo"), // (27,33): 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. // static async Task Meth2() @@ -2908,7 +3203,7 @@ public void Meth() Foo(); }); }; - }); + }); } static int Main() @@ -2950,7 +3245,7 @@ public void Meth() return """"; }; del3(); - + }; } @@ -2981,7 +3276,7 @@ public static Task ExMeth(this int i) return (Task) Foo(); } } -class Test +class Test { public static int amount=0; static int Main() @@ -3066,7 +3361,7 @@ public Task Foo2() return Task.Run(() => { }); } } -class Test +class Test { static int Main() { @@ -3097,7 +3392,7 @@ public async Task Foo() { await Task.Delay(10); } - public void Dispose() + public void Dispose() { Foo(); } @@ -3486,19 +3781,39 @@ public static int Main() public void Repro_17885() { var source = @" +using System.Threading.Tasks; class Test { - async public static void Main() + async public static Task Main() { } }"; - CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( - // (4,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 void Main() + CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( + // (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")); + } + + [Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")] + public void Repro_17885_CSharp7() + { + var source = @" +using System.Threading.Tasks; +class Test +{ + async public static Task Main() + { + } +}"; + CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( + // (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"), - // (4,30): error CS4009: 'Test.Main()': an entry point cannot be marked with the 'async' modifier - // async public static void Main() - Diagnostic(ErrorCode.ERR_MainCantBeAsync, "Main").WithArguments("Test.Main()")); + // (5,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async public static Task Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async public static Task Main() + { + }").WithArguments("async main", "7.1").WithLocation(5, 5)); } [Fact, WorkItem(547088, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547088")] @@ -3511,7 +3826,7 @@ static int Main() { return 1; } - + public async void Foo(ref int x) { } }"; @@ -3582,7 +3897,7 @@ public void GetAwaiterIsExtension() var source = @"using System; using A; - + namespace A { public class IAS @@ -3717,7 +4032,7 @@ public void ReturnExpressionNotConvertible() { string source = @" using System.Threading.Tasks; - + class Program { static async Task Foo() @@ -3740,9 +4055,9 @@ public void RefParameterOnAsyncLambda() string source = @" using System; using System.Threading.Tasks; - + delegate Task D(ref int x); - + class C { static void Main() @@ -3752,7 +4067,7 @@ static void Main() await Task.Delay(500); Console.WriteLine(i++); }; - + int x = 5; d(ref x).Wait(); Console.WriteLine(x); @@ -3830,7 +4145,7 @@ public void DelegateTypeWithNoInvokeMethod() @".class public auto ansi sealed D`1 extends [mscorlib]System.MulticastDelegate { - .method public hidebysig specialname rtspecialname + .method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed { From c371dc16270dec9e94fec20079eae2740a1344a2 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 7 Apr 2017 13:21:16 -0700 Subject: [PATCH 010/284] Document refout feature (#18501) --- docs/features/refout.md | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/features/refout.md diff --git a/docs/features/refout.md b/docs/features/refout.md new file mode 100644 index 0000000000000..9e533c52c72f7 --- /dev/null +++ b/docs/features/refout.md @@ -0,0 +1,78 @@ +# Reference assemblies + +Reference assemblies are metadata-only assemblies with the minimum amount of metadata to preserve the compile-time behavior of consumers (diagnostics may be affected, though). + +## Scenarios +There are 4 scenarios: + +1. The traditional one, where an assembly is emitted as the primary output (`/out` command-line parameter, or `peStream` parameter in `Compilation.Emit` APIs). +2. The IDE scenario, where the metadata-only assembly is emitted (via `Emit` API), still as the primary output. Later on, the IDE is interested to get metadata-only assemblies even when there are errors in the compilation. +3. The CoreFX scenario, where only the ref assembly is emitted, still as the primary output (`/refonly` command-line parameter) +4. The MSBuild scenario, which is the new scenario, where both a real assembly is emitted as the primary output, and a ref assembly is emitted as the secondary output (`/refout` command-line parameter, or `metadataPeStream` parameter in `Emit`). + + +## Definition of ref assemblies +Metadata-only assembly have their method bodies replaced with a single `throw null` body, but include all members except anonymous types. The reason for using `throw null` bodies (as opposed to no bodies) is so that PEVerify could run and pass (thus validating the completeness of the metadata). +Ref assemblies further remove metadata (private members) from metadata-only assemblies. + +A reference assembly will only have references for what it needs in the API surface. The real assembly may have additional references related to specific implementations. For instance, the reference assembly for `class C { private void M() { dynamic .... } }` will not have any references for `dynamic` types. + +Private function-members (methods, properties and events) will be removed. If there are no `InternalsVisibleTo` attributes, do the same for internal function-members + +All types (including private or nested types) must be kept in reference assemblies. + +All fields of a struct will be kept (in C# 7.1 timeframe), but this can later be refined. There are three cases to consider (ref case, struct case, generic case), where we could possibly substitute the fields with adequate placeholders that would minimize the rate of change of the ref assembly. + +Reference assemblies will include an assembly-level `ReferenceAssembly` attribute. If such an attribute is found in source, we won't need to synthesize it. Because of this attribute, runtimes will refuse to load reference assemblies. + +## API changes + +### Command-line +Two mutually exclusive command-line parameters will be added to `csc.exe` and `vbc.exe`: +- `/refout` +- `/refonly` + +The `/refout` parameter specifies a file path where the ref assembly should be output. This translates to `metadataPeStream` in the `Emit` API (see details below). + +The `/refonly` parameter is a flag that indicates that a ref assembly should be output instead of an implementation assembly. +The `/refonly` parameter is not allowed together with the `/refout` parameter, as it doesn't make sense to have both the primary and secondary outputs be ref assemblies. Also, the `/refonly` parameter silently disables outputting PDBs, as ref assemblies cannot be executed. +The `/refonly` parameter translates to `EmitMetadataOnly` being `true`, and `IncludePrivateMembers` being `false` in the `Emit` API (see details below). + +When the compiler produces documentation, the contents produced will match the APIs that go into the primary output. In other words, the documentation will be filtered down when using the `/refonly` parameter. + +The compilation from the command-line will either produce both assemblies (implementation and ref) or neither. There is no "partial success" scenario. + +### CscTask/CoreCompile +The `CoreCompile` target will support a new output, called `IntermediateRefAssembly`, which parallels the existing `IntermediateAssembly`. +The `Csc` task will support a new output, called `OutputRefAssembly`, which parallels the existing `OutputAssembly`. +Both of those basically map to the `/refout` command-line parameter. + +An additional task, called `CopyRefAssembly`, will be provided along with the existing `Csc` task. It takes a `SourcePath` and a `DestinationPath` and generally copies the file from the source over to the destination. But if it can determine that the contents of those two files match, then the destination file is left untouched. + +### CodeAnalysis APIs +It is already possible to produce metadata-only assemblies by using `EmitOptions.EmitMetadataOnly`, which is used in IDE scenarios with cross-language dependencies. +The compiler will be updated to honour the `EmitOptions.IncludePrivateMembers` flag as well. When combined with `EmitMetadataOnly` or a `metadataPeStream` in `Emit`, a ref assembly will be produced. +The diagnostic check for emitting methods lacking a body (`void M();`) will be moved from declaration diagnostics to regular diagnostics, so that code will successfully emit with `EmitMetadataOnly`. +Later on, the `EmitOptions.TolerateErrors` flag will allow emitting error types as well. + +Going back to the 4 driving scenarios: +1. For a regular compilation, `EmitMetadataOnly` is left to `false` and no `metadataPeStream` is passed into `Emit`. +2. For the IDE scenario, `EmitMetadataOnly` is set to `true`, but `IncludePrivateMembers` is left to `true`. +3. For the CoreFX scenario, ref assembly source code is used as input, `EmitMetadataOnly` is set to `true`, and `IncludePrivateMembers` is set to `false`. +4. For the MSBuild scenario, `EmitMetadataOnly` is left to `false`, a `metadataPeStream` is passed in and `IncludePrivateMembers` is set to `false`. + +## Future +As mentioned above, there may be further refinements after C# 7.1: +- controlling internals (producing public ref assemblies) +- produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set) + +## Open questions +- should explicit method implementations be included in ref assemblies? +- Non-public attributes on public APIs (emit attribute based on accessibility rule) +- ref assemblies and NoPia +- `/refout` and `/addmodule`, should we disallow this combination? + +## Related issues +- Produce ref assemblies from command-line and msbuild (https://github.com/dotnet/roslyn/issues/2184) +- Refine what is in reference assemblies and what diagnostics prevent generating one (https://github.com/dotnet/roslyn/issues/17612) +- [Are private members part of the API surface?](http://blog.paranoidcoding.com/2016/02/15/are-private-members-api-surface.html) From 8e6cfc0fbe71934a4e9a626ba88880bc93085bdc Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 7 Apr 2017 23:57:33 -0700 Subject: [PATCH 011/284] Strip out private members from ref assemblies (#18339) --- .../CSharp/Portable/CSharpCodeAnalysis.csproj | 1 + .../CommandLine/CSharpCommandLineParser.cs | 13 +- .../Portable/Compilation/CSharpCompilation.cs | 17 +- .../Portable/Compiler/MethodCompiler.cs | 35 +- .../Emitter/EditAndContinue/EmitHelpers.cs | 5 +- .../EditAndContinue/PEDeltaAssemblyBuilder.cs | 4 +- .../Emitter/Model/MethodSymbolAdapter.cs | 4 +- .../Emitter/Model/NamedTypeSymbolAdapter.cs | 45 +- .../Portable/Emitter/Model/PEModuleBuilder.cs | 9 +- .../Model/SourceAssemblySymbolAdapter.cs | 30 ++ .../Portable/Emitter/Model/SymbolAdapter.cs | 1 + .../Emitter/NoPia/EmbeddedTypesManager.cs | 2 +- .../Symbols/Source/SourceAssemblySymbol.cs | 18 +- .../Test/CommandLine/CommandLineTests.cs | 61 +-- .../Attributes/AttributeTests_Assembly.cs | 6 +- .../Attributes/WellKnownAttributesTestBase.cs | 2 +- .../CSharp/Test/Emit/CodeGen/CodeGenTests.cs | 1 + .../Test/Emit/CodeGen/CodeGenTupleTest.cs | 8 +- .../Test/Emit/Emit/CompilationEmitTests.cs | 413 ++++++++++++++++-- .../EditAndContinue/SymbolMatcherTests.cs | 4 +- .../CSharp/Test/Emit/Emit/EmitMetadata.cs | 2 +- .../Symbol/Symbols/ExtensionMethodTests.cs | 2 +- .../Symbol/Symbols/Source/BaseClassTests.cs | 2 +- .../Test/Symbol/Symbols/Source/MethodTests.cs | 12 +- .../Symbol/Symbols/Source/PropertyTests.cs | 4 +- .../CodeAnalysisTest/Emit/EmitOptionsTests.cs | 2 +- .../Core/MSBuildTask/CopyRefAssembly.cs | 6 +- .../CodeAnalysisResources.Designer.cs | 9 + .../Core/Portable/CodeAnalysisResources.resx | 3 + .../CodeGen/PrivateImplementationDetails.cs | 2 +- .../Portable/CommandLine/CommonCompiler.cs | 6 + .../Core/Portable/Compilation/Compilation.cs | 102 +++-- .../Portable/Emit/CommonPEModuleBuilder.cs | 14 +- src/Compilers/Core/Portable/Emit/Context.cs | 28 +- .../EditAndContinue/DeltaMetadataWriter.cs | 11 +- .../Emit/EditAndContinue/SymbolChanges.cs | 2 +- .../Core/Portable/Emit/EmitOptions.cs | 8 +- .../Portable/Emit/NoPia/CommonEmbeddedType.cs | 29 +- .../Emit/NoPia/EmbeddedTypesManager.cs | 2 +- .../Portable/PEWriter/FullMetadataWriter.cs | 16 +- .../Core/Portable/PEWriter/Members.cs | 22 + .../Core/Portable/PEWriter/MetadataWriter.cs | 29 +- .../PEWriter/NoPiaReferenceIndexer.cs | 2 +- .../Core/Portable/PEWriter/PeWriter.cs | 4 +- .../Portable/PEWriter/ReferenceIndexer.cs | 2 +- .../Portable/PEWriter/ReferenceIndexerBase.cs | 2 +- .../Core/Portable/PEWriter/RootModuleType.cs | 4 +- src/Compilers/Core/Portable/PEWriter/Types.cs | 2 +- .../Core/Portable/PublicAPI.Unshipped.txt | 2 +- .../Attributes/AttributeDescription.cs | 3 + .../CommonAssemblyWellKnownAttributeData.cs | 18 + .../Symbols/ISourceAssemblySymbolInternal.cs | 2 + .../Core/Portable/WellKnownMember.cs | 1 + .../Core/Portable/WellKnownMembers.cs | 9 + src/Compilers/Core/Portable/WellKnownTypes.cs | 5 +- .../Portable/BasicCodeAnalysis.vbproj | 1 + .../Portable/CodeGen/EmitStatement.vb | 2 +- .../VisualBasicCommandLineParser.vb | 13 +- .../Portable/Compilation/MethodCompiler.vb | 21 +- .../Compilation/VisualBasicCompilation.vb | 10 +- .../Emit/EditAndContinue/EmitHelpers.vb | 5 +- .../EditAndContinue/PEDeltaAssemblyBuilder.vb | 4 +- .../Portable/Emit/NamedTypeSymbolAdapter.vb | 49 ++- .../Emit/NoPia/EmbeddedTypesManager.vb | 2 +- .../Portable/Emit/PEModuleBuilder.vb | 12 +- .../Emit/SourceAssemblySymbolAdapter.vb | 24 + .../Portable/Emit/SymbolAdapter.vb | 2 + .../VisualBasic/Portable/Errors/Errors.vb | 1 - .../Symbols/Source/SourceAssemblySymbol.vb | 31 +- .../Test/CommandLine/CommandLineTests.vb | 48 +- .../Emit/Attributes/AssemblyAttributes.vb | 10 +- .../Test/Emit/Emit/CompilationEmitTests.vb | 408 +++++++++++++++-- .../EditAndContinue/SymbolMatcherTests.vb | 6 +- .../SymbolsTests/Source/PropertyTests.vb | 2 +- .../ExpressionCompiler/EvaluationContext.cs | 12 +- .../ReferencedModulesTests.cs | 8 +- .../Test/ExpressionCompiler/TupleTests.cs | 6 +- .../ExpressionCompilerTestHelpers.cs | 7 +- .../NamespaceTypeDefinitionNoBase.cs | 2 +- .../ExpressionCompiler/EvaluationContext.vb | 9 +- .../ReferencedModulesTests.vb | 6 +- .../Test/ExpressionCompiler/TupleTests.vb | 6 +- .../Portable/Metadata/MetadataReaderUtils.cs | 48 +- 83 files changed, 1404 insertions(+), 399 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Emitter/Model/SourceAssemblySymbolAdapter.cs create mode 100644 src/Compilers/VisualBasic/Portable/Emit/SourceAssemblySymbolAdapter.vb diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj index 059d2f1220c84..bf32c7f307545 100644 --- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj +++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj @@ -271,6 +271,7 @@ + diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index 7d19a65d1ac3b..e3846cec32b50 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -68,7 +68,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar ImmutableArray> pathMap = ImmutableArray>.Empty; string outputFileName = null; string outputRefFilePath = null; - bool metadataOnly = false; + bool refOnly = false; string documentationPath = null; string errorLogPath = null; bool parseDocumentationComments = false; //Don't just null check documentationFileName because we want to do this even if the file name is invalid. @@ -411,7 +411,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar if (value != null) break; - metadataOnly = true; + refOnly = true; continue; case "t": @@ -1170,12 +1170,12 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar diagnosticOptions[o.Key] = o.Value; } - if (metadataOnly && outputRefFilePath != null) + if (refOnly && outputRefFilePath != null) { AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly); } - if (outputKind == OutputKind.NetModule && (metadataOnly || outputRefFilePath != null)) + if (outputKind == OutputKind.NetModule && (refOnly || outputRefFilePath != null)) { AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly); } @@ -1294,7 +1294,8 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar var emitOptions = new EmitOptions ( - metadataOnly: metadataOnly, + metadataOnly: refOnly, + includePrivateMembers: !refOnly && outputRefFilePath == null, debugInformationFormat: debugInformationFormat, pdbFilePath: null, // to be determined later outputNameOverride: null, // to be determined later @@ -1322,7 +1323,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar OutputFileName = outputFileName, OutputRefFilePath = outputRefFilePath, PdbPath = pdbPath, - EmitPdb = emitPdb && !metadataOnly, // silently ignore emitPdb when metadataOnly is set + EmitPdb = emitPdb && !refOnly, // silently ignore emitPdb when refOnly is set SourceLink = sourceLink, OutputDirectory = outputDirectory, DocumentationPath = documentationPath, diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 816d82bef8169..fe5d5899819ad 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2017,7 +2017,8 @@ private void GetDiagnosticsForAllMethodBodies(DiagnosticBag diagnostics, Cancell MethodCompiler.CompileMethodBodies( compilation: this, moduleBeingBuiltOpt: null, - generateDebugInfo: false, + emittingPdb: false, + emitTestCoverageData: false, hasDeclarationErrors: false, diagnostics: diagnostics, filterOpt: null, @@ -2059,7 +2060,8 @@ private ImmutableArray GetDiagnosticsForMethodBodiesInTree(SyntaxTre MethodCompiler.CompileMethodBodies( compilation: this, moduleBeingBuiltOpt: null, - generateDebugInfo: false, + emittingPdb: false, + emitTestCoverageData: false, hasDeclarationErrors: false, diagnostics: diagnostics, filterOpt: s => IsDefinedOrImplementedInSourceTree(s, tree, span), @@ -2301,6 +2303,8 @@ internal override CommonPEModuleBuilder CreateModuleBuilder( internal override bool CompileMethods( CommonPEModuleBuilder moduleBuilder, bool emittingPdb, + bool emitMetadataOnly, + bool emitTestCoverageData, DiagnosticBag diagnostics, Predicate filterOpt, CancellationToken cancellationToken) @@ -2313,9 +2317,8 @@ internal override bool CompileMethods( // EmbeddedSymbolManager.MarkAllDeferredSymbolsAsReferenced(this) var moduleBeingBuilt = (PEModuleBuilder)moduleBuilder; - var emitOptions = moduleBeingBuilt.EmitOptions; - if (emitOptions.EmitMetadataOnly) + if (emitMetadataOnly) { if (hasDeclarationErrors) { @@ -2333,7 +2336,7 @@ internal override bool CompileMethods( } else { - if ((emittingPdb || emitOptions.EmitTestCoverageData) && + if ((emittingPdb || emitTestCoverageData) && !CreateDebugDocuments(moduleBeingBuilt.DebugDocumentsBuilder, moduleBeingBuilt.EmbeddedTexts, diagnostics)) { return false; @@ -2349,6 +2352,7 @@ internal override bool CompileMethods( this, moduleBeingBuilt, emittingPdb, + emitTestCoverageData, hasDeclarationErrors, diagnostics: methodBodyDiagnosticBag, filterOpt: filterOpt, @@ -2369,6 +2373,7 @@ internal override bool GenerateResourcesAndDocumentationComments( CommonPEModuleBuilder moduleBuilder, Stream xmlDocStream, Stream win32Resources, + string outputNameOverride, DiagnosticBag diagnostics, CancellationToken cancellationToken) { @@ -2393,7 +2398,7 @@ internal override bool GenerateResourcesAndDocumentationComments( // Use a temporary bag so we don't have to refilter pre-existing diagnostics. var xmlDiagnostics = DiagnosticBag.GetInstance(); - string assemblyName = FileNameUtilities.ChangeExtension(moduleBuilder.EmitOptions.OutputNameOverride, extension: null); + string assemblyName = FileNameUtilities.ChangeExtension(outputNameOverride, extension: null); DocumentationCommentCompiler.WriteDocumentationCommentXml(this, assemblyName, xmlDocStream, xmlDiagnostics, cancellationToken); return FilterAndAppendAndFreeDiagnostics(diagnostics, ref xmlDiagnostics); diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 8daff0363d5a4..af6f3d6f99fcc 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -23,6 +23,7 @@ internal sealed class MethodCompiler : CSharpSymbolVisitor filterOpt, CancellationToken cancellationToken) { Debug.Assert(compilation != null); @@ -90,16 +91,19 @@ internal MethodCompiler(CSharpCompilation compilation, PEModuleBuilder moduleBei _hasDeclarationErrors = hasDeclarationErrors; SetGlobalErrorIfTrue(hasDeclarationErrors); - if (emittingPdb || moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData == true) + if (emittingPdb || emitTestCoverageData) { _debugDocumentProvider = (path, basePath) => moduleBeingBuiltOpt.DebugDocumentsBuilder.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile); } + + _emitTestCoverageData = emitTestCoverageData; } public static void CompileMethodBodies( CSharpCompilation compilation, PEModuleBuilder moduleBeingBuiltOpt, - bool generateDebugInfo, + bool emittingPdb, + bool emitTestCoverageData, bool hasDeclarationErrors, DiagnosticBag diagnostics, Predicate filterOpt, @@ -122,7 +126,8 @@ public static void CompileMethodBodies( MethodCompiler methodCompiler = new MethodCompiler( compilation, moduleBeingBuiltOpt, - generateDebugInfo, + emittingPdb, + emitTestCoverageData, hasDeclarationErrors, diagnostics, filterOpt, @@ -217,6 +222,7 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul debugDocumentProvider: null, importChainOpt: null, emittingPdb: false, + emitTestCoverageData: false, dynamicAnalysisSpans: ImmutableArray.Empty); moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody); } @@ -546,7 +552,7 @@ private void CompileSynthesizedMethods(PrivateImplementationDetails privateImplC Debug.Assert(_moduleBeingBuiltOpt != null); var compilationState = new TypeCompilationState(null, _compilation, _moduleBeingBuiltOpt); - foreach (MethodSymbol method in privateImplClass.GetMethods(new EmitContext(_moduleBeingBuiltOpt, null, diagnostics))) + foreach (MethodSymbol method in privateImplClass.GetMethods(new EmitContext(_moduleBeingBuiltOpt, null, diagnostics, metadataOnly: false, includePrivateMembers: true))) { Debug.Assert(method.SynthesizesLoweredBoundBody); method.GenerateMethodBody(compilationState, diagnostics); @@ -640,8 +646,9 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) variableSlotAllocatorOpt, diagnosticsThisMethod, _debugDocumentProvider, - method.GenerateDebugInfo ? importChain : null, + method.GenerateDebugInfo ? importChain : null, emittingPdb: _emittingPdb, + emitTestCoverageData: _emitTestCoverageData, dynamicAnalysisSpans: ImmutableArray.Empty); } } @@ -747,6 +754,7 @@ private void CompileFieldLikeEventAccessor(SourceEventSymbol eventSymbol, bool i debugDocumentProvider: _debugDocumentProvider, importChainOpt: null, emittingPdb: false, + emitTestCoverageData: _emitTestCoverageData, dynamicAnalysisSpans: ImmutableArray.Empty); _moduleBeingBuiltOpt.SetMethodBody(accessor, emittedBody); @@ -818,7 +826,6 @@ private void CompileMethod( } ImportChain oldImportChain = compilationState.CurrentImportChain; - bool instrumentForDynamicAnalysis = _moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData == true; // In order to avoid generating code for methods with errors, we create a diagnostic bag just for this method. DiagnosticBag diagsForCurrentMethod = DiagnosticBag.GetInstance(); @@ -887,9 +894,9 @@ private void CompileMethod( analyzedInitializers = InitializerRewriter.RewriteConstructor(processedInitializers.BoundInitializers, methodSymbol); processedInitializers.HasErrors = processedInitializers.HasErrors || analyzedInitializers.HasAnyErrors; - if (body != null && ((methodSymbol.ContainingType.IsStructType() && !methodSymbol.IsImplicitConstructor) || instrumentForDynamicAnalysis)) + if (body != null && ((methodSymbol.ContainingType.IsStructType() && !methodSymbol.IsImplicitConstructor) || _emitTestCoverageData)) { - if (instrumentForDynamicAnalysis && methodSymbol.IsImplicitConstructor) + if (_emitTestCoverageData && methodSymbol.IsImplicitConstructor) { // Flow analysis over the initializers is necessary in order to find assignments to fields. // Bodies of implicit constructors do not get flow analysis later, so the initializers @@ -1004,7 +1011,7 @@ private void CompileMethod( flowAnalyzedBody, previousSubmissionFields, compilationState, - instrumentForDynamicAnalysis, + _emitTestCoverageData, _debugDocumentProvider, ref dynamicAnalysisSpans, diagsForCurrentMethod, @@ -1046,7 +1053,7 @@ private void CompileMethod( { // For dynamic analysis, field initializers are instrumented as part of constructors, // and so are never instrumented here. - Debug.Assert(!instrumentForDynamicAnalysis); + Debug.Assert(!_emitTestCoverageData); StateMachineTypeSymbol initializerStateMachineTypeOpt; BoundStatement lowered = LowerBodyOrInitializer( @@ -1055,7 +1062,7 @@ private void CompileMethod( analyzedInitializers, previousSubmissionFields, compilationState, - instrumentForDynamicAnalysis, + _emitTestCoverageData, _debugDocumentProvider, ref dynamicAnalysisSpans, diagsForCurrentMethod, @@ -1119,6 +1126,7 @@ private void CompileMethod( _debugDocumentProvider, importChain, _emittingPdb, + _emitTestCoverageData, dynamicAnalysisSpans); _moduleBeingBuiltOpt.SetMethodBody(methodSymbol.PartialDefinitionPart ?? methodSymbol, emittedBody); @@ -1275,6 +1283,7 @@ private static MethodBody GenerateMethodBody( DebugDocumentProvider debugDocumentProvider, ImportChain importChainOpt, bool emittingPdb, + bool emitTestCoverageData, ImmutableArray dynamicAnalysisSpans) { // Note: don't call diagnostics.HasAnyErrors() in release; could be expensive if compilation has many warnings. @@ -1364,7 +1373,7 @@ private static MethodBody GenerateMethodBody( } DynamicAnalysisMethodBodyData dynamicAnalysisDataOpt = null; - if (moduleBuilder.EmitOptions.EmitTestCoverageData) + if (emitTestCoverageData) { Debug.Assert(debugDocumentProvider != null); dynamicAnalysisDataOpt = new DynamicAnalysisMethodBodyData(dynamicAnalysisSpans); diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs index 72b8f9303c885..00ed2fbed8133 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs @@ -87,6 +87,7 @@ internal static EmitDifferenceResult EmitDifference( updatedMethods, diagnostics, testData?.SymWriterFactory, + emitOptions.PdbFilePath, cancellationToken); } @@ -123,8 +124,8 @@ private static EmitBaseline MapToCompilation( // Mapping from previous compilation to the current. var anonymousTypeMap = moduleBeingBuilt.GetAnonymousTypeMap(); var sourceAssembly = ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly; - var sourceContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag()); - var otherContext = new EmitContext(moduleBeingBuilt, null, new DiagnosticBag()); + var sourceContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); + var otherContext = new EmitContext(moduleBeingBuilt, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var matcher = new CSharpSymbolMatcher( anonymousTypeMap, diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs index 50fe809e3f4c8..f304335d41170 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs @@ -33,7 +33,7 @@ public PEDeltaAssemblyBuilder( : base(sourceAssembly, emitOptions, outputKind, serializationProperties, manifestResources, additionalTypes: ImmutableArray.Empty) { var initialBaseline = previousGeneration.InitialBaseline; - var context = new EmitContext(this, null, new DiagnosticBag()); + var context = new EmitContext(this, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); // Hydrate symbols from initial metadata. Once we do so it is important to reuse these symbols across all generations, // in order for the symbol matcher to be able to use reference equality once it maps symbols to initial metadata. @@ -47,7 +47,7 @@ public PEDeltaAssemblyBuilder( if (previousGeneration.Ordinal > 0) { var previousAssembly = ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly; - var previousContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag()); + var previousContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); matchToPrevious = new CSharpSymbolMatcher( previousGeneration.AnonymousTypeMap, diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs index c776c270e26a2..20bb10723f950 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs @@ -582,10 +582,8 @@ private IEnumerable GetReturnValueCustomAttributesToEmit() { CheckDefinitionInvariant(); - ImmutableArray userDefined; + ImmutableArray userDefined = this.GetReturnTypeAttributes(); ArrayBuilder synthesized = null; - - userDefined = this.GetReturnTypeAttributes(); this.AddSynthesizedReturnTypeAttributes(ref synthesized); // Note that callers of this method (CCI and ReflectionEmitter) have to enumerate diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index da185b6ee18cf..cfbad7e754c4b 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -287,12 +287,15 @@ Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context) diagnostics: context.Diagnostics) : null; } - IEnumerable Cci.ITypeDefinition.Events + IEnumerable Cci.ITypeDefinition.GetEvents(EmitContext context) { - get + CheckDefinitionInvariant(); + foreach (var e in GetEventsToEmit()) { - CheckDefinitionInvariant(); - return GetEventsToEmit(); + if (e.ShouldInclude(context)) + { + yield return e; + } } } @@ -322,6 +325,7 @@ internal virtual IEnumerable GetEventsToEmit() foreach (var member in this.GetMembers()) { + // PROTOTYPE(refout) Do something here? if (member.Kind == SymbolKind.Method) { var method = (MethodSymbol)member; @@ -389,9 +393,15 @@ internal virtual IEnumerable GetEventsToEmit() { CheckDefinitionInvariant(); + // All fields in a struct should be emitted + bool isStruct = this.IsStructType(); + foreach (var f in GetFieldsToEmit()) { - yield return f; + if (isStruct || f.ShouldInclude(context)) + { + yield return f; + } } IEnumerable generated = ((PEModuleBuilder)context.Module).GetSynthesizedFields(this); @@ -400,7 +410,10 @@ internal virtual IEnumerable GetEventsToEmit() { foreach (var f in generated) { - yield return f; + if (isStruct || f.ShouldInclude(context)) + { + yield return f; + } } } } @@ -630,7 +643,10 @@ internal virtual bool IsMetadataSealed foreach (var method in this.GetMethodsToEmit()) { Debug.Assert((object)method != null); - yield return method; + if (method.ShouldInclude(context)) + { + yield return method; + } } IEnumerable generated = ((PEModuleBuilder)context.Module).GetSynthesizedMethods(this); @@ -639,7 +655,10 @@ internal virtual bool IsMetadataSealed { foreach (var m in generated) { - yield return m; + if (m.ShouldInclude(context)) + { + yield return m; + } } } } @@ -704,7 +723,10 @@ internal virtual IEnumerable GetMethodsToEmit() foreach (var property in this.GetPropertiesToEmit()) { Debug.Assert((object)property != null); - yield return property; + if (property.ShouldInclude(context)) + { + yield return property; + } } IEnumerable generated = ((PEModuleBuilder)context.Module).GetSynthesizedProperties(this); @@ -713,7 +735,10 @@ internal virtual IEnumerable GetMethodsToEmit() { foreach (var m in generated) { - yield return m; + if (m.ShouldInclude(context)) + { + yield return m; + } } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 60b6fb3d1412d..1f6765b2b9526 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -83,9 +83,10 @@ internal sealed override Cci.ICustomAttribute SynthesizeAttribute(WellKnownMembe return Compilation.TrySynthesizeAttribute(attributeConstructor); } - public sealed override IEnumerable GetSourceAssemblyAttributes() + public sealed override IEnumerable GetSourceAssemblyAttributes(bool isRefAssembly) { - return SourceModule.ContainingSourceAssembly.GetCustomAttributesToEmit(this.CompilationState, emittingAssemblyAttributesInNetModule: OutputKind.IsNetModule()); + return SourceModule.ContainingSourceAssembly + .GetCustomAttributesToEmit(this.CompilationState, isRefAssembly, emittingAssemblyAttributesInNetModule: OutputKind.IsNetModule()); } public sealed override IEnumerable GetSourceAssemblySecurityAttributes() @@ -358,9 +359,9 @@ internal virtual bool TryGetAnonymousTypeName(NamedTypeSymbol template, out stri return false; } - internal sealed override ImmutableArray GetAnonymousTypes() + internal sealed override ImmutableArray GetAnonymousTypes(EmitContext context) { - if (EmitOptions.EmitMetadataOnly) + if (context.MetadataOnly) { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/SourceAssemblySymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/SourceAssemblySymbolAdapter.cs new file mode 100644 index 0000000000000..e93fc7cdcc63f --- /dev/null +++ b/src/Compilers/CSharp/Portable/Emitter/Model/SourceAssemblySymbolAdapter.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal partial class SourceAssemblySymbol + { + internal IEnumerable GetCustomAttributesToEmit(ModuleCompilationState compilationState, bool emittingRefAssembly, bool emittingAssemblyAttributesInNetModule) + { + CheckDefinitionInvariant(); + + ImmutableArray userDefined = this.GetAttributes(); + ArrayBuilder synthesized = null; + this.AddSynthesizedAttributes(compilationState, ref synthesized); + + if (emittingRefAssembly && !HasReferenceAssemblyAttribute) + { + var referenceAssemblyAttribute = this.DeclaringCompilation + .TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, isOptionalUse: true); + Symbol.AddSynthesizedAttribute(ref synthesized, referenceAssemblyAttribute); + } + + // Note that callers of this method (CCI and ReflectionEmitter) have to enumerate + // all items of the returned iterator, otherwise the synthesized ArrayBuilder may leak. + return GetCustomAttributesToEmit(userDefined, synthesized, isReturnType: false, emittingAssemblyAttributesInNetModule: emittingAssemblyAttributesInNetModule); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs index d1c10b1df43d0..1395f36244021 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs @@ -66,6 +66,7 @@ internal virtual IEnumerable GetCustomAttributesToEmit(Modu internal IEnumerable GetCustomAttributesToEmit(ModuleCompilationState compilationState, bool emittingAssemblyAttributesInNetModule) { CheckDefinitionInvariant(); + Debug.Assert(this.Kind != SymbolKind.Assembly); ImmutableArray userDefined; ArrayBuilder synthesized = null; diff --git a/src/Compilers/CSharp/Portable/Emitter/NoPia/EmbeddedTypesManager.cs b/src/Compilers/CSharp/Portable/Emitter/NoPia/EmbeddedTypesManager.cs index 7d15930176980..2e41a059aaf81 100644 --- a/src/Compilers/CSharp/Portable/Emitter/NoPia/EmbeddedTypesManager.cs +++ b/src/Compilers/CSharp/Portable/Emitter/NoPia/EmbeddedTypesManager.cs @@ -331,7 +331,7 @@ private EmbeddedType EmbedType( // Therefore, the following check can be as simple as: Debug.Assert(!IsFrozen, "Set of embedded types is frozen."); - var noPiaIndexer = new Cci.NoPiaReferenceIndexer(new EmitContext(ModuleBeingBuilt, syntaxNodeOpt, diagnostics)); + var noPiaIndexer = new Cci.NoPiaReferenceIndexer(new EmitContext(ModuleBeingBuilt, syntaxNodeOpt, diagnostics, metadataOnly: false, includePrivateMembers: true)); // Make sure we embed all types referenced by the type declaration: implemented interfaces, etc. noPiaIndexer.VisitTypeDefinitionNoMembers(embedded); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index 4cc0f8a9bea23..48f86ecbd1c2e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Reflection; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -20,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// /// Represents an assembly built by compiler. /// - internal sealed class SourceAssemblySymbol : MetadataOrSourceAssemblySymbol, ISourceAssemblySymbolInternal, IAttributeTargetSymbol + internal sealed partial class SourceAssemblySymbol : MetadataOrSourceAssemblySymbol, ISourceAssemblySymbolInternal, IAttributeTargetSymbol { /// /// A Compilation the assembly is created for. @@ -730,7 +729,7 @@ private void ValidateIVTPublicKeys(DiagnosticBag diagnostics) /// Forces binding and decoding of attributes. /// This property shouldn't be accessed during binding as it can lead to attribute binding cycle. /// - private bool InternalsAreVisible + public bool InternalsAreVisible { get { @@ -1696,6 +1695,15 @@ private bool HasDebuggableAttribute } } + private bool HasReferenceAssemblyAttribute + { + get + { + CommonAssemblyWellKnownAttributeData assemblyData = this.GetSourceDecodedWellKnownAttributeData(); + return assemblyData != null && assemblyData.HasReferenceAssemblyAttribute; + } + } + internal override void AddSynthesizedAttributes(ModuleCompilationState compilationState, ref ArrayBuilder attributes) { base.AddSynthesizedAttributes(compilationState, ref attributes); @@ -2264,6 +2272,10 @@ private void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments().HasCompilationRelaxationsAttribute = true; } + else if (attribute.IsTargetAttribute(this, AttributeDescription.ReferenceAssemblyAttribute)) + { + arguments.GetOrCreateData().HasReferenceAssemblyAttribute = true; + } else if (attribute.IsTargetAttribute(this, AttributeDescription.RuntimeCompatibilityAttribute)) { bool wrapNonExceptionThrows = true; diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 489f10ade08e8..2f5ba3d35484d 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -9006,8 +9006,8 @@ public static void Main() Assert.True(File.Exists(exe)); MetadataReaderUtils.VerifyPEMetadata(exe, - new[] { "TypeDef:", "TypeDef:C" }, - new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "TypeDefinition:", "TypeDefinition:C" }, + new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } ); @@ -9036,54 +9036,18 @@ public static void Main() Assert.True(File.Exists(refDll)); // The types and members that are included needs further refinement. - // ReferenceAssemblyAttribute is missing. // See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - new[] { "TypeDef:", "TypeDef:C" }, - new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, - new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + new[] { "TypeDefinition:", "TypeDefinition:C" }, + new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" } ); - VerifyNullReferenceException(refDir, "a.dll"); - // Clean up temp files CleanupAllGeneratedFiles(dir.Path); CleanupAllGeneratedFiles(refDir.Path); } - /// - /// Calls C.Main() on the program and verifies that a null reference exception is thrown. - /// - private static void VerifyNullReferenceException(TempDirectory dir, string programName) - { - var src = dir.CreateFile("verifier.cs"); - src.WriteAllText(@" -class Verifier -{ - public static void Main() - { - try - { - C.Main(); - } - catch(System.NullReferenceException) - { - System.Console.Write(""Got expected exception""); - } - } -}"); - - var outWriter = new StringWriter(CultureInfo.InvariantCulture); - var csc = new MockCSharpCompiler(null, dir.Path, - new[] { "/nologo", "/out:verifier.exe", $"/reference:{programName}", "verifier.cs" }); - - int exitCode = csc.Run(outWriter); - Assert.Equal(0, exitCode); - string dirPath = dir.ToString(); - string output = ProcessUtilities.RunAndGetOutput(Path.Combine(dirPath, "verifier.exe"), startFolder: dirPath, expectedRetCode: 0); - Assert.Equal("Got expected exception", output); - } - [Fact] public void RefOutWithError() { @@ -9118,6 +9082,7 @@ public void RefOnly() var src = dir.CreateFile("a.cs"); src.WriteAllText(@" +using System; class C { /// Main method @@ -9125,24 +9090,30 @@ public static void Main() { error(); // semantic error in method body } + private event Action E1 + { + add { } + remove { } + } + private event Action E2; }"); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var csc = new MockCSharpCompiler(null, dir.Path, new[] { "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "/doc:doc.xml", "a.cs" }); int exitCode = csc.Run(outWriter); + Assert.Equal("", outWriter.ToString()); Assert.Equal(0, exitCode); var refDll = Path.Combine(dir.Path, "a.dll"); Assert.True(File.Exists(refDll)); // The types and members that are included needs further refinement. - // ReferenceAssemblyAttribute is missing. // See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - new[] { "TypeDef:", "TypeDef:C" }, - new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, - new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + new[] { "TypeDefinition:", "TypeDefinition:C" }, + new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" } ); var pdb = Path.Combine(dir.Path, "a.pdb"); diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Assembly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Assembly.cs index 29c448c524456..8169f56bbd693 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Assembly.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Assembly.cs @@ -954,7 +954,8 @@ private static void TestDuplicateAssemblyAttributesNotEmitted(AssemblySymbol ass // We should get only unique netmodule/assembly attributes here, duplicate ones should not be emitted. int expectedEmittedAttrsCount = expectedSrcAttrCount - expectedDuplicateAttrCount; - var allEmittedAttrs = assembly.GetCustomAttributesToEmit(new ModuleCompilationState(), emittingAssemblyAttributesInNetModule: false); + var allEmittedAttrs = ((SourceAssemblySymbol)assembly).GetCustomAttributesToEmit(new ModuleCompilationState(), + emittingRefAssembly: false, emittingAssemblyAttributesInNetModule: false); var emittedAttrs = allEmittedAttrs.Where(a => string.Equals(a.AttributeClass.Name, attrTypeName, StringComparison.Ordinal)).AsImmutable(); Assert.Equal(expectedEmittedAttrsCount, emittedAttrs.Length); @@ -1243,7 +1244,8 @@ static void Main(string[] args) { } expectedDuplicateAttrCount: 1, attrTypeName: "AssemblyTitleAttribute"); - var attrs = consoleappCompilation.Assembly.GetCustomAttributesToEmit(new ModuleCompilationState(), emittingAssemblyAttributesInNetModule: false); + var attrs = ((SourceAssemblySymbol)consoleappCompilation.Assembly).GetCustomAttributesToEmit(new ModuleCompilationState(), + emittingRefAssembly: false, emittingAssemblyAttributesInNetModule: false); foreach (var a in attrs) { switch (a.AttributeClass.Name) diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/WellKnownAttributesTestBase.cs b/src/Compilers/CSharp/Test/Emit/Attributes/WellKnownAttributesTestBase.cs index 10d44a7cf2c09..332fe419da9ad 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/WellKnownAttributesTestBase.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/WellKnownAttributesTestBase.cs @@ -87,7 +87,7 @@ internal static void VerifyParamArrayAttribute(ParameterSymbol parameter, Source var paramArrayAttributeCtor = (MethodSymbol)emitModule.Compilation.GetWellKnownTypeMember(WellKnownMember.System_ParamArrayAttribute__ctor); bool found = false; - var context = new EmitContext(emitModule, null, new DiagnosticBag()); + var context = new EmitContext(emitModule, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); foreach (Microsoft.Cci.ICustomAttribute attr in parameter.GetSynthesizedAttributes()) { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs index 00aedb211f357..27176d1dd9d1b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs @@ -12826,6 +12826,7 @@ static System.Action M() compilation: compilation, moduleBeingBuiltOpt: module, emittingPdb: false, + emitTestCoverageData: false, hasDeclarationErrors: false, diagnostics: diagnostics, filterOpt: null, diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 52e573a6b45d4..c15e49dde5932 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -19852,7 +19852,7 @@ static void Main() { var reader = block.MetadataReader; AssertEx.SetEqual(new[] { "mscorlib 4.0", "System.ValueTuple 4.0" }, reader.DumpAssemblyReferences()); - Assert.Contains("ValueTuple`2, System, AssemblyRef:System.ValueTuple", reader.DumpTypeReferences()); + Assert.Contains("ValueTuple`2, System, AssemblyReference:System.ValueTuple", reader.DumpTypeReferences()); } // emit with pdb @@ -19861,7 +19861,7 @@ static void Main() { var reader = assembly.GetMetadataReader(); AssertEx.SetEqual(new[] { "mscorlib 4.0", "System.ValueTuple 4.0" }, reader.DumpAssemblyReferences()); - Assert.Contains("ValueTuple`2, System, AssemblyRef:System.ValueTuple", reader.DumpTypeReferences()); + Assert.Contains("ValueTuple`2, System, AssemblyReference:System.ValueTuple", reader.DumpTypeReferences()); }); // no assertion in MetadataWriter } @@ -19892,7 +19892,7 @@ static void Main() { var reader = block.MetadataReader; AssertEx.SetEqual(new[] { "mscorlib 4.0", "lib 0.0" }, reader.DumpAssemblyReferences()); - Assert.Contains("ReferencedType, , AssemblyRef:lib", reader.DumpTypeReferences()); + Assert.Contains("ReferencedType, , AssemblyReference:lib", reader.DumpTypeReferences()); } // emit with pdb @@ -19901,7 +19901,7 @@ static void Main() { var reader = assembly.GetMetadataReader(); AssertEx.SetEqual(new[] { "mscorlib 4.0", "lib 0.0" }, reader.DumpAssemblyReferences()); - Assert.Contains("ReferencedType, , AssemblyRef:lib", reader.DumpTypeReferences()); + Assert.Contains("ReferencedType, , AssemblyReference:lib", reader.DumpTypeReferences()); }); // no assertion in MetadataWriter } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 20b13f8ac0e1c..69dc56b4444cd 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -8,6 +8,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; +using System.Text; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -237,41 +238,335 @@ public static void Main() } } + [Fact] + public void RefAssembly_HasReferenceAssemblyAttribute() + { + var emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + + Action assemblyValidator = assembly => + { + var reader = assembly.GetMetadataReader(); + var attributes = reader.GetAssemblyDefinition().GetCustomAttributes(); + AssertEx.Equal(new[] { + "MemberReference:Void System.Runtime.CompilerServices.CompilationRelaxationsAttribute.ctor(Int32)", + "MemberReference:Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute.ctor()", + "MemberReference:Void System.Diagnostics.DebuggableAttribute.ctor(DebuggingModes)", + "MemberReference:Void System.Runtime.CompilerServices.ReferenceAssemblyAttribute.ctor()" + }, + attributes.Select(a => MetadataReaderUtils.Dump(reader, reader.GetCustomAttribute(a).Constructor))); + }; + + CompileAndVerify("", emitOptions: emitRefAssembly, assemblyValidator: assemblyValidator); + } + + [Fact] + public void RefAssembly_HandlesMissingReferenceAssemblyAttribute() + { + var emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + + Action assemblyValidator = assembly => + { + var reader = assembly.GetMetadataReader(); + var attributes = reader.GetAssemblyDefinition().GetCustomAttributes(); + AssertEx.SetEqual(attributes.Select(a => MetadataReaderUtils.Dump(reader, reader.GetCustomAttribute(a).Constructor)), + new string[] { + "MemberReference:Void System.Runtime.CompilerServices.CompilationRelaxationsAttribute.ctor(Int32)", + "MemberReference:Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute.ctor()", + "MemberReference:Void System.Diagnostics.DebuggableAttribute.ctor(DebuggingModes)" + }); + }; + + var comp = CreateCompilationWithMscorlib(""); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor); + CompileAndVerify(compilation: comp, emitOptions: emitRefAssembly, assemblyValidator: assemblyValidator); + } + + [Fact] + public void RefAssembly_ReferenceAssemblyAttributeAlsoInSource() + { + var emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + + Action assemblyValidator = assembly => + { + var reader = assembly.GetMetadataReader(); + var attributes = reader.GetAssemblyDefinition().GetCustomAttributes(); + AssertEx.Equal(new string[] { + "MemberReference:Void System.Runtime.CompilerServices.CompilationRelaxationsAttribute.ctor(Int32)", + "MemberReference:Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute.ctor()", + "MemberReference:Void System.Diagnostics.DebuggableAttribute.ctor(DebuggingModes)", + "MemberReference:Void System.Runtime.CompilerServices.ReferenceAssemblyAttribute.ctor()" + }, + attributes.Select(a => MetadataReaderUtils.Dump(reader, reader.GetCustomAttribute(a).Constructor))); + }; + string source = @"[assembly:System.Runtime.CompilerServices.ReferenceAssembly()]"; + CompileAndVerify(source, emitOptions: emitRefAssembly, assemblyValidator: assemblyValidator); + } + + [Theory] + [InlineData("public int M() { return 1; }", "public int M() { return 2; }", Match.BothMetadataAndRefOut)] + [InlineData("public int M() { return 1; }", "public int M() { error(); }", Match.BothMetadataAndRefOut)] + [InlineData("private void M() { }", "", Match.RefOut)] + [InlineData("internal void M() { }", "", Match.RefOut)] + [InlineData("private void M() { dynamic x = 1; }", "", Match.RefOut)] // no reference added from method bodies + [InlineData(@"private void M() { var x = new { id = 1 }; }", "", Match.RefOut)] + [InlineData("private int P { get { Error(); } set { Error(); } }", "", Match.RefOut)] // errors in methods bodies don't matter + [InlineData("public int P { get; set; }", "", Match.Different)] + [InlineData("protected int P { get; set; }", "", Match.Different)] + [InlineData("private int P { get; set; }", "", Match.RefOut)] // private auto-property and underlying field are removed + [InlineData("internal int P { get; set; }", "", Match.RefOut)] + [InlineData("private event Action E { add { Error(); } remove { Error(); } }", "", Match.RefOut)] + [InlineData("internal event Action E { add { Error(); } remove { Error(); } }", "", Match.RefOut)] + [InlineData("private class C2 { }", "", Match.Different)] // all types are included + [InlineData("private struct S { }", "", Match.Different)] + [InlineData("public struct S { private int i; }", "public struct S { }", Match.Different)] + [InlineData("private int i;", "", Match.RefOut)] + [InlineData("public C() { }", "", Match.BothMetadataAndRefOut)] + //[InlineData("public int NoBody();", "public int NoBody() { }", Match.BothMetadataAndRefOut)] // PROTOTYPE(refout) Further refinement https://github.com/dotnet/roslyn/issues/17612 + public void RefAssembly_InvariantToSomeChanges(string left, string right, Match expectedMatch) + { + string sourceTemplate = @" +using System; +public class C +{ + CHANGE +} +"; + + CompareAssemblies(sourceTemplate, left, right, expectedMatch, includePrivateMembers: true); + CompareAssemblies(sourceTemplate, left, right, expectedMatch, includePrivateMembers: false); + } + [Theory] - [InlineData("public int M() { return 1; }", "public int M() { return 2; }", true)] - [InlineData("public int M() { return 1; }", "public int M() { error(); }", true)] - [InlineData("", "private void M() { }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 - [InlineData("internal void M() { }", "", false)] - [InlineData("public struct S { private int i; }", "public struct S { }", false)] - public void RefAssembly_InvariantToSomeChanges(string change1, string change2, bool expectMatch) + [InlineData("internal void M() { }", "", Match.Different)] + public void RefAssembly_InvariantToSomeChangesWithInternalsVisibleTo(string left, string right, Match expectedMatch) { string sourceTemplate = @" +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleToAttribute(""Friend"")] public class C { CHANGE } "; + + CompareAssemblies(sourceTemplate, left, right, expectedMatch, includePrivateMembers: true); + CompareAssemblies(sourceTemplate, left, right, expectedMatch, includePrivateMembers: false); + } + + public enum Match + { + BothMetadataAndRefOut, + RefOut, + Different + } + + /// + /// Are the metadata-only assemblies identical with two source code modifications? + /// Metadata-only assemblies can either include private/internal members or not. + /// + private void CompareAssemblies(string sourceTemplate, string change1, string change2, Match expectedMatch, bool includePrivateMembers) + { + bool expectMatch = includePrivateMembers ? + expectedMatch == Match.BothMetadataAndRefOut : + (expectedMatch == Match.BothMetadataAndRefOut || expectedMatch == Match.RefOut); + string name = GetUniqueName(); string source1 = sourceTemplate.Replace("CHANGE", change1); CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source1), options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); - ImmutableArray image1 = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true)); + ImmutableArray image1 = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(includePrivateMembers)); var source2 = sourceTemplate.Replace("CHANGE", change2); Compilation comp2 = CreateCompilationWithMscorlib(Parse(source2), options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); - ImmutableArray image2 = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true)); + ImmutableArray image2 = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(includePrivateMembers)); if (expectMatch) { - AssertEx.Equal(image1, image2); + AssertEx.Equal(image1, image2, message: $"Expecting match for includePrivateMembers={includePrivateMembers} case, but differences were found."); } else { - AssertEx.NotEqual(image1, image2); + AssertEx.NotEqual(image1, image2, message: $"Expecting difference for includePrivateMembers={includePrivateMembers} case, but they matched."); } } + [Fact] + public void RefAssemblyClient_StructWithPrivateReferenceTypeField() + { + VerifyRefAssemblyClient(@" +public struct S +{ + private object _field; + public static S GetValue() => new S() { _field = new object() }; +}", +@"class C +{ + void M() + { + unsafe + { + System.Console.WriteLine(sizeof(S*)); + } + } +}", +comp => comp.VerifyDiagnostics( + // (7,45): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('S') + // System.Console.WriteLine(sizeof(S*)); + Diagnostic(ErrorCode.ERR_ManagedAddr, "S*").WithArguments("S").WithLocation(7, 45) + )); + } + + [Fact] + public void RefAssemblyClient_EmitAllNestedTypes() + { + VerifyRefAssemblyClient(@" +public interface I1 { } +public interface I2 { } +public class A: I1 +{ + private class X: I2 { } +}", +@"class C +{ + I1 M(A a) + { + return (I1)a; + } +}", +comp => comp.VerifyDiagnostics()); + } + + [Fact] + public void RefAssemblyClient_EmitAllTypes() + { + VerifyRefAssemblyClient(@" +public interface I1 { } +public interface I2 { } +public class A: I1 { } +internal class X: I2 { } +", +@"class C +{ + I1 M(A a) + { + return (I1)a; + } +}", +comp => comp.VerifyDiagnostics()); + } + + [Fact] + public void RefAssemblyClient_StructWithPrivateGenericField() + { + VerifyRefAssemblyClient(@" +public struct Container +{ + private T contained; +}", +@"public struct Usage +{ + public Container x; +}", +comp => comp.VerifyDiagnostics( + // (3,29): error CS0523: Struct member 'Usage.x' of type 'Container' causes a cycle in the struct layout + // public Container x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("Usage.x", "Container").WithLocation(3, 29) + )); + } + + [Fact] + public void RefAssemblyClient_EmitAllVirtualMethods() + { + + var comp1 = CreateCSharpCompilation("CS1", +@"[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""CS2"")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""CS3"")] +public abstract class C1 +{ + internal abstract void M(); +}", + referencedAssemblies: new[] { MscorlibRef }); + comp1.VerifyDiagnostics(); + var image1 = comp1.EmitToImageReference(EmitOptions.Default); + + var comp2 = CreateCSharpCompilation("CS2", +@"public abstract class C2 : C1 +{ + internal override void M() { } +}", + referencedAssemblies: new[] { MscorlibRef, image1 }); + var image2 = comp2.EmitToImageReference(EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)); + + // If internal virtual methods were not included in ref assemblies, then C3 could not be concrete and would report + // error CS0534: 'C3' does not implement inherited abstract member 'C1.M()' + + var comp3 = CreateCSharpCompilation("CS3", +@"public class C3 : C2 +{ +}", + referencedAssemblies: new[] { MscorlibRef, image1, image2 }); + comp3.VerifyDiagnostics(); + } + + [Fact] + public void RefAssemblyClient_StructWithPrivateIntField() + { + VerifyRefAssemblyClient(@" +public struct S +{ + private int i; +}", +@"class C +{ + string M() + { + S s; + return s.ToString(); + } +}", +comp => comp.VerifyDiagnostics( + // (6,16): error CS0165: Use of unassigned local variable 's' + // return s.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(6, 16) + )); + } + + private void VerifyRefAssemblyClient(string lib_cs, string client_cs, Action validator, int debugFlag = -1) + { + // Whether the library is compiled in full, as metadata-only, or as a ref assembly should be transparent + // to the client and the validator should be able to verify the same expectations. + + if (debugFlag == -1 || debugFlag == 0) + { + VerifyRefAssemblyClient(lib_cs, client_cs, validator, + EmitOptions.Default.WithEmitMetadataOnly(false)); + } + + if (debugFlag == -1 || debugFlag == 1) + { + VerifyRefAssemblyClient(lib_cs, client_cs, validator, + EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(true)); + } + + if (debugFlag == -1 || debugFlag == 2) + { + VerifyRefAssemblyClient(lib_cs, client_cs, validator, + EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)); + } + } + + private static void VerifyRefAssemblyClient(string lib_cs, string source, Action validator, EmitOptions emitOptions) + { + string name = GetUniqueName(); + var libComp = CreateCompilationWithMscorlib(Parse(lib_cs), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + var libImage = libComp.EmitToImageReference(emitOptions); + + var comp = CreateCompilationWithMscorlib(source, references: new[] { libImage }, options: TestOptions.DebugDll.WithAllowUnsafe(true)); + validator(comp); + } + [Theory] [InlineData("public int M() { error(); }", true)] [InlineData("public int M() { error() }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 @@ -301,46 +596,90 @@ public class C public void VerifyRefAssembly() { string source = @" -public abstract class PublicClass +public class PublicClass { - public void PublicMethod() { System.Console.Write(""Hello""); } + public void PublicMethod() { System.Console.Write(new { anonymous = 1 }); } private void PrivateMethod() { System.Console.Write(""Hello""); } protected void ProtectedMethod() { System.Console.Write(""Hello""); } internal void InternalMethod() { System.Console.Write(""Hello""); } - public abstract void AbstractMethod(); } "; CSharpCompilation comp = CreateCompilation(source, references: new[] { MscorlibRef }, options: TestOptions.DebugDll.WithDeterministic(true)); - var emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(true); + // verify metadata (types, members, attributes) of the regular assembly + CompileAndVerify(comp, emitOptions: EmitOptions.Default, verify: true); + + var realImage = comp.EmitToImageReference(EmitOptions.Default); + var compWithReal = CreateCompilation("", references: new[] { MscorlibRef, realImage }, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + AssertEx.Equal( + compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString()), + new[] { "", "<>f__AnonymousType0<j__TPar>", "PublicClass" }); + + AssertEx.Equal( + ((NamedTypeSymbol)compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMember("PublicClass")).GetMembers() + .Select(m => m.ToTestDisplayString()), + new[] { "void PublicClass.PublicMethod()", "void PublicClass.PrivateMethod()", + "void PublicClass.ProtectedMethod()", "void PublicClass.InternalMethod()", + "PublicClass..ctor()" }); + + AssertEx.Equal(compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute" }); - var verifier = CompileAndVerify(comp, emitOptions: emitRefOnly, verify: true); + // verify metadata (types, members, attributes) of the metadata-only assembly + var emitMetadataOnly = EmitOptions.Default.WithEmitMetadataOnly(true); + CompileAndVerify(comp, emitOptions: emitMetadataOnly, verify: true); - // verify metadata (types, members, attributes) - var image = comp.EmitToImageReference(emitRefOnly); - var comp2 = CreateCompilation("", references: new[] { MscorlibRef, image }, + var metadataImage = comp.EmitToImageReference(emitMetadataOnly); + var compWithMetadata = CreateCompilation("", references: new[] { MscorlibRef, metadataImage }, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); - AssemblySymbol assembly = comp2.SourceModule.GetReferencedAssemblySymbols().Last(); - var members = assembly.GlobalNamespace.GetMembers(); - AssertEx.SetEqual(members.Select(m => m.ToDisplayString()), + AssertEx.Equal( + compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString()), new[] { "", "PublicClass" }); - AssertEx.SetEqual( - ((NamedTypeSymbol)assembly.GlobalNamespace.GetMember("PublicClass")).GetMembers() + AssertEx.Equal( + ((NamedTypeSymbol)compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMember("PublicClass")).GetMembers() .Select(m => m.ToTestDisplayString()), new[] { "void PublicClass.PublicMethod()", "void PublicClass.PrivateMethod()", - "void PublicClass.InternalMethod()", "void PublicClass.ProtectedMethod()", - "void PublicClass.AbstractMethod()", "PublicClass..ctor()" }); + "void PublicClass.ProtectedMethod()", "void PublicClass.InternalMethod()", + "PublicClass..ctor()" }); - AssertEx.SetEqual(assembly.GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + AssertEx.Equal(compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", "System.Diagnostics.DebuggableAttribute" }); - var peImage = comp.EmitToArray(emitRefOnly); - MetadataReaderUtils.AssertEmptyOrThrowNull(peImage); + MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitMetadataOnly)); + + // verify metadata (types, members, attributes) of the metadata-only assembly + var emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + CompileAndVerify(comp, emitOptions: emitRefOnly, verify: true); + + var refImage = comp.EmitToImageReference(emitRefOnly); + var compWithRef = CreateCompilation("", references: new[] { MscorlibRef, refImage }, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + AssertEx.Equal( + compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString()), + new[] { "", "PublicClass" }); + + AssertEx.Equal( + ((NamedTypeSymbol)compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMember("PublicClass")).GetMembers() + .Select(m => m.ToTestDisplayString()), + new[] { "void PublicClass.PublicMethod()", "void PublicClass.ProtectedMethod()", "PublicClass..ctor()" }); + + AssertEx.Equal(compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + new[] { + "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute", + "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" }); + + MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitRefOnly)); } + [Fact] public void EmitMetadataOnly_DisallowPdbs() { @@ -369,6 +708,20 @@ public void EmitMetadataOnly_DisallowMetadataPeStream() } } + [Fact] + public void IncludePrivateMembers_DisallowMetadataPeStream() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var metadataPeOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, metadataPEStream: metadataPeOutput, + options: EmitOptions.Default.WithIncludePrivateMembers(true))); + } + } + [Fact] public void EmitMetadata_DisallowOutputtingNetModule() { @@ -406,7 +759,7 @@ public void EmitMetadata_AllowEmbedding() using (var metadataOutput = new MemoryStream()) { var result = comp.Emit(output, metadataPEStream: metadataOutput, - options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded)); + options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded).WithIncludePrivateMembers(false)); VerifyEmbeddedDebugInfo(output, new[] { DebugDirectoryEntryType.CodeView, DebugDirectoryEntryType.EmbeddedPortablePdb }); VerifyEmbeddedDebugInfo(metadataOutput, new DebugDirectoryEntryType[] { }); @@ -523,7 +876,7 @@ public class Class1 : CppCli.CppBase2, CppCli.CppInterface1 var class1TypeDef = (Cci.ITypeDefinition)class1; var symbolSynthesized = class1.GetSynthesizedExplicitImplementations(CancellationToken.None); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var cciExplicit = class1TypeDef.GetExplicitImplementationOverrides(context); var cciMethods = class1TypeDef.GetMethods(context).Where(m => ((MethodSymbol)m).MethodKind != MethodKind.Constructor); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.cs index 373588419adbe..2c5bd19cf25a5 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.cs @@ -434,7 +434,7 @@ static void F() var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single(); Assert.Equal("<>c__DisplayClass0_0", displayClass.Name); - var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag()); + var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var fields = displayClass.GetFields(emitContext).ToArray(); var x1 = fields[0]; @@ -504,7 +504,7 @@ static void F() var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single(); Assert.Equal("<>c__DisplayClass0_0", displayClass.Name); - var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag()); + var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var fields = displayClass.GetFields(emitContext).ToArray(); AssertEx.SetEqual(fields.Select(f => f.Name), new[] { "x1", "x2" }); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs index 429024763cd1a..c6265c904fa5b 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadata.cs @@ -1371,7 +1371,7 @@ private void CheckEnumType(NamedTypeSymbol type, Accessibility declaredAccessibi var module = new PEAssemblyBuilder((SourceAssemblySymbol)sourceType.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var typeDefinition = (Microsoft.Cci.ITypeDefinition)type; var fieldDefinition = typeDefinition.GetFields(context).First(); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index 146816590c3bc..259f3b388e25b 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -2547,7 +2547,7 @@ private static void Main(string[] args) { } var extensionAttrCtor = (MethodSymbol)emitModule.Compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); Assert.NotNull(extensionAttrCtor); - var context = new EmitContext(emitModule, null, new DiagnosticBag()); + var context = new EmitContext(emitModule, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); Assert.Equal(extensionAttrCtor, attr.Constructor(context)); Assert.NotNull(extensionAttrCtor.ContainingType); Assert.Equal(extensionAttrCtor.ContainingType, attr.GetType(context)); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs index 97bea9381ebc4..f93efbd2d509b 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs @@ -1550,7 +1550,7 @@ class C : I2 { } var typeDef = (Cci.ITypeDefinition)@class; var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var cciInterfaces = typeDef.Interfaces(context) .Select(impl => impl.TypeRef).Cast().AsImmutable(); Assert.True(cciInterfaces.SetEquals(bothInterfaces, EqualityComparer.Default)); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index 753ae4e0faaaa..43a07570061b2 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -1571,7 +1571,7 @@ void I.Method() { } var typeDef = (Cci.ITypeDefinition)@class; var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverride = typeDef.GetExplicitImplementationOverrides(context).Single(); Assert.Equal(@class, explicitOverride.ContainingType); Assert.Equal(classMethod, explicitOverride.ImplementingMethod); @@ -1615,7 +1615,7 @@ string System.IFormattable.ToString(string format, System.IFormatProvider format var typeDef = (Cci.ITypeDefinition)@class; var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverride = typeDef.GetExplicitImplementationOverrides(context).Single(); Assert.Equal(@class, explicitOverride.ContainingType); Assert.Equal(classMethod, explicitOverride.ImplementingMethod); @@ -1662,7 +1662,7 @@ class C : I var typeDef = (Cci.ITypeDefinition)@class; var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverride = typeDef.GetExplicitImplementationOverrides(context).Single(); Assert.Equal(@class, explicitOverride.ContainingType); Assert.Equal(classMethod, explicitOverride.ImplementingMethod); @@ -1716,7 +1716,7 @@ void Namespace.I.Method(int i) { } var typeDef = (Cci.ITypeDefinition)@class; var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverride = typeDef.GetExplicitImplementationOverrides(context).Single(); Assert.Equal(@class, explicitOverride.ContainingType); Assert.Equal(classMethod, explicitOverride.ImplementingMethod); @@ -1857,7 +1857,7 @@ public class C : B var typeDefC = (Cci.ITypeDefinition)classC; var module = new PEAssemblyBuilder((SourceAssemblySymbol)classC.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverride = typeDefC.GetExplicitImplementationOverrides(context).Single(); Assert.Equal(classC, explicitOverride.ContainingType); Assert.Equal(methodC, explicitOverride.ImplementingMethod); @@ -1901,7 +1901,7 @@ public class C : B var typeDefC = (Cci.ITypeDefinition)classC; var module = new PEAssemblyBuilder((SourceAssemblySymbol)classC.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverride = typeDefC.GetExplicitImplementationOverrides(context).Single(); Assert.Equal(classC, explicitOverride.ContainingType); Assert.Equal(methodC, explicitOverride.ImplementingMethod); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs index 0a6e7a54b92ab..8ff49c87a5350 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/PropertyTests.cs @@ -2468,7 +2468,7 @@ private static void CheckPropertyExplicitImplementation(NamedTypeSymbol @class, var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverrides = typeDef.GetExplicitImplementationOverrides(context); Assert.Equal(2, explicitOverrides.Count()); Assert.True(explicitOverrides.All(@override => ReferenceEquals(@class, @override.ContainingType))); @@ -2506,7 +2506,7 @@ private static void CheckRefPropertyExplicitImplementation(NamedTypeSymbol @clas var module = new PEAssemblyBuilder((SourceAssemblySymbol)@class.ContainingAssembly, EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable()); - var context = new EmitContext(module, null, new DiagnosticBag()); + var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var explicitOverrides = typeDef.GetExplicitImplementationOverrides(context); Assert.Equal(1, explicitOverrides.Count()); Assert.True(explicitOverrides.All(@override => ReferenceEquals(@class, @override.ContainingType))); diff --git a/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs b/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs index 4ec5211154bb4..6aa7389beebe0 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs @@ -53,7 +53,7 @@ public void WithXxx() TestProperty((old, value) => old.WithOutputNameOverride(value), opt => opt.OutputNameOverride, @"x.dll"); TestProperty((old, value) => old.WithDebugInformationFormat(value), opt => opt.DebugInformationFormat, (DebugInformationFormat)2); TestProperty((old, value) => old.WithTolerateErrors(value), opt => opt.TolerateErrors, true); - TestProperty((old, value) => old.WithIncludePrivateMembers(value), opt => opt.IncludePrivateMembers, true); + TestProperty((old, value) => old.WithIncludePrivateMembers(value), opt => opt.IncludePrivateMembers, false); TestProperty((old, value) => old.WithInstrumentationKinds(value), opt => opt.InstrumentationKinds, ImmutableArray.Create(InstrumentationKind.TestCoverage)); } diff --git a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs index cdb32fa7adf59..1c2f34c70646a 100644 --- a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs +++ b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs @@ -10,10 +10,10 @@ namespace Microsoft.CodeAnalysis.BuildTasks { /// - /// If the source and destination ref assemblies are different, - /// this task copies the source over to the destination. + /// By default, this task copies the source over to the destination. + /// But if we're able to check that they are identical, the destination is left untouched. /// - public class CopyRefAssembly : Task + public sealed class CopyRefAssembly : Task { [Required] public string SourcePath { get; set; } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 376bf146c519b..9a38358b5b06a 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -604,6 +604,15 @@ internal static string IconStreamUnexpectedFormat { } } + /// + /// Looks up a localized string similar to Including private members should not be used when emitting to the secondary assembly output.. + /// + internal static string IncludingPrivateMembersUnexpectedWhenEmittingToMetadataPeStream { + get { + return ResourceManager.GetString("IncludingPrivateMembersUnexpectedWhenEmittingToMetadataPeStream", resourceCulture); + } + } + /// /// Looks up a localized string similar to Inconsistent language versions. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 54d4a21da6f75..5843e7ac09b72 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -336,6 +336,9 @@ Metadata PE stream should not be given when emitting metadata only. + + Including private members should not be used when emitting to the secondary assembly output. + Embedding PDB is not allowed when emitting metadata. diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs index 38feca8508a25..3691be8ca02ea 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -469,7 +469,7 @@ internal MappedField(string name, Cci.INamedTypeDefinition containingType, Cci.I /// internal abstract class DefaultTypeDef : Cci.ITypeDefinition { - public IEnumerable Events + public IEnumerable GetEvents(EmitContext context) => SpecializedCollections.EmptyEnumerable(); public IEnumerable GetExplicitImplementationOverrides(EmitContext context) diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs index 051f8e3c5b693..ad2bd964a5862 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @@ -659,6 +659,8 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella success = compilation.CompileMethods( moduleBeingBuilt, Arguments.EmitPdb, + emitOptions.EmitMetadataOnly, + emitOptions.EmitTestCoverageData, diagnosticBag, filterOpt: null, cancellationToken: cancellationToken); @@ -704,6 +706,7 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella moduleBeingBuilt, xmlStreamDisposerOpt?.Stream, win32ResourceStreamOpt, + emitOptions.OutputNameOverride, diagnosticBag, cancellationToken); } @@ -761,6 +764,9 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella testSymWriterFactory: null, diagnostics: diagnosticBag, metadataOnly: emitOptions.EmitMetadataOnly, + includePrivateMembers: emitOptions.IncludePrivateMembers, + emitTestCoverageData: emitOptions.EmitTestCoverageData, + pdbFilePath: emitOptions.PdbFilePath, cancellationToken: cancellationToken); } finally diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index daf5c278c980c..a550001f4e3d2 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1739,6 +1739,8 @@ internal abstract CommonPEModuleBuilder CreateModuleBuilder( internal abstract bool CompileMethods( CommonPEModuleBuilder moduleBuilder, bool emittingPdb, + bool emitMetadataOnly, + bool emitTestCoverageData, DiagnosticBag diagnostics, Predicate filterOpt, CancellationToken cancellationToken); @@ -1830,6 +1832,7 @@ internal abstract bool GenerateResourcesAndDocumentationComments( CommonPEModuleBuilder moduleBeingBuilt, Stream xmlDocumentationStream, Stream win32ResourcesStream, + string outputNameOverride, DiagnosticBag diagnostics, CancellationToken cancellationToken); @@ -1864,9 +1867,11 @@ internal bool Compile( return CompileMethods( moduleBuilder, emittingPdb, - diagnostics, - filterOpt, - cancellationToken); + emitMetadataOnly: false, + emitTestCoverageData: false, + diagnostics: diagnostics, + filterOpt: filterOpt, + cancellationToken: cancellationToken); } finally { @@ -2072,6 +2077,11 @@ public EmitResult Emit( throw new ArgumentException(CodeAnalysisResources.MetadataPeStreamUnexpectedWhenEmittingMetadataOnly, nameof(metadataPEStream)); } + if (metadataPEStream != null && options?.IncludePrivateMembers == true) + { + throw new ArgumentException(CodeAnalysisResources.IncludingPrivateMembersUnexpectedWhenEmittingToMetadataPeStream, nameof(metadataPEStream)); + } + if (options?.DebugInformationFormat == DebugInformationFormat.Embedded && options?.EmitMetadataOnly == true) { @@ -2156,8 +2166,10 @@ internal EmitResult Emit( CompilationTestData testData, CancellationToken cancellationToken) { - bool embedPdb = options?.DebugInformationFormat == DebugInformationFormat.Embedded; + options = options ?? EmitOptions.Default.WithIncludePrivateMembers(metadataPEStream == null); + bool embedPdb = options.DebugInformationFormat == DebugInformationFormat.Embedded; Debug.Assert(!embedPdb || pdbStream == null); + Debug.Assert(metadataPEStream == null || !options.IncludePrivateMembers); // you may not use a secondary stream and include private members together var diagnostics = DiagnosticBag.GetInstance(); @@ -2180,16 +2192,19 @@ internal EmitResult Emit( success = CompileMethods( moduleBeingBuilt, emittingPdb: pdbStream != null || embedPdb, + emitMetadataOnly: options.EmitMetadataOnly, + emitTestCoverageData: options.EmitTestCoverageData, diagnostics: diagnostics, filterOpt: null, cancellationToken: cancellationToken); - if (!moduleBeingBuilt.EmitOptions.EmitMetadataOnly) + if (!options.EmitMetadataOnly) { if (!GenerateResourcesAndDocumentationComments( moduleBeingBuilt, xmlDocumentationStream, win32Resources, + options.OutputNameOverride, diagnostics, cancellationToken)) { @@ -2216,7 +2231,10 @@ internal EmitResult Emit( (pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null, testData?.SymWriterFactory, diagnostics, - metadataOnly: (options != null) && options.EmitMetadataOnly, + metadataOnly: options.EmitMetadataOnly, + includePrivateMembers: options.IncludePrivateMembers, + emitTestCoverageData: options.EmitTestCoverageData, + pdbFilePath: options.PdbFilePath, cancellationToken: cancellationToken); } } @@ -2321,14 +2339,7 @@ internal CommonPEModuleBuilder CheckOptionsAndCreateModuleBuilder( CompilationTestData testData, CancellationToken cancellationToken) { - if (options != null) - { - options.ValidateOptions(diagnostics, this.MessageProvider); - } - else - { - options = EmitOptions.Default; - } + options.ValidateOptions(diagnostics, this.MessageProvider); if (debugEntryPoint != null) { @@ -2384,6 +2395,9 @@ internal bool SerializeToPeStream( Func testSymWriterFactory, DiagnosticBag diagnostics, bool metadataOnly, + bool includePrivateMembers, + bool emitTestCoverageData, + string pdbFilePath, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -2397,13 +2411,12 @@ internal bool SerializeToPeStream( Stream portablePdbStream = null; bool deterministic = IsEmitDeterministic; - var debugFormat = moduleBeingBuilt.EmitOptions.DebugInformationFormat; // PDB Stream provider should not be given if PDB is to be embedded into the PE file: - Debug.Assert(debugFormat != DebugInformationFormat.Embedded || pdbStreamProvider == null); + Debug.Assert(moduleBeingBuilt.DebugInformationFormat != DebugInformationFormat.Embedded || pdbStreamProvider == null); - string pdbPath = (pdbStreamProvider != null || debugFormat == DebugInformationFormat.Embedded) ? - (moduleBeingBuilt.EmitOptions.PdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb")) : null; + string pdbPath = (pdbStreamProvider != null || moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.Embedded) ? + (pdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb")) : null; // The PDB path is emitted in it's entirety into the PE. This makes it impossible to have deterministic // builds that occur in different source directories. To enable this we shave all path information from @@ -2413,7 +2426,7 @@ internal bool SerializeToPeStream( // tracks getting an official solution here. // // https://github.com/dotnet/roslyn/issues/9813 - string pePdbPath = (debugFormat == DebugInformationFormat.Embedded || Feature("pdb-path-determinism") != null) && !string.IsNullOrEmpty(pdbPath) + string pePdbPath = (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.Embedded || Feature("pdb-path-determinism") != null) && !string.IsNullOrEmpty(pdbPath) ? Path.GetFileName(pdbPath) : pdbPath; @@ -2421,7 +2434,7 @@ internal bool SerializeToPeStream( { metadataDiagnostics = DiagnosticBag.GetInstance(); - if (debugFormat == DebugInformationFormat.Pdb && pdbStreamProvider != null) + if (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.Pdb && pdbStreamProvider != null) { // The calls ISymUnmanagedWriter2.GetDebugInfo require a file name in order to succeed. This is // frequently used during PDB writing. Ensure a name is provided here in the case we were given @@ -2430,7 +2443,7 @@ internal bool SerializeToPeStream( } Func getPortablePdbStream; - if (debugFormat == DebugInformationFormat.PortablePdb && pdbStreamProvider != null) + if (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.PortablePdb && pdbStreamProvider != null) { getPortablePdbStream = () => { @@ -2515,7 +2528,8 @@ internal bool SerializeToPeStream( try { if (SerializePeToStream( - new EmitContext(moduleBeingBuilt, null, metadataDiagnostics), + moduleBeingBuilt, + metadataDiagnostics, this.MessageProvider, getPeStream, getRefPeStream, @@ -2523,7 +2537,9 @@ internal bool SerializeToPeStream( nativePdbWriter, pePdbPath, metadataOnly, + includePrivateMembers, deterministic, + emitTestCoverageData, cancellationToken)) { if (nativePdbWriter != null) @@ -2598,19 +2614,25 @@ internal bool SerializeToPeStream( } internal static bool SerializePeToStream( - EmitContext context, - CommonMessageProvider messageProvider, - Func getPeStream, - Func getMetadataPeStreamOpt, - Func getPortablePdbStreamOpt, - Cci.PdbWriter nativePdbWriterOpt, - string pdbPathOpt, - bool metadataOnly, - bool isDeterministic, - CancellationToken cancellationToken) + CommonPEModuleBuilder moduleBeingBuilt, + DiagnosticBag metadataDiagnostics, + CommonMessageProvider messageProvider, + Func getPeStream, + Func getMetadataPeStreamOpt, + Func getPortablePdbStreamOpt, + Cci.PdbWriter nativePdbWriterOpt, + string pdbPathOpt, + bool metadataOnly, + bool includePrivateMembers, + bool isDeterministic, + bool emitTestCoverageData, + CancellationToken cancellationToken) { + bool emitSecondaryAssembly = getMetadataPeStreamOpt != null; + + bool includePrivateMembersOnPrimaryOutput = metadataOnly ? includePrivateMembers : true; if (!Cci.PeWriter.WritePeToStream( - context, + new EmitContext(moduleBeingBuilt, null, metadataDiagnostics, metadataOnly, includePrivateMembersOnPrimaryOutput), messageProvider, getPeStream, getPortablePdbStreamOpt, @@ -2618,18 +2640,20 @@ internal static bool SerializePeToStream( pdbPathOpt, metadataOnly, isDeterministic, + emitTestCoverageData, cancellationToken)) { return false; } // produce the secondary output (ref assembly) if needed - if (getMetadataPeStreamOpt != null) + if (emitSecondaryAssembly) { Debug.Assert(!metadataOnly); + Debug.Assert(!includePrivateMembers); if (!Cci.PeWriter.WritePeToStream( - context, + new EmitContext(moduleBeingBuilt, null, metadataDiagnostics, metadataOnly: true, includePrivateMembers: false), messageProvider, getMetadataPeStreamOpt, getPortablePdbStreamOpt: null, @@ -2637,6 +2661,7 @@ internal static bool SerializePeToStream( pdbPathOpt: null, metadataOnly: true, isDeterministic: isDeterministic, + emitTestCoverageData: false, cancellationToken: cancellationToken)) { return false; @@ -2657,17 +2682,18 @@ internal EmitBaseline SerializeToDeltaStreams( ICollection updatedMethods, DiagnosticBag diagnostics, Func testSymWriterFactory, + string pdbFilePath, CancellationToken cancellationToken) { - var nativePdbWriterOpt = (moduleBeingBuilt.EmitOptions.DebugInformationFormat != DebugInformationFormat.Pdb) ? null : + var nativePdbWriterOpt = (moduleBeingBuilt.DebugInformationFormat != DebugInformationFormat.Pdb) ? null : new Cci.PdbWriter( - moduleBeingBuilt.EmitOptions.PdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"), + pdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"), testSymWriterFactory, deterministic: false); using (nativePdbWriterOpt) { - var context = new EmitContext(moduleBeingBuilt, null, diagnostics); + var context = new EmitContext(moduleBeingBuilt, null, diagnostics, metadataOnly: false, includePrivateMembers: true); var encId = Guid.NewGuid(); try diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index cb1bc5c85f79a..bdf7c957645d5 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -19,7 +19,6 @@ internal abstract class CommonPEModuleBuilder : Cci.IUnit, Cci.IModuleReference { internal readonly DebugDocumentsBuilder DebugDocumentsBuilder; internal readonly IEnumerable ManifestResources; - internal readonly EmitOptions EmitOptions; internal readonly Cci.ModulePropertiesForSerialization SerializationProperties; internal readonly OutputKind OutputKind; internal IEnumerable Win32Resources; @@ -41,6 +40,8 @@ internal abstract class CommonPEModuleBuilder : Cci.IUnit, Cci.IModuleReference // Only set when running tests to allow realized IL for a given method to be looked up by method. internal ConcurrentDictionary TestData { get; private set; } + internal readonly DebugInformationFormat DebugInformationFormat; + public CommonPEModuleBuilder( IEnumerable manifestResources, EmitOptions emitOptions, @@ -49,16 +50,15 @@ public CommonPEModuleBuilder( Compilation compilation) { Debug.Assert(manifestResources != null); - Debug.Assert(emitOptions != null); Debug.Assert(serializationProperties != null); Debug.Assert(compilation != null); - EmitOptions = emitOptions; ManifestResources = manifestResources; DebugDocumentsBuilder = new DebugDocumentsBuilder(compilation.Options.SourceReferenceResolver, compilation.IsCaseSensitive); OutputKind = outputKind; SerializationProperties = serializationProperties; _methodBodyMap = new ConcurrentDictionary(ReferenceEqualityComparer.Instance); + DebugInformationFormat = emitOptions.DebugInformationFormat; } /// @@ -80,7 +80,7 @@ public CommonPEModuleBuilder( internal abstract Cci.ITypeReference Translate(ITypeSymbol symbol, SyntaxNode syntaxOpt, DiagnosticBag diagnostics); internal abstract Cci.IMethodReference Translate(IMethodSymbol symbol, DiagnosticBag diagnostics, bool needDeclaration); internal abstract bool SupportsPrivateImplClass { get; } - internal abstract ImmutableArray GetAnonymousTypes(); + internal abstract ImmutableArray GetAnonymousTypes(EmitContext context); internal abstract Compilation CommonCompilation { get; } internal abstract IModuleSymbol CommonSourceModule { get; } internal abstract IAssemblySymbol CommonCorLibrary { get; } @@ -89,7 +89,7 @@ public CommonPEModuleBuilder( internal abstract ImmutableDictionary> GetSynthesizedMembers(); internal abstract CommonEmbeddedTypesManager CommonEmbeddedTypesManagerOpt { get; } internal abstract Cci.ITypeReference EncTranslateType(ITypeSymbol type, DiagnosticBag diagnostics); - public abstract IEnumerable GetSourceAssemblyAttributes(); + public abstract IEnumerable GetSourceAssemblyAttributes(bool isRefAssembly); public abstract IEnumerable GetSourceAssemblySecurityAttributes(); public abstract IEnumerable GetSourceModuleAttributes(); internal abstract Cci.ICustomAttribute SynthesizeAttribute(WellKnownMember attributeConstructor); @@ -247,7 +247,7 @@ public uint GetFakeSymbolTokenForIL(Cci.IReference symbol, SyntaxNode syntaxNode uint token = _referencesInILMap.GetOrAddTokenFor(symbol, out added); if (added) { - ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics)); + ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true)); // PROTOTYPE(refout) Not sure about this } return token; } @@ -499,7 +499,7 @@ protected bool ContainsTopLevelType(string fullEmittedName) VisitTopLevelType(noPiaIndexer, _rootModuleType); yield return _rootModuleType; - foreach (var type in this.GetAnonymousTypes()) + foreach (var type in this.GetAnonymousTypes(context)) { AddTopLevelType(names, type); VisitTopLevelType(noPiaIndexer, type); diff --git a/src/Compilers/Core/Portable/Emit/Context.cs b/src/Compilers/Core/Portable/Emit/Context.cs index 494c8ddb48ed5..9d608ff6bdf49 100644 --- a/src/Compilers/Core/Portable/Emit/Context.cs +++ b/src/Compilers/Core/Portable/Emit/Context.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Diagnostics; namespace Microsoft.CodeAnalysis.Emit @@ -9,15 +10,40 @@ internal struct EmitContext public readonly CommonPEModuleBuilder Module; public readonly SyntaxNode SyntaxNodeOpt; public readonly DiagnosticBag Diagnostics; + private readonly Flags _flags; - public EmitContext(CommonPEModuleBuilder module, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics) + public bool IncludePrivateMembers => (_flags & Flags.IncludePrivateMembers) != 0; + public bool MetadataOnly => (_flags & Flags.MetadataOnly) != 0; + public bool IsRefAssembly => MetadataOnly && !IncludePrivateMembers; + + public EmitContext(CommonPEModuleBuilder module, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics, bool metadataOnly, bool includePrivateMembers) { Debug.Assert(module != null); Debug.Assert(diagnostics != null); + Debug.Assert(includePrivateMembers || metadataOnly); Module = module; SyntaxNodeOpt = syntaxNodeOpt; Diagnostics = diagnostics; + + Flags flags = Flags.None; + if (metadataOnly) + { + flags |= Flags.MetadataOnly; + } + if (includePrivateMembers) + { + flags |= Flags.IncludePrivateMembers; + } + _flags = flags; + } + + [Flags] + private enum Flags + { + None = 0, + MetadataOnly = 1, + IncludePrivateMembers = 2, } } } diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs index 7c8b2fdb0ec67..360cef3af8267 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs @@ -50,19 +50,20 @@ public DeltaMetadataWriter( DefinitionMap definitionMap, SymbolChanges changes, CancellationToken cancellationToken) - : base(metadata: MakeTablesBuilder(previousGeneration), - debugMetadataOpt: (context.Module.EmitOptions.DebugInformationFormat == DebugInformationFormat.PortablePdb) ? new MetadataBuilder() : null, + : base(metadata: MakeTablesBuilder(previousGeneration), + debugMetadataOpt: (context.Module.DebugInformationFormat == DebugInformationFormat.PortablePdb) ? new MetadataBuilder() : null, dynamicAnalysisDataWriterOpt: null, context: context, messageProvider: messageProvider, - metadataOnly: false, + metadataOnly: false, deterministic: false, + emitTestCoverageData: false, cancellationToken: cancellationToken) { Debug.Assert(previousGeneration != null); Debug.Assert(encId != default(Guid)); Debug.Assert(encId != previousGeneration.EncId); - Debug.Assert(context.Module.EmitOptions.DebugInformationFormat != DebugInformationFormat.Embedded); + Debug.Assert(context.Module.DebugInformationFormat != DebugInformationFormat.Embedded); _previousGeneration = previousGeneration; _encId = encId; @@ -486,7 +487,7 @@ protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef) var ok = _typeDefs.TryGetValue(typeDef, out typeIndex); Debug.Assert(ok); - foreach (var eventDef in typeDef.Events) + foreach (var eventDef in typeDef.GetEvents(this.Context)) { int eventMapIndex; if (!_eventMap.TryGetValue(typeIndex, out eventMapIndex)) diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs index 1cbacb1b0cb9f..4e43a6e9abb77 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs @@ -188,7 +188,7 @@ private SymbolChange GetChange(ISymbol symbol) public IEnumerable GetTopLevelTypes(EmitContext context) { var module = (CommonPEModuleBuilder)context.Module; - foreach (var type in module.GetAnonymousTypes()) + foreach (var type in module.GetAnonymousTypes(context)) { yield return type; } diff --git a/src/Compilers/Core/Portable/Emit/EmitOptions.cs b/src/Compilers/Core/Portable/Emit/EmitOptions.cs index 4b64674b709b6..28d1a900b011b 100644 --- a/src/Compilers/Core/Portable/Emit/EmitOptions.cs +++ b/src/Compilers/Core/Portable/Emit/EmitOptions.cs @@ -26,11 +26,9 @@ public sealed class EmitOptions : IEquatable /// /// Unless set (private) members that don't affect the language semantics of the resulting assembly will be excluded - /// when emitting with on. + /// when emitting metadata-only assemblies as primary output (with on). + /// If emitting a secondary output, this flag is required to be false. /// - /// - /// Has no effect when is false. - /// public bool IncludePrivateMembers { get; private set; } /// @@ -133,7 +131,7 @@ public EmitOptions( SubsystemVersion subsystemVersion = default(SubsystemVersion), string runtimeMetadataVersion = null, bool tolerateErrors = false, - bool includePrivateMembers = false, + bool includePrivateMembers = true, ImmutableArray instrumentationKinds = default(ImmutableArray)) { this.EmitMetadataOnly = metadataOnly; diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs index 0b93a04a61711..704346c9a7d64 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs @@ -232,31 +232,28 @@ Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context) return GetBaseClass((TPEModuleBuilder)context.Module, (TSyntaxNode)context.SyntaxNodeOpt, context.Diagnostics); } - IEnumerable Cci.ITypeDefinition.Events + IEnumerable Cci.ITypeDefinition.GetEvents(EmitContext context) { - get + if (_lazyEvents.IsDefault) { - if (_lazyEvents.IsDefault) - { - Debug.Assert(TypeManager.IsFrozen); + Debug.Assert(TypeManager.IsFrozen); - var builder = ArrayBuilder.GetInstance(); + var builder = ArrayBuilder.GetInstance(); - foreach (var e in GetEventsToEmit()) - { - TEmbeddedEvent embedded; + foreach (var e in GetEventsToEmit()) + { + TEmbeddedEvent embedded; - if (TypeManager.EmbeddedEventsMap.TryGetValue(e, out embedded)) - { - builder.Add(embedded); - } + if (TypeManager.EmbeddedEventsMap.TryGetValue(e, out embedded)) + { + builder.Add(embedded); } - - ImmutableInterlocked.InterlockedInitialize(ref _lazyEvents, builder.ToImmutableAndFree()); } - return _lazyEvents; + ImmutableInterlocked.InterlockedInitialize(ref _lazyEvents, builder.ToImmutableAndFree()); } + + return _lazyEvents; } IEnumerable Cci.ITypeDefinition.GetExplicitImplementationOverrides(EmitContext context) diff --git a/src/Compilers/Core/Portable/Emit/NoPia/EmbeddedTypesManager.cs b/src/Compilers/Core/Portable/Emit/NoPia/EmbeddedTypesManager.cs index 3dcaa30aaee8e..f7f45f8f87ffd 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/EmbeddedTypesManager.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/EmbeddedTypesManager.cs @@ -189,7 +189,7 @@ public int Compare(TEmbeddedType x, TEmbeddedType y) protected void EmbedReferences(Cci.ITypeDefinitionMember embeddedMember, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics) { - var noPiaIndexer = new Cci.NoPiaReferenceIndexer(new EmitContext(ModuleBeingBuilt, syntaxNodeOpt, diagnostics)); + var noPiaIndexer = new Cci.NoPiaReferenceIndexer(new EmitContext(ModuleBeingBuilt, syntaxNodeOpt, diagnostics, metadataOnly: false, includePrivateMembers: true)); noPiaIndexer.Visit(embeddedMember); } diff --git a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs index 11cfb2027b637..4ceb1ad3c7f29 100644 --- a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs @@ -39,12 +39,13 @@ public static MetadataWriter Create( CommonMessageProvider messageProvider, bool metadataOnly, bool deterministic, + bool emitTestCoverageData, bool hasPdbStream, CancellationToken cancellationToken) { var builder = new MetadataBuilder(); MetadataBuilder debugBuilderOpt; - switch (context.Module.EmitOptions.DebugInformationFormat) + switch (context.Module.DebugInformationFormat) { case DebugInformationFormat.PortablePdb: debugBuilderOpt = hasPdbStream ? new MetadataBuilder() : null; @@ -59,11 +60,12 @@ public static MetadataWriter Create( break; } - var dynamicAnalysisDataWriterOpt = context.Module.EmitOptions.EmitTestCoverageData ? - new DynamicAnalysisDataWriter(context.Module.DebugDocumentCount, context.Module.HintNumberOfMethodDefinitions) : + var dynamicAnalysisDataWriterOpt = emitTestCoverageData ? + new DynamicAnalysisDataWriter(context.Module.DebugDocumentCount, context.Module.HintNumberOfMethodDefinitions) : null; - return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, metadataOnly, deterministic, cancellationToken); + return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, metadataOnly, deterministic, + emitTestCoverageData, cancellationToken); } private FullMetadataWriter( @@ -74,8 +76,10 @@ private FullMetadataWriter( CommonMessageProvider messageProvider, bool metadataOnly, bool deterministic, + bool emitTestCoverageData, CancellationToken cancellationToken) - : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, metadataOnly, deterministic, cancellationToken) + : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, metadataOnly, deterministic, + emitTestCoverageData, cancellationToken) { // EDMAURER make some intelligent guesses for the initial sizes of these things. int numMethods = this.module.HintNumberOfMethodDefinitions; @@ -390,7 +394,7 @@ protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef) this.methodImplList.Add(methodImplementation); } - foreach (IEventDefinition eventDef in typeDef.Events) + foreach (IEventDefinition eventDef in typeDef.GetEvents(Context)) { _eventDefs.Add(eventDef); } diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index 8dd6fcb535b50..4c0ffc23e73b7 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -979,5 +979,27 @@ internal static bool HasBody(this IMethodDefinition methodDef) return !methodDef.IsAbstract && !methodDef.IsExternal && (methodDef.ContainingTypeDefinition == null || !methodDef.ContainingTypeDefinition.IsComObject); } + + /// + /// When emitting ref assemblies, some members will not be included. + /// + public static bool ShouldInclude(this ITypeDefinitionMember member, EmitContext context) + { + var method = member as IMethodDefinition; + if (method != null && method.IsVirtual) + { + return true; + } + + switch (member.Visibility) + { + case TypeMemberVisibility.Private: + return context.IncludePrivateMembers; + case TypeMemberVisibility.Assembly: + case TypeMemberVisibility.FamilyAndAssembly: + return context.IncludePrivateMembers || context.Module.SourceAssemblyOpt?.InternalsAreVisible == true; + } + return true; + } } } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index aeeb574e8dc7b..930a5c7d8cdec 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -66,6 +66,7 @@ internal abstract partial class MetadataWriter private readonly bool _deterministic; internal readonly bool MetadataOnly; + internal readonly bool EmitTestCoverageData; // A map of method body before token translation to RVA. Used for deduplication of small bodies. private readonly Dictionary, int> _smallMethodBodies; @@ -86,11 +87,13 @@ protected MetadataWriter( CommonMessageProvider messageProvider, bool metadataOnly, bool deterministic, + bool emitTestCoverageData, CancellationToken cancellationToken) { this.module = context.Module; _deterministic = deterministic; this.MetadataOnly = metadataOnly; + this.EmitTestCoverageData = emitTestCoverageData; // EDMAURER provide some reasonable size estimates for these that will avoid // much of the reallocation that would occur when growing these from empty. @@ -506,7 +509,7 @@ private void CreateUserStringIndices() private void CreateIndicesForModule() { - var nestedTypes = new Queue(); + var nestedTypes = new Queue(); foreach (INamespaceTypeDefinition typeDef in this.GetTopLevelTypes(this.module)) { @@ -515,7 +518,8 @@ private void CreateIndicesForModule() while (nestedTypes.Count > 0) { - this.CreateIndicesFor(nestedTypes.Dequeue(), nestedTypes); + var nestedType = nestedTypes.Dequeue(); + this.CreateIndicesFor(nestedType, nestedTypes); } } @@ -523,7 +527,7 @@ protected virtual void OnIndicesCreated() { } - private void CreateIndicesFor(ITypeDefinition typeDef, Queue nestedTypes) + private void CreateIndicesFor(ITypeDefinition typeDef, Queue nestedTypes) { _cancellationToken.ThrowIfCancellationRequested(); @@ -1790,9 +1794,16 @@ public void BuildMetadataAndIL( } } - int[] methodBodyOffsets = MetadataOnly ? - SerializeThrowNullMethodBodies(ilBuilder, out mvidStringFixup) : - SerializeMethodBodies(ilBuilder, nativePdbWriterOpt, out mvidStringFixup); + int[] methodBodyOffsets; + if (MetadataOnly) + { + methodBodyOffsets = SerializeThrowNullMethodBodies(ilBuilder); + mvidStringFixup = default(Blob); + } + else + { + methodBodyOffsets = SerializeMethodBodies(ilBuilder, nativePdbWriterOpt, out mvidStringFixup); + } _cancellationToken.ThrowIfCancellationRequested(); @@ -2011,7 +2022,7 @@ private void AddAssemblyAttributesToTable() } AddAssemblyAttributesToTable( - this.module.GetSourceAssemblyAttributes(), + this.module.GetSourceAssemblyAttributes(Context.IsRefAssembly), needsDummyParent: writingNetModule, isSecurity: false); } @@ -2815,8 +2826,9 @@ private void PopulateStandaloneSignatures() } } - private int[] SerializeThrowNullMethodBodies(BlobBuilder ilBuilder, out Blob mvidStringFixup) + private int[] SerializeThrowNullMethodBodies(BlobBuilder ilBuilder) { + Debug.Assert(MetadataOnly); var methods = this.GetMethodDefs(); int[] bodyOffsets = new int[methods.Count]; @@ -2840,7 +2852,6 @@ private int[] SerializeThrowNullMethodBodies(BlobBuilder ilBuilder, out Blob mvi methodRid++; } - mvidStringFixup = default(Blob); return bodyOffsets; } diff --git a/src/Compilers/Core/Portable/PEWriter/NoPiaReferenceIndexer.cs b/src/Compilers/Core/Portable/PEWriter/NoPiaReferenceIndexer.cs index 22fadb61bf7f3..a740dc8c29c33 100644 --- a/src/Compilers/Core/Portable/PEWriter/NoPiaReferenceIndexer.cs +++ b/src/Compilers/Core/Portable/PEWriter/NoPiaReferenceIndexer.cs @@ -23,7 +23,7 @@ public override void Visit(CommonPEModuleBuilder module) //EDMAURER visit these assembly-level attributes even when producing a module. //They'll be attached off the "AssemblyAttributesGoHere" typeRef if a module is being produced. - this.Visit(module.GetSourceAssemblyAttributes()); + this.Visit(module.GetSourceAssemblyAttributes(Context.IsRefAssembly)); this.Visit(module.GetSourceAssemblySecurityAttributes()); this.Visit(module.GetSourceModuleAttributes()); } diff --git a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs index 82fecdd4e60c6..cd9011bb617ab 100644 --- a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs @@ -35,12 +35,14 @@ internal static bool WritePeToStream( string pdbPathOpt, bool metadataOnly, bool isDeterministic, + bool emitTestCoverageData, CancellationToken cancellationToken) { // If PDB writer is given, we have to have PDB path. Debug.Assert(nativePdbWriterOpt == null || pdbPathOpt != null); - var mdWriter = FullMetadataWriter.Create(context, messageProvider, metadataOnly, isDeterministic, getPortablePdbStreamOpt != null, cancellationToken); + var mdWriter = FullMetadataWriter.Create(context, messageProvider, metadataOnly, isDeterministic, + emitTestCoverageData, getPortablePdbStreamOpt != null, cancellationToken); var properties = context.Module.SerializationProperties; diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs index b871a1565f161..b0f12a11c3b1a 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs @@ -23,7 +23,7 @@ public override void Visit(CommonPEModuleBuilder module) { // Visit these assembly-level attributes even when producing a module. // They'll be attached off the "AssemblyAttributesGoHere" typeRef if a module is being produced. - Visit(module.GetSourceAssemblyAttributes()); + Visit(module.GetSourceAssemblyAttributes(Context.IsRefAssembly)); Visit(module.GetSourceAssemblySecurityAttributes()); Visit(module.GetAssemblyReferences(Context)); diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs index 9287bcecffc2a..887671429c747 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs @@ -279,7 +279,7 @@ public override void Visit(ITypeDefinition typeDefinition) { VisitTypeDefinitionNoMembers(typeDefinition); - this.Visit(typeDefinition.Events); + this.Visit(typeDefinition.GetEvents(Context)); this.Visit(typeDefinition.GetFields(Context)); this.Visit(typeDefinition.GetMethods(Context)); this.VisitNestedTypes(typeDefinition.GetNestedTypes(Context)); diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs index 6efd067885c4f..a5e256bbbac34 100644 --- a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs @@ -49,9 +49,9 @@ public ITypeReference GetBaseClass(EmitContext context) return null; } - public IEnumerable Events + public IEnumerable GetEvents(EmitContext context) { - get { return SpecializedCollections.EmptyEnumerable(); } + return SpecializedCollections.EmptyEnumerable(); } public IEnumerable GetExplicitImplementationOverrides(EmitContext context) diff --git a/src/Compilers/Core/Portable/PEWriter/Types.cs b/src/Compilers/Core/Portable/PEWriter/Types.cs index bc6634c760bbd..841fbacf758b2 100644 --- a/src/Compilers/Core/Portable/PEWriter/Types.cs +++ b/src/Compilers/Core/Portable/PEWriter/Types.cs @@ -433,7 +433,7 @@ internal interface ITypeDefinition : IDefinition, ITypeReference /// /// Zero or more events defined by this type. /// - IEnumerable Events { get; } + IEnumerable GetEvents(EmitContext context); /// /// Zero or more implementation overrides provided by the class. diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index d3935e7a9c429..b696246816cd8 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -80,7 +80,7 @@ Microsoft.CodeAnalysis.EmbeddedText Microsoft.CodeAnalysis.EmbeddedText.Checksum.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.EmbeddedText.ChecksumAlgorithm.get -> Microsoft.CodeAnalysis.Text.SourceHashAlgorithm Microsoft.CodeAnalysis.EmbeddedText.FilePath.get -> string -Microsoft.CodeAnalysis.Emit.EmitOptions.EmitOptions(bool metadataOnly = false, Microsoft.CodeAnalysis.Emit.DebugInformationFormat debugInformationFormat = (Microsoft.CodeAnalysis.Emit.DebugInformationFormat)0, string pdbFilePath = null, string outputNameOverride = null, int fileAlignment = 0, ulong baseAddress = 0, bool highEntropyVirtualAddressSpace = false, Microsoft.CodeAnalysis.SubsystemVersion subsystemVersion = default(Microsoft.CodeAnalysis.SubsystemVersion), string runtimeMetadataVersion = null, bool tolerateErrors = false, bool includePrivateMembers = false, System.Collections.Immutable.ImmutableArray instrumentationKinds = default(System.Collections.Immutable.ImmutableArray)) -> void +Microsoft.CodeAnalysis.Emit.EmitOptions.EmitOptions(bool metadataOnly = false, Microsoft.CodeAnalysis.Emit.DebugInformationFormat debugInformationFormat = (Microsoft.CodeAnalysis.Emit.DebugInformationFormat)0, string pdbFilePath = null, string outputNameOverride = null, int fileAlignment = 0, ulong baseAddress = 0, bool highEntropyVirtualAddressSpace = false, Microsoft.CodeAnalysis.SubsystemVersion subsystemVersion = default(Microsoft.CodeAnalysis.SubsystemVersion), string runtimeMetadataVersion = null, bool tolerateErrors = false, bool includePrivateMembers = true, System.Collections.Immutable.ImmutableArray instrumentationKinds = default(System.Collections.Immutable.ImmutableArray)) -> void Microsoft.CodeAnalysis.Emit.EmitOptions.EmitOptions(bool metadataOnly, Microsoft.CodeAnalysis.Emit.DebugInformationFormat debugInformationFormat, string pdbFilePath, string outputNameOverride, int fileAlignment, ulong baseAddress, bool highEntropyVirtualAddressSpace, Microsoft.CodeAnalysis.SubsystemVersion subsystemVersion, string runtimeMetadataVersion, bool tolerateErrors, bool includePrivateMembers) -> void Microsoft.CodeAnalysis.Emit.EmitOptions.InstrumentationKinds.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.EmitOptions.WithInstrumentationKinds(System.Collections.Immutable.ImmutableArray instrumentationKinds) -> Microsoft.CodeAnalysis.Emit.EmitOptions diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index 7e2d9840a954c..d8afa2d0cbf89 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -322,6 +322,8 @@ static AttributeDescription() s_signature_HasThis_Void_CompilationRelaxations }; + private static readonly byte[][] s_signaturesOfReferenceAssemblyAttribute = { s_signature_HasThis_Void }; + private static readonly byte[][] s_signaturesOfDebuggableAttribute = { s_signature_HasThis_Void_Boolean_Boolean, @@ -474,6 +476,7 @@ static AttributeDescription() internal static readonly AttributeDescription AsyncStateMachineAttribute = new AttributeDescription("System.Runtime.CompilerServices", "AsyncStateMachineAttribute", s_signaturesOfAsyncStateMachineAttribute); internal static readonly AttributeDescription IteratorStateMachineAttribute = new AttributeDescription("System.Runtime.CompilerServices", "IteratorStateMachineAttribute", s_signaturesOfIteratorStateMachineAttribute); internal static readonly AttributeDescription CompilationRelaxationsAttribute = new AttributeDescription("System.Runtime.CompilerServices", "CompilationRelaxationsAttribute", s_signaturesOfCompilationRelaxationsAttribute); + internal static readonly AttributeDescription ReferenceAssemblyAttribute = new AttributeDescription("System.Runtime.CompilerServices", "ReferenceAssemblyAttribute", s_signaturesOfReferenceAssemblyAttribute); internal static readonly AttributeDescription RuntimeCompatibilityAttribute = new AttributeDescription("System.Runtime.CompilerServices", "RuntimeCompatibilityAttribute", s_signaturesOfRuntimeCompatibilityAttribute); internal static readonly AttributeDescription DebuggableAttribute = new AttributeDescription("System.Diagnostics", "DebuggableAttribute", s_signaturesOfDebuggableAttribute); internal static readonly AttributeDescription TypeForwardedToAttribute = new AttributeDescription("System.Runtime.CompilerServices", "TypeForwardedToAttribute", s_signaturesOfTypeForwardedToAttribute); diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs index e68997e7d2062..1d71fc6009859 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs @@ -323,6 +323,24 @@ public bool HasCompilationRelaxationsAttribute } #endregion + #region ReferenceAssemblyeAttribute + private bool _hasReferenceAssemblyAttribute; + public bool HasReferenceAssemblyAttribute + { + get + { + VerifySealed(expected: true); + return _hasReferenceAssemblyAttribute; + } + set + { + VerifySealed(expected: false); + _hasReferenceAssemblyAttribute = value; + SetDataStored(); + } + } + #endregion + #region RuntimeCompatibilityAttribute private bool? _runtimeCompatibilityWrapNonExceptionThrows; diff --git a/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbolInternal.cs b/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbolInternal.cs index e2c779d15062a..d754d4c3c2543 100644 --- a/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbolInternal.cs +++ b/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbolInternal.cs @@ -17,5 +17,7 @@ internal interface ISourceAssemblySymbolInternal : ISourceAssemblySymbol AssemblyHashAlgorithm HashAlgorithm { get; } Version AssemblyVersionPattern { get; } + + bool InternalsAreVisible { get; } } } diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 341ed960772ca..3b054a306fcbc 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -415,6 +415,7 @@ internal enum WellKnownMember System_String__Format_IFormatProvider, Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayload, + System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, Count } diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 8cac8670c4e3c..9a972169f8329 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -2878,6 +2878,13 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, (byte)SignatureTypeCode.ByReference, (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor + (byte)MemberFlags.Constructor, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_ReferenceAssemblyAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, }; string[] allNames = new string[(int)WellKnownMember.Count] @@ -3236,6 +3243,8 @@ static WellKnownMembers() "Format", // System_String__Format_IFormatProvider "CreatePayload", // Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayload + + ".ctor", // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 5641cd6a02dd9..6a59f6c763626 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -263,6 +263,7 @@ internal enum WellKnownType System_Runtime_CompilerServices_TupleElementNamesAttribute, Microsoft_CodeAnalysis_Runtime_Instrumentation, + System_Runtime_CompilerServices_ReferenceAssemblyAttribute, NextAvailable, } @@ -519,7 +520,9 @@ internal static class WellKnownTypes "System.Runtime.CompilerServices.TupleElementNamesAttribute", - "Microsoft.CodeAnalysis.Runtime.Instrumentation" + "Microsoft.CodeAnalysis.Runtime.Instrumentation", + + "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" }; private readonly static Dictionary s_nameToTypeIdMap = new Dictionary((int)Count); diff --git a/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj b/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj index 924ff675aeaae..0e0d51ff1089a 100644 --- a/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj +++ b/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj @@ -329,6 +329,7 @@ + diff --git a/src/Compilers/VisualBasic/Portable/CodeGen/EmitStatement.vb b/src/Compilers/VisualBasic/Portable/CodeGen/EmitStatement.vb index c22ba0ad08bb9..e0d1a44f9304f 100644 --- a/src/Compilers/VisualBasic/Portable/CodeGen/EmitStatement.vb +++ b/src/Compilers/VisualBasic/Portable/CodeGen/EmitStatement.vb @@ -1422,7 +1422,7 @@ OtherExpressions: Private Sub EmitStateMachineScope(scope As BoundStateMachineScope) _builder.OpenLocalScope() - If _module.EmitOptions.DebugInformationFormat = DebugInformationFormat.Pdb Then + If _module.debugInformationFormat = DebugInformationFormat.Pdb Then 'Native PDBs: VB EE uses name mangling to match up original locals and the fields where they are hoisted 'The scoping information is passed by recording PDB scopes of "fake" locals named the same 'as the fields. These locals are not emitted to IL. diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 2e59edc2fae68..db39bd14ca19b 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -97,7 +97,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim utf8output As Boolean = False Dim outputFileName As String = Nothing Dim outputRefFileName As String = Nothing - Dim metadataOnly As Boolean = False + Dim refOnly As Boolean = False Dim outputDirectory As String = baseDirectory Dim documentationPath As String = Nothing Dim errorLogPath As String = Nothing @@ -471,7 +471,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "refonly") End If - metadataOnly = True + refOnly = True Continue For @@ -1197,11 +1197,11 @@ lVbRuntimePlus: specificDiagnosticOptions(item.Key) = item.Value Next - If metadataOnly AndAlso outputRefFileName IsNot Nothing Then + If refOnly AndAlso outputRefFileName IsNot Nothing Then AddDiagnostic(diagnostics, ERRID.ERR_NoRefOutWhenRefOnly) End If - If outputKind = OutputKind.NetModule AndAlso (metadataOnly OrElse outputRefFileName IsNot Nothing) Then + If outputKind = OutputKind.NetModule AndAlso (refOnly OrElse outputRefFileName IsNot Nothing) Then AddDiagnostic(diagnostics, ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly) End If @@ -1353,7 +1353,8 @@ lVbRuntimePlus: reportSuppressedDiagnostics:=reportSuppressedDiagnostics) Dim emitOptions = New EmitOptions( - metadataOnly:=metadataOnly, + metadataOnly:=refOnly, + includePrivateMembers:=Not refOnly AndAlso outputRefFileName Is Nothing, debugInformationFormat:=debugInformationFormat, pdbFilePath:=Nothing, ' to be determined later outputNameOverride:=Nothing, ' to be determined later @@ -1413,7 +1414,7 @@ lVbRuntimePlus: .ScriptArguments = scriptArgs.AsImmutableOrEmpty(), .TouchedFilesPath = touchedFilesPath, .OutputLevel = outputLevel, - .EmitPdb = emitPdb AndAlso Not metadataOnly, ' Silently ignore emitPdb when metadataOnly is set + .EmitPdb = emitPdb AndAlso Not refOnly, ' Silently ignore emitPdb when refOnly is set .SourceLink = sourceLink, .DefaultCoreLibraryReference = defaultCoreLibraryReference, .PreferredUILang = preferredUILang, diff --git a/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb b/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb index 10348ad1f7c0c..ba54dbfeab21f 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb @@ -21,6 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Private ReadOnly _compilation As VisualBasicCompilation Private ReadOnly _cancellationToken As CancellationToken Private ReadOnly _emittingPdb As Boolean + Private ReadOnly _emitTestCoverageData As Boolean Private ReadOnly _diagnostics As DiagnosticBag Private ReadOnly _hasDeclarationErrors As Boolean Private ReadOnly _moduleBeingBuiltOpt As PEModuleBuilder ' Nothing if compiling for diagnostics @@ -78,6 +79,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Private Sub New(compilation As VisualBasicCompilation, moduleBeingBuiltOpt As PEModuleBuilder, emittingPdb As Boolean, + emitTestCoverageData As Boolean, doEmitPhase As Boolean, hasDeclarationErrors As Boolean, diagnostics As DiagnosticBag, @@ -91,9 +93,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic _cancellationToken = cancellationToken _doEmitPhase = doEmitPhase _emittingPdb = emittingPdb + _emitTestCoverageData = emitTestCoverageData _filterOpt = filter - If emittingPdb OrElse moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData Then + If emittingPdb OrElse emitTestCoverageData Then _debugDocumentProvider = Function(path As String, basePath As String) moduleBeingBuiltOpt.DebugDocumentsBuilder.GetOrAddDebugDocument(path, basePath, AddressOf CreateDebugDocumentForFile) End If @@ -155,6 +158,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim compiler = New MethodCompiler(compilation, moduleBeingBuiltOpt:=Nothing, emittingPdb:=False, + emitTestCoverageData:=False, doEmitPhase:=doEmitPhase, hasDeclarationErrors:=hasDeclarationErrors, diagnostics:=diagnostics, @@ -187,6 +191,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend Shared Sub CompileMethodBodies(compilation As VisualBasicCompilation, moduleBeingBuiltOpt As PEModuleBuilder, emittingPdb As Boolean, + emitTestCoverageData As Boolean, hasDeclarationErrors As Boolean, filter As Predicate(Of Symbol), diagnostics As DiagnosticBag, @@ -209,6 +214,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim compiler = New MethodCompiler(compilation, moduleBeingBuiltOpt, emittingPdb, + emitTestCoverageData, doEmitPhase:=True, hasDeclarationErrors:=hasDeclarationErrors, diagnostics:=diagnostics, @@ -286,6 +292,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic debugDocumentProvider:=Nothing, diagnostics:=diagnostics, emittingPdb:=False, + emitTestCoverageData:=False, dynamicAnalysisSpans:=ImmutableArray(Of SourceSpan).Empty) moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody) End If @@ -847,9 +854,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic closureDebugInfo:=ImmutableArray(Of ClosureDebugInfo).Empty, stateMachineTypeOpt:=Nothing, variableSlotAllocatorOpt:=Nothing, - debugDocumentProvider:=If(_moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData, _debugDocumentProvider, Nothing), + debugDocumentProvider:=If(_emitTestCoverageData, _debugDocumentProvider, Nothing), diagnostics:=diagnosticsThisMethod, emittingPdb:=False, + emitTestCoverageData:=_emitTestCoverageData, dynamicAnalysisSpans:=ImmutableArray(Of SourceSpan).Empty) _diagnostics.AddRange(diagnosticsThisMethod) @@ -923,6 +931,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic debugDocumentProvider:=Nothing, diagnostics:=diagnosticsThisMethod, emittingPdb:=False, + emitTestCoverageData:=False, dynamicAnalysisSpans:=dynamicAnalysisSpans) End If @@ -973,6 +982,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic debugDocumentProvider:=_debugDocumentProvider, diagnostics:=diagnosticsThisMethod, emittingPdb:=_emittingPdb, + emitTestCoverageData:=_emitTestCoverageData, dynamicAnalysisSpans:=ImmutableArray(Of SourceSpan).Empty) _diagnostics.AddRange(diagnosticsThisMethod) @@ -1405,14 +1415,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim lambdaDebugInfoBuilder = ArrayBuilder(Of LambdaDebugInfo).GetInstance() Dim closureDebugInfoBuilder = ArrayBuilder(Of ClosureDebugInfo).GetInstance() Dim dynamicAnalysisSpans As ImmutableArray(Of SourceSpan) = ImmutableArray(Of SourceSpan).Empty - Dim emitDynamicAnalysisData As Boolean? = _moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData body = Rewriter.LowerBodyOrInitializer(method, methodOrdinal, body, previousSubmissionFields, compilationState, - emitDynamicAnalysisData.HasValue AndAlso emitDynamicAnalysisData.Value, + _emitTestCoverageData, dynamicAnalysisSpans, _debugDocumentProvider, diagnostics, @@ -1462,6 +1471,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic _debugDocumentProvider, diagnostics, emittingPdb:=_emittingPdb, + emitTestCoverageData:=_emitTestCoverageData, dynamicAnalysisSpans:=dynamicAnalysisSpans) If diagnostics IsNot diagsForCurrentMethod Then @@ -1486,6 +1496,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic debugDocumentProvider As DebugDocumentProvider, diagnostics As DiagnosticBag, emittingPdb As Boolean, + emitTestCoverageData As Boolean, dynamicAnalysisSpans As ImmutableArray(Of SourceSpan)) As MethodBody Dim compilation = moduleBuilder.Compilation @@ -1566,7 +1577,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim localScopes = builder.GetAllScopes() Dim dynamicAnalysisDataOpt As DynamicAnalysisMethodBodyData = Nothing - If moduleBuilder.EmitOptions.EmitTestCoverageData Then + If emitTestCoverageData Then Debug.Assert(debugDocumentProvider IsNot Nothing) dynamicAnalysisDataOpt = New DynamicAnalysisMethodBodyData(dynamicAnalysisSpans) End If diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 798d084a9e8ad..bbb44f49d5c2b 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2213,6 +2213,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend Overrides Function CompileMethods( moduleBuilder As CommonPEModuleBuilder, emittingPdb As Boolean, + emitMetadataOnly As Boolean, + emitTestCoverageData As Boolean, diagnostics As DiagnosticBag, filterOpt As Predicate(Of ISymbol), cancellationToken As CancellationToken) As Boolean @@ -2232,7 +2234,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic moduleBeingBuilt.TranslateImports(diagnostics) End If - If moduleBeingBuilt.EmitOptions.EmitMetadataOnly Then + If emitMetadataOnly Then If hasDeclarationErrors Then Return False End If @@ -2246,7 +2248,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic SynthesizedMetadataCompiler.ProcessSynthesizedMembers(Me, moduleBeingBuilt, cancellationToken) Else ' start generating PDB checksums if we need to emit PDBs - If (emittingPdb OrElse moduleBeingBuilt.EmitOptions.EmitTestCoverageData) AndAlso + If (emittingPdb OrElse emitTestCoverageData) AndAlso Not CreateDebugDocuments(moduleBeingBuilt.DebugDocumentsBuilder, moduleBeingBuilt.EmbeddedTexts, diagnostics) Then Return False End If @@ -2261,6 +2263,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Me, moduleBeingBuilt, emittingPdb, + emitTestCoverageData, hasDeclarationErrors, filterOpt, methodBodyDiagnosticBag, @@ -2282,6 +2285,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic moduleBuilder As CommonPEModuleBuilder, xmlDocStream As Stream, win32Resources As Stream, + outputNameOverride As String, diagnostics As DiagnosticBag, cancellationToken As CancellationToken) As Boolean @@ -2306,7 +2310,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Use a temporary bag so we don't have to refilter pre-existing diagnostics. Dim xmlDiagnostics = DiagnosticBag.GetInstance() - Dim assemblyName = FileNameUtilities.ChangeExtension(moduleBuilder.EmitOptions.OutputNameOverride, extension:=Nothing) + Dim assemblyName = FileNameUtilities.ChangeExtension(outputNameOverride, extension:=Nothing) DocumentationCommentCompiler.WriteDocumentationCommentXml(Me, assemblyName, xmlDocStream, xmlDiagnostics, cancellationToken) Return FilterAndAppendAndFreeDiagnostics(diagnostics, xmlDiagnostics) diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb index e771efcfdd5ee..b3e4e82236c5b 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb @@ -81,6 +81,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit updatedMethods, diagnostics, testData?.SymWriterFactory, + emitOpts.PdbFilePath, cancellationToken) End If @@ -109,8 +110,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit ' Mapping from previous compilation to the current. Dim anonymousTypeMap = moduleBeingBuilt.GetAnonymousTypeMap() Dim sourceAssembly = DirectCast(previousGeneration.Compilation, VisualBasicCompilation).SourceAssembly - Dim sourceContext = New EmitContext(DirectCast(previousGeneration.PEModuleBuilder, PEModuleBuilder), Nothing, New DiagnosticBag()) - Dim otherContext = New EmitContext(moduleBeingBuilt, Nothing, New DiagnosticBag()) + Dim sourceContext = New EmitContext(DirectCast(previousGeneration.PEModuleBuilder, PEModuleBuilder), Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) + Dim otherContext = New EmitContext(moduleBeingBuilt, Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) Dim matcher = New VisualBasicSymbolMatcher( anonymousTypeMap, diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb index 20e013d904434..1671f391e4c54 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb @@ -33,7 +33,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit MyBase.New(sourceAssembly, emitOptions, outputKind, serializationProperties, manifestResources, additionalTypes:=ImmutableArray(Of NamedTypeSymbol).Empty) Dim initialBaseline = previousGeneration.InitialBaseline - Dim context = New EmitContext(Me, Nothing, New DiagnosticBag()) + Dim context = New EmitContext(Me, Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) ' Hydrate symbols from initial metadata. Once we do so it is important to reuse these symbols across all generations, ' in order for the symbol matcher to be able to use reference equality once it maps symbols to initial metadata. @@ -46,7 +46,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Dim matchToPrevious As VisualBasicSymbolMatcher = Nothing If previousGeneration.Ordinal > 0 Then Dim previousAssembly = DirectCast(previousGeneration.Compilation, VisualBasicCompilation).SourceAssembly - Dim previousContext = New EmitContext(DirectCast(previousGeneration.PEModuleBuilder, PEModuleBuilder), Nothing, New DiagnosticBag()) + Dim previousContext = New EmitContext(DirectCast(previousGeneration.PEModuleBuilder, PEModuleBuilder), Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) matchToPrevious = New VisualBasicSymbolMatcher( previousGeneration.AnonymousTypeMap, diff --git a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb index 03076eefb3eeb..8375d049798a2 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeSymbolAdapter.vb @@ -232,18 +232,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return Nothing End Function - Private ReadOnly Property ITypeDefinitionEvents As IEnumerable(Of IEventDefinition) Implements ITypeDefinition.Events - Get - Debug.Assert(Not Me.IsAnonymousType) - 'Debug.Assert(((ITypeReference)this).AsTypeDefinition != null); + Private Iterator Function ITypeDefinitionEvents(context As EmitContext) As IEnumerable(Of IEventDefinition) Implements ITypeDefinition.GetEvents + Debug.Assert(Not Me.IsAnonymousType) + 'Debug.Assert(((ITypeReference)this).AsTypeDefinition != null); - ' can't be generic instantiation - ' must be declared in the module we are building - CheckDefinitionInvariant() + ' can't be generic instantiation + ' must be declared in the module we are building + CheckDefinitionInvariant() - Return GetEventsToEmit() - End Get - End Property + For Each e In GetEventsToEmit() + If e.ShouldInclude(context) Then + Yield e + End If + Next + End Function Friend Overridable Iterator Function GetEventsToEmit() As IEnumerable(Of EventSymbol) CheckDefinitionInvariant() @@ -329,14 +331,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' must be declared in the module we are building CheckDefinitionInvariant() + ' All fields in a struct should be emitted + Dim isStruct = Me.IsStructureType() + For Each field In Me.GetFieldsToEmit() - Yield field + If isStruct OrElse field.ShouldInclude(context) Then + Yield field + End If Next Dim syntheticFields = DirectCast(context.Module, PEModuleBuilder).GetSynthesizedFields(Me) If syntheticFields IsNot Nothing Then For Each field In syntheticFields - Yield field + If isStruct OrElse field.ShouldInclude(context) Then + Yield field + End If Next End If End Function @@ -661,13 +670,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols CheckDefinitionInvariant() For Each method In Me.GetMethodsToEmit() - Yield method + If method.ShouldInclude(context) Then + Yield method + End If Next Dim syntheticMethods = DirectCast(context.Module, PEModuleBuilder).GetSynthesizedMethods(Me) If syntheticMethods IsNot Nothing Then For Each method In syntheticMethods - Yield method + If method.ShouldInclude(context) Then + Yield method + End If Next End If End Function @@ -726,13 +739,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols For Each [property] In Me.GetPropertiesToEmit() Debug.Assert([property] IsNot Nothing) - Yield [property] + If [property].ShouldInclude(context) Then + Yield [property] + End If Next Dim syntheticProperties = DirectCast(context.Module, PEModuleBuilder).GetSynthesizedProperties(Me) If syntheticProperties IsNot Nothing Then For Each prop In syntheticProperties - Yield prop + If prop.ShouldInclude(context) Then + Yield prop + End If Next End If End Function diff --git a/src/Compilers/VisualBasic/Portable/Emit/NoPia/EmbeddedTypesManager.vb b/src/Compilers/VisualBasic/Portable/Emit/NoPia/EmbeddedTypesManager.vb index 2c8af65584bdc..5726579784f22 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/NoPia/EmbeddedTypesManager.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/NoPia/EmbeddedTypesManager.vb @@ -278,7 +278,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit.NoPia ' We do not expect this method to be called on a different thread once GetTypes is called. VerifyNotFrozen() - Dim noPiaIndexer = New Cci.NoPiaReferenceIndexer(New EmitContext(ModuleBeingBuilt, syntaxNodeOpt, diagnostics)) + Dim noPiaIndexer = New Cci.NoPiaReferenceIndexer(New EmitContext(ModuleBeingBuilt, syntaxNodeOpt, diagnostics, metadataOnly:=False, includePrivateMembers:=True)) ' Make sure we embed all types referenced by the type declaration: implemented interfaces, etc. noPiaIndexer.VisitTypeDefinitionNoMembers(embedded) diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index b0424cf0b3bff..4cb385e14dc9e 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -41,7 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Dim specifiedName = sourceModule.MetadataName - _metadataName = If(specifiedName <> Microsoft.CodeAnalysis.Compilation.UnspecifiedModuleAssemblyName, + _metadataName = If(specifiedName <> CodeAnalysis.Compilation.UnspecifiedModuleAssemblyName, specifiedName, If(emitOptions.OutputNameOverride, specifiedName)) @@ -176,8 +176,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Return Me.Compilation.TrySynthesizeAttribute(attributeConstructor) End Function - Public NotOverridable Overrides Function GetSourceAssemblyAttributes() As IEnumerable(Of Cci.ICustomAttribute) - Return SourceModule.ContainingSourceAssembly.GetCustomAttributesToEmit(Me.CompilationState, emittingAssemblyAttributesInNetModule:=OutputKind.IsNetModule()) + Public NotOverridable Overrides Function GetSourceAssemblyAttributes(isRefAssembly As Boolean) As IEnumerable(Of Cci.ICustomAttribute) + Return SourceModule.ContainingSourceAssembly.GetAssemblyCustomAttributesToEmit(Me.CompilationState, + isRefAssembly, + emittingAssemblyAttributesInNetModule:=OutputKind.IsNetModule()) End Function Public NotOverridable Overrides Function GetSourceAssemblySecurityAttributes() As IEnumerable(Of Cci.SecurityAttribute) @@ -330,8 +332,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Return False End Function - Friend NotOverridable Overrides Function GetAnonymousTypes() As ImmutableArray(Of Cci.INamespaceTypeDefinition) - If EmitOptions.EmitMetadataOnly Then + Friend NotOverridable Overrides Function GetAnonymousTypes(context As EmitContext) As ImmutableArray(Of Cci.INamespaceTypeDefinition) + If context.MetadataOnly Then Return ImmutableArray(Of Cci.INamespaceTypeDefinition).Empty End If diff --git a/src/Compilers/VisualBasic/Portable/Emit/SourceAssemblySymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/SourceAssemblySymbolAdapter.vb new file mode 100644 index 0000000000000..29d49a1530881 --- /dev/null +++ b/src/Compilers/VisualBasic/Portable/Emit/SourceAssemblySymbolAdapter.vb @@ -0,0 +1,24 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols + Partial Friend Class SourceAssemblySymbol + + Friend Function GetAssemblyCustomAttributesToEmit(compilationState As ModuleCompilationState, + emittingRefAssembly As Boolean, + emittingAssemblyAttributesInNetModule As Boolean) As IEnumerable(Of VisualBasicAttributeData) + + Dim synthesized As ArrayBuilder(Of SynthesizedAttributeData) = Nothing + AddSynthesizedAttributes(compilationState, synthesized) + + If emittingRefAssembly AndAlso Not HasReferenceAssemblyAttribute Then + Dim referenceAssemblyAttribute = Me.DeclaringCompilation. + TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, isOptionalUse:=True) + + Symbol.AddSynthesizedAttribute(synthesized, referenceAssemblyAttribute) + End If + + Return GetCustomAttributesToEmit(Me.GetAttributes(), synthesized, isReturnType:=False, emittingAssemblyAttributesInNetModule:=emittingAssemblyAttributesInNetModule) + End Function + + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb index c431426fe723e..af65ce3f230cb 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb @@ -40,6 +40,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Friend Function GetCustomAttributesToEmit(compilationState As ModuleCompilationState, emittingAssemblyAttributesInNetModule As Boolean) As IEnumerable(Of VisualBasicAttributeData) + Debug.Assert(Me.Kind <> SymbolKind.Assembly) + Dim synthesized As ArrayBuilder(Of SynthesizedAttributeData) = Nothing AddSynthesizedAttributes(compilationState, synthesized) Return GetCustomAttributesToEmit(Me.GetAttributes(), synthesized, isReturnType:=False, emittingAssemblyAttributesInNetModule:=emittingAssemblyAttributesInNetModule) diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 8876c568bcb07..77eb9205a9125 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1707,7 +1707,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_ExplicitTupleElementNamesAttribute = 37269 ERR_TupleLiteralDisallowsTypeChar = 37270 - ' Available 37270 ERR_DuplicateProcDefWithDifferentTupleNames2 = 37271 ERR_InterfaceImplementedTwiceWithDifferentTupleNames2 = 37272 ERR_InterfaceImplementedTwiceWithDifferentTupleNames3 = 37273 diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb index 0c84acfd3c7b1..a52f9c10e5f1c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb @@ -18,7 +18,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' Represents an assembly built by compiler. ''' ''' - Friend NotInheritable Class SourceAssemblySymbol + Partial Friend NotInheritable Class SourceAssemblySymbol Inherits MetadataOrSourceAssemblySymbol Implements ISourceAssemblySymbolInternal, IAttributeTargetSymbol @@ -1064,6 +1064,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols attrData.DecodeGuidAttribute(arguments.AttributeSyntaxOpt, arguments.Diagnostics) ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.CompilationRelaxationsAttribute) Then arguments.GetOrCreateData(Of CommonAssemblyWellKnownAttributeData)().HasCompilationRelaxationsAttribute = True + ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.ReferenceAssemblyAttribute) Then + arguments.GetOrCreateData(Of CommonAssemblyWellKnownAttributeData)().HasReferenceAssemblyAttribute = True ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.RuntimeCompatibilityAttribute) Then ' VB doesn't need to decode argument values arguments.GetOrCreateData(Of CommonAssemblyWellKnownAttributeData)().RuntimeCompatibilityWrapNonExceptionThrows = True @@ -1382,6 +1384,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End If End Sub + ''' + ''' True if internals are exposed at all. + ''' + ''' + ''' Forces binding and decoding of attributes. + ''' This property shouldn't be accessed during binding as it can lead to attribute binding cycle. + ''' + Public ReadOnly Property InternalsAreVisible As Boolean Implements ISourceAssemblySymbolInternal.InternalsAreVisible + Get + EnsureAttributesAreBound() + Return _lazyInternalsVisibleToMap IsNot Nothing + End Get + End Property + ''' ''' We may synthesize some well-known attributes for this assembly symbol. However, at synthesis time, it is ''' too late to report diagnostics or cancel the emit. Instead, we check for use site errors on the types and members @@ -1422,7 +1438,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Friend Overrides Sub AddSynthesizedAttributes(compilationState as ModuleCompilationState, ByRef attributes As ArrayBuilder(Of SynthesizedAttributeData)) + Private ReadOnly Property HasReferenceAssemblyAttribute As Boolean + Get + Dim assemblyData As CommonAssemblyWellKnownAttributeData = Me.GetSourceDecodedWellKnownAttributeData() + Return assemblyData IsNot Nothing AndAlso assemblyData.HasReferenceAssemblyAttribute + End Get + End Property + + Friend Overrides Sub AddSynthesizedAttributes(compilationState As ModuleCompilationState, ByRef attributes As ArrayBuilder(Of SynthesizedAttributeData)) MyBase.AddSynthesizedAttributes(compilationState, attributes) Debug.Assert(_lazyEmitExtensionAttribute <> ThreeState.Unknown) @@ -1652,8 +1675,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End If End If - keys = StrongNameKeys.Create(DeclaringCompilation.Options.StrongNameProvider, keyFile, keyContainer, MessageProvider.Instance) - Interlocked.CompareExchange(_lazyStrongNameKeys, keys, Nothing) + keys = StrongNameKeys.Create(DeclaringCompilation.Options.StrongNameProvider, keyFile, keyContainer, MessageProvider.Instance) + Interlocked.CompareExchange(_lazyStrongNameKeys, keys, Nothing) End Sub Private Function ComputeIdentity() As AssemblyIdentity diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 3e7296cb2b476..cdf88510d19b9 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -8101,8 +8101,8 @@ End Class") Assert.True(File.Exists(exe)) MetadataReaderUtils.VerifyPEMetadata(exe, - {"TypeDef:", "TypeDef:C"}, - {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"TypeDefinition:", "TypeDefinition:C"}, + {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} ) @@ -8133,49 +8133,18 @@ a Assert.True(File.Exists(refDll)) ' The types and members that are included needs further refinement. - ' ReferenceAssemblyAttribute is missing. ' See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - {"TypeDef:", "TypeDef:C"}, - {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, - {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + {"TypeDefinition:", "TypeDefinition:C"}, + {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute", "ReferenceAssemblyAttribute"} ) - VerifyNullReferenceException(refDir, "a.dll") - ' Clean up temp files CleanupAllGeneratedFiles(dir.Path) CleanupAllGeneratedFiles(refDir.Path) End Sub - ''' - ''' Calls C.Main() on the program and verifies that a null reference exception is thrown. - ''' - Private Shared Sub VerifyNullReferenceException(dir As TempDirectory, referenceName As String) - - Dim src = dir.CreateFile("verifier.vb") - src.WriteAllText(" -Class Verifier - Public Shared Sub Main() - Try - C.Main() - Catch ex As System.NullReferenceException - System.Console.Write(""Got expected exception"") - End Try - End Sub -End Class") - - Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) - Dim vbc = New MockVisualBasicCompiler(Nothing, dir.Path, - {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:verifier.exe", $"/reference:{referenceName}", "verifier.vb"}) - - Dim exitCode = vbc.Run(outWriter) - Assert.Equal(0, exitCode) - Dim dirPath As String = dir.ToString() - Dim output = ProcessUtilities.RunAndGetOutput(Path.Combine(dirPath, "verifier.exe"), startFolder:=dirPath, expectedRetCode:=0) - Assert.Equal("Got expected exception", output) - End Sub - Public Sub RefOutWithError() Dim dir = Temp.CreateDirectory() @@ -8239,12 +8208,11 @@ End Class") Assert.True(File.Exists(refDll)) ' The types and members that are included needs further refinement. - ' ReferenceAssemblyAttribute is missing. ' See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - {"TypeDef:", "TypeDef:C"}, - {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, - {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + {"TypeDefinition:", "TypeDefinition:C"}, + {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute", "ReferenceAssemblyAttribute"} ) Dim pdb = Path.Combine(dir.Path, "a.pdb") diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AssemblyAttributes.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AssemblyAttributes.vb index b74e4d966a211..767168811a64a 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AssemblyAttributes.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AssemblyAttributes.vb @@ -1222,7 +1222,10 @@ End Class ' We should get only unique netmodule/assembly attributes here, duplicate ones should not be emitted. Dim expectedEmittedAttrsCount As Integer = expectedSrcAttrCount - expectedDuplicateAttrCount - Dim allEmittedAttrs = assembly.GetCustomAttributesToEmit(New ModuleCompilationState).Cast(Of VisualBasicAttributeData)() + Dim allEmittedAttrs = DirectCast(assembly, SourceAssemblySymbol). + GetAssemblyCustomAttributesToEmit(New ModuleCompilationState, emittingRefAssembly:=False, emittingAssemblyAttributesInNetModule:=False). + Cast(Of VisualBasicAttributeData)() + Dim emittedAttrs = allEmittedAttrs.Where(Function(a) a.AttributeClass.Name.Equals(attrTypeName)).AsImmutable() Assert.Equal(expectedEmittedAttrsCount, emittedAttrs.Length) @@ -1504,7 +1507,10 @@ End Class expectedDuplicateAttrCount:=1, attrTypeName:="AssemblyTitleAttribute") - Dim attrs = consoleappCompilation.Assembly.GetCustomAttributesToEmit(New ModuleCompilationState).Cast(Of VisualBasicAttributeData)() + Dim attrs = DirectCast(consoleappCompilation.Assembly, SourceAssemblySymbol). + GetAssemblyCustomAttributesToEmit(New ModuleCompilationState, emittingRefAssembly:=False, emittingAssemblyAttributesInNetModule:=False). + Cast(Of VisualBasicAttributeData)() + For Each a In attrs Select Case a.AttributeClass.Name Case "AssemblyTitleAttribute" diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index b94de992431c0..68f38a335d6cb 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -297,59 +297,221 @@ End Class End Using End Sub + + Public Sub RefAssembly_HasReferenceAssemblyAttribute() + Dim emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False) + + Dim assemblyValidator As Action(Of PEAssembly) = + Sub(assembly) + Dim reader = assembly.GetMetadataReader() + Dim attributes = reader.GetAssemblyDefinition().GetCustomAttributes() + AssertEx.Equal( + {"MemberReference:Void System.Runtime.CompilerServices.CompilationRelaxationsAttribute.ctor(Int32)", + "MemberReference:Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute.ctor()", + "MemberReference:Void System.Diagnostics.DebuggableAttribute.ctor(DebuggingModes)", + "MemberReference:Void System.Runtime.CompilerServices.ReferenceAssemblyAttribute.ctor()" + }, + attributes.Select(Function(a) MetadataReaderUtils.Dump(reader, reader.GetCustomAttribute(a).Constructor))) + End Sub + + Dim source = + + + CompileAndVerify(source, emitOptions:=emitRefAssembly, verify:=True, validator:=assemblyValidator) + End Sub + + + Public Sub RefAssembly_HandlesMissingReferenceAssemblyAttribute() + Dim emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False) + + Dim assemblyValidator As Action(Of PEAssembly) = + Sub(assembly) + Dim reader = assembly.GetMetadataReader() + Dim attributes = reader.GetAssemblyDefinition().GetCustomAttributes() + AssertEx.Empty(attributes.Select(Function(a) MetadataReaderUtils.Dump(reader, reader.GetCustomAttribute(a).Constructor))) + End Sub + + Dim comp = CreateCompilation({Parse("")}) + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor) + CompileAndVerify(comp, emitOptions:=emitRefAssembly, verify:=True, validator:=assemblyValidator) + End Sub + + + Public Sub RefAssembly_ReferenceAssemblyAttributeAlsoInSource() + Dim emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False) + + Dim assemblyValidator As Action(Of PEAssembly) = + Sub(assembly) + Dim reader = assembly.GetMetadataReader() + Dim attributes = reader.GetAssemblyDefinition().GetCustomAttributes() + AssertEx.Equal( + {"MemberReference:Void System.Runtime.CompilerServices.CompilationRelaxationsAttribute.ctor(Int32)", + "MemberReference:Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute.ctor()", + "MemberReference:Void System.Diagnostics.DebuggableAttribute.ctor(DebuggingModes)", + "MemberReference:Void System.Runtime.CompilerServices.ReferenceAssemblyAttribute.ctor()" + }, + attributes.Select(Function(a) MetadataReaderUtils.Dump(reader, reader.GetCustomAttribute(a).Constructor))) + End Sub + + Dim source = + + ]]> + + CompileAndVerify(source, emitOptions:=emitRefAssembly, verify:=True, validator:=assemblyValidator) + End Sub + Public Sub RefAssembly_InvariantToSomeChanges() + Dim sourceTemplate As String = " +Public Class C + CHANGE +End Class" - RefAssembly_InvariantToSomeChanges( + CompareAssemblies(sourceTemplate, "Public Function M() As Integer Return 1 End Function", "Public Function M() As Integer Return 2 -End Function", True) +End Function", Match.BothMetadataAndRefOut) - RefAssembly_InvariantToSomeChanges( + CompareAssemblies(sourceTemplate, "Public Function M() As Integer Return 1 End Function", "Public Function M() As Integer -End Function", True) + Return Bad() +End Function", Match.BothMetadataAndRefOut) - RefAssembly_InvariantToSomeChanges( -"", + CompareAssemblies(sourceTemplate, "Private Sub M() -End Sub", False) ' Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 +End Sub", +"", Match.RefOut) - RefAssembly_InvariantToSomeChanges( + CompareAssemblies(sourceTemplate, "Friend Sub M() End Sub", -"", False) +"", Match.RefOut) - RefAssembly_InvariantToSomeChanges( + CompareAssemblies(sourceTemplate, +"Private Sub M() +Dim product = New With {Key .Id = 1} +End Sub", +"", Match.RefOut) + + CompareAssemblies(sourceTemplate, +"Private Property P As Integer + Get + Bad() + End Get + Set + Bad() + End Set +End Property", +"", Match.RefOut) ' Errors in method bodies don't matter + + CompareAssemblies(sourceTemplate, +"Public Property P As Integer", +"", Match.Different) + + CompareAssemblies(sourceTemplate, +"Protected Property P As Integer", +"", Match.Different) + + CompareAssemblies(sourceTemplate, +"Private Property P As Integer", +"", Match.RefOut) ' Private auto-property and underlying field are removed + + CompareAssemblies(sourceTemplate, +"Friend Property P As Integer", +"", Match.RefOut) + + CompareAssemblies(sourceTemplate, +"Private Event DoSomething()", +"", Match.Different) ' VB events add nested types (C.DoSomethingEventHandler in this case) + + CompareAssemblies(sourceTemplate, +"Friend Event DoSomething()", +"", Match.Different) + + CompareAssemblies(sourceTemplate, +"Private Class C2 +End Class", +"", Match.Different) ' All types are included + + CompareAssemblies(sourceTemplate, +"Private Structure S +End Structure", +"", Match.Different) + + CompareAssemblies(sourceTemplate, "Public Structure S Private Dim i As Integer End Structure", "Public Structure S -End Structure", False) +End Structure", Match.Different) + + CompareAssemblies(sourceTemplate, +"Private Dim i As Integer", +"", Match.RefOut) + + CompareAssemblies(sourceTemplate, +"Public Sub New() +End Sub", +"", Match.BothMetadataAndRefOut) + + CompareAssemblies(sourceTemplate, +"Public Function NoBody() As Integer +End Function", +"Public Function NoBody() As Integer + Return 1 +End Function", Match.BothMetadataAndRefOut) End Sub - Private Sub RefAssembly_InvariantToSomeChanges(change1 As String, change2 As String, expectMatch As Boolean) + + Public Sub RefAssembly_InvariantToSomeChangesWithInternalsVisibleTo() Dim sourceTemplate As String = " +Imports System.Runtime.CompilerServices + Public Class C CHANGE End Class" + CompareAssemblies(sourceTemplate, +"Friend Function M() As Integer +End Function", +"", Match.Different) + + End Sub + + Private Sub CompareAssemblies(sourceTemplate As String, left As String, right As String, expectedMatch As Match) + CompareAssemblies(sourceTemplate, left, right, expectedMatch, includePrivateMembers:=True) + CompareAssemblies(sourceTemplate, left, right, expectedMatch, includePrivateMembers:=False) + End Sub + + Public Enum Match + BothMetadataAndRefOut + RefOut + Different + End Enum + + Private Sub CompareAssemblies(sourceTemplate As String, change1 As String, change2 As String, expectedMatch As Match, includePrivateMembers As Boolean) + Dim expectMatch = If(includePrivateMembers, + expectedMatch = Match.BothMetadataAndRefOut, + expectedMatch = Match.BothMetadataAndRefOut OrElse expectedMatch = Match.RefOut) + Dim name As String = GetUniqueName() Dim source1 As String = sourceTemplate.Replace("CHANGE", change1) Dim comp1 = CreateCompilationWithMscorlib(source1, options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) - Dim image1 As ImmutableArray(Of Byte) = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True)) + Dim image1 As ImmutableArray(Of Byte) = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(includePrivateMembers)) Dim source2 = sourceTemplate.Replace("CHANGE", change2) Dim comp2 = CreateCompilationWithMscorlib(source2, options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) - Dim image2 As ImmutableArray(Of Byte) = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True)) + Dim image2 As ImmutableArray(Of Byte) = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(includePrivateMembers)) If expectMatch Then AssertEx.Equal(image1, image2) @@ -358,6 +520,156 @@ End Class" End If End Sub + + Public Sub RefAssemblyClient_EmitAllNestedTypes() + VerifyRefAssemblyClient(" +Public Interface I1(Of T) +End Interface +Public Interface I2 +End Interface +Public Class A + Implements I1(Of A.X) + + Private Class X + Implements I2 + + End Class +End Class +", " +Public Class C + Public Function M(a As A) As I1(Of I2) + Return DirectCast(a, I1(Of I2)) + End Function +End Class +", +Sub(comp) comp.AssertNoDiagnostics()) + End Sub + + + Public Sub RefAssemblyClient_EmitAllTypes() + VerifyRefAssemblyClient(" +Public Interface I1(Of T) +End Interface +Public Interface I2 +End Interface +Public Class A + Implements I1(Of X) +End Class +Friend Class X + Implements I2 +End Class +", " +Public Class C + Public Function M(a As A) As I1(Of I2) + Return DirectCast(a, I1(Of I2)) + End Function +End Class +", +Sub(comp) comp.AssertNoDiagnostics()) + End Sub + + + Public Sub RefAssemblyClient_StructWithPrivateGenericField() + VerifyRefAssemblyClient(" +Public Structure Container(Of T) + Private Dim contained As T +End Structure +", " +Public Structure Usage + Public Dim x As Container(Of Usage) +End Structure +", +Sub(comp) + comp.AssertTheseDiagnostics( +BC30294: Structure 'Usage' cannot contain an instance of itself: + 'Usage' contains 'Container(Of Usage)' (variable 'x'). + 'Container(Of Usage)' contains 'Usage' (variable 'contained'). + Public Dim x As Container(Of Usage) + ~ + ) +End Sub) + End Sub + + + Public Sub RefAssemblyClient_EmitAllVirtualMethods() + Dim comp1 = CreateVisualBasicCompilation(" + + +Public MustInherit Class C1 + Friend Sub M() + End Sub +End Class +", assemblyName:="VB1", referencedAssemblies:={MscorlibRef}) + comp1.AssertTheseDiagnostics() + Dim image1 = comp1.EmitToImageReference(EmitOptions.Default) + + Dim comp2 = CreateVisualBasicCompilation(" +Public MustInherit Class C2 + Inherits C1 + + Friend Overloads Sub M() + End Sub +End Class +", assemblyName:="VB2", referencedAssemblies:={MscorlibRef, image1}) + comp2.AssertTheseDiagnostics() + Dim image2 = comp2.EmitToImageReference(EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False)) + + ' If internal virtual methods were not included in ref assemblies, then C3 could not be concrete + + Dim comp3 = CreateVisualBasicCompilation(" +Public Class C3 + Inherits C2 +End Class +", assemblyName:="VB3", referencedAssemblies:={MscorlibRef, image1, image2}) + comp3.AssertTheseDiagnostics() + + End Sub + + + Public Sub RefAssemblyClient_StructWithPrivateIntField() + VerifyRefAssemblyClient(" +Public Structure S + Private Dim i As Integer +End Structure +", " +Public Class C + Public Function M() As String + Dim s As S + Return s.ToString() + End Function +End Class +", +Sub(comp) comp.AssertTheseDiagnostics()) + End Sub + + Private Sub VerifyRefAssemblyClient(lib_vb As String, client_vb As String, validator As Action(Of VisualBasicCompilation), Optional debugFlag As Integer = -1) + ' Whether the library is compiled in full, as metadata-only, or as a ref assembly should be transparent + ' to the client and the validator should be able to verify the same expectations. + + If debugFlag = -1 OrElse debugFlag = 0 Then + VerifyRefAssemblyClient(lib_vb, client_vb, validator, + EmitOptions.Default.WithEmitMetadataOnly(False)) + End If + If debugFlag = -1 OrElse debugFlag = 1 Then + VerifyRefAssemblyClient(lib_vb, client_vb, validator, + EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(True)) + End If + If debugFlag = -1 OrElse debugFlag = 2 Then + VerifyRefAssemblyClient(lib_vb, client_vb, validator, + EmitOptions.Default.WithEmitMetadataOnly(False).WithIncludePrivateMembers(False)) + End If + End Sub + + Private Sub VerifyRefAssemblyClient(lib_vb As String, source As String, validator As Action(Of VisualBasicCompilation), emitOptions As EmitOptions) + Dim name = GetUniqueName() + Dim libComp = CreateCompilationWithMscorlib({Parse(lib_vb)}, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + Dim libImage = libComp.EmitToImageReference(emitOptions) + + Dim comp = CreateCompilationWithMscorlib(source, references:={libImage}, options:=TestOptions.DebugDll) + validator(comp) + End Sub + Public Sub RefAssembly_IgnoresSomeDiagnostics() @@ -415,33 +727,77 @@ End Class" Dim comp As Compilation = CreateCompilation(source, references:={MscorlibRef}, options:=TestOptions.DebugDll.WithDeterministic(True)) - Dim emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(True) + Dim verifier = CompileAndVerify(comp, emitOptions:=EmitOptions.Default.WithEmitMetadataOnly(True), verify:=True) + + ' verify metadata (types, members, attributes) of the regular assembly + Dim realImage = comp.EmitToImageReference(EmitOptions.Default) + Dim compWithReal = CreateCompilation("", references:={MscorlibRef, realImage}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim realAssembly = compWithReal.SourceModule.GetReferencedAssemblySymbols().Last() + AssertEx.SetEqual(realAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString()), + {"", "PublicClass"}) + + AssertEx.SetEqual( + DirectCast(realAssembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). + Select(Function(m) m.ToTestDisplayString()), + {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", + "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", + "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}) + + AssertEx.SetEqual(realAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute"}) - Dim verifier = CompileAndVerify(comp, emitOptions:=emitRefOnly, verify:=True) + ' verify metadata (types, members, attributes) of the metadata-only assembly + Dim emitMetadataOnly = EmitOptions.Default.WithEmitMetadataOnly(True) + CompileAndVerify(comp, emitOptions:=emitMetadataOnly, verify:=True) - ' verify metadata (types, members, attributes) - Dim image = comp.EmitToImageReference(emitRefOnly) - Dim comp2 = CreateCompilation("", references:={MscorlibRef, image}, + Dim metadataImage = comp.EmitToImageReference(emitMetadataOnly) + Dim compWithMetadata = CreateCompilation("", references:={MscorlibRef, metadataImage}, options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) - Dim assembly As AssemblySymbol = comp2.SourceModule.GetReferencedAssemblySymbols().Last() - Dim members = assembly.GlobalNamespace.GetMembers() - AssertEx.SetEqual(members.Select(Function(m) m.ToDisplayString()), + Dim metadataAssembly As AssemblySymbol = compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last() + AssertEx.SetEqual(metadataAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString()), {"", "PublicClass"}) AssertEx.SetEqual( - DirectCast(assembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). + DirectCast(metadataAssembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). Select(Function(m) m.ToTestDisplayString()), {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}) - AssertEx.SetEqual(assembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + AssertEx.SetEqual(metadataAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", "System.Diagnostics.DebuggableAttribute"}) - Dim peImage = comp.EmitToArray(emitRefOnly) - MetadataReaderUtils.AssertEmptyOrThrowNull(peImage) + MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitMetadataOnly)) + + ' verify metadata (types, members, attributes) of the metadata-only assembly + Dim emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False) + CompileAndVerify(comp, emitOptions:=emitRefOnly, verify:=True) + + Dim refImage = comp.EmitToImageReference(emitRefOnly) + Dim compWithRef = CreateCompilation("", references:={MscorlibRef, refImage}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim refAssembly As AssemblySymbol = compWithRef.SourceModule.GetReferencedAssemblySymbols().Last() + AssertEx.SetEqual(refAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString()), + {"", "PublicClass"}) + + AssertEx.SetEqual( + DirectCast(refAssembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). + Select(Function(m) m.ToTestDisplayString()), + {"Sub PublicClass..ctor()", "Sub PublicClass.PublicMethod()", + "Sub PublicClass.ProtectedMethod()", "Sub PublicClass.AbstractMethod()"}) + + AssertEx.SetEqual(refAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute", + "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"}) + + MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitRefOnly)) End Sub diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb index bdbaf61013e05..ca3e62666efee 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb @@ -344,7 +344,7 @@ End Class Dim displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single() Assert.Equal("_Closure$__1-0", displayClass.Name) - Dim emitContext = New EmitContext(peAssemblyBuilder, Nothing, New DiagnosticBag()) + Dim emitContext = New EmitContext(peAssemblyBuilder, Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) Dim fields = displayClass.GetFields(emitContext).ToArray() Dim x1 = fields(0) @@ -415,7 +415,7 @@ End Class Dim displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single() Assert.Equal("_Closure$__1-0", displayClass.Name) - Dim emitContext = New EmitContext(peAssemblyBuilder, Nothing, New DiagnosticBag()) + Dim emitContext = New EmitContext(peAssemblyBuilder, Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) Dim fields = displayClass.GetFields(emitContext).ToArray() Dim x1 = fields(0) @@ -492,7 +492,7 @@ End Class Assert.Equal("_Closure$__1-0", displayClasses(0).Name) Assert.Equal("_Closure$__", displayClasses(1).Name) - Dim emitContext = New EmitContext(peAssemblyBuilder, Nothing, New DiagnosticBag()) + Dim emitContext = New EmitContext(peAssemblyBuilder, Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) Dim fields = displayClasses(0).GetFields(emitContext).ToArray() Dim x1 = fields(0) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/PropertyTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/PropertyTests.vb index c6a5b40bf4a6f..b82c94c69f06c 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/PropertyTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/PropertyTests.vb @@ -8143,7 +8143,7 @@ End Class Dim typeDef = DirectCast([class], Cci.ITypeDefinition) Dim [module] = New PEAssemblyBuilder(DirectCast([class].ContainingAssembly, SourceAssemblySymbol), EmitOptions.Default, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable(Of ResourceDescription)()) - Dim context = New EmitContext([module], Nothing, New DiagnosticBag()) + Dim context = New EmitContext([module], Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True) Dim explicitOverrides = typeDef.GetExplicitImplementationOverrides(context) Assert.Equal(2, explicitOverrides.Count()) Assert.True(explicitOverrides.All(Function(override) [class] Is override.ContainingType)) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index d8242652582ab..9d20afd45c659 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -258,7 +258,7 @@ internal byte[] CompileExpressions( using (var stream = new MemoryStream()) { Cci.PeWriter.WritePeToStream( - new EmitContext(moduleBuilder, null, diagnostics), + new EmitContext(moduleBuilder, null, diagnostics, metadataOnly: false, includePrivateMembers: true), context.MessageProvider, () => stream, getPortablePdbStreamOpt: null, @@ -266,6 +266,7 @@ internal byte[] CompileExpressions( pdbPathOpt: null, metadataOnly: false, isDeterministic: false, + emitTestCoverageData: false, cancellationToken: default(CancellationToken)); if (!diagnostics.HasAnyErrors()) { @@ -319,7 +320,7 @@ internal override CompileResult CompileExpression( using (var stream = new MemoryStream()) { Cci.PeWriter.WritePeToStream( - new EmitContext(moduleBuilder, null, diagnostics), + new EmitContext(moduleBuilder, null, diagnostics, metadataOnly: false, includePrivateMembers: true), context.MessageProvider, () => stream, getPortablePdbStreamOpt: null, @@ -327,6 +328,7 @@ internal override CompileResult CompileExpression( pdbPathOpt: null, metadataOnly: false, isDeterministic: false, + emitTestCoverageData: false, cancellationToken: default(CancellationToken)); if (diagnostics.HasAnyErrors()) @@ -403,7 +405,7 @@ internal override CompileResult CompileAssignment( using (var stream = new MemoryStream()) { Cci.PeWriter.WritePeToStream( - new EmitContext(moduleBuilder, null, diagnostics), + new EmitContext(moduleBuilder, null, diagnostics, metadataOnly: false, includePrivateMembers: true), context.MessageProvider, () => stream, getPortablePdbStreamOpt: null, @@ -411,6 +413,7 @@ internal override CompileResult CompileAssignment( pdbPathOpt: null, metadataOnly: false, isDeterministic: false, + emitTestCoverageData: false, cancellationToken: default(CancellationToken)); if (diagnostics.HasAnyErrors()) @@ -450,7 +453,7 @@ internal override ReadOnlyCollection CompileGetLocals( using (var stream = new MemoryStream()) { Cci.PeWriter.WritePeToStream( - new EmitContext(moduleBuilder, null, diagnostics), + new EmitContext(moduleBuilder, null, diagnostics, metadataOnly: false, includePrivateMembers: true), context.MessageProvider, () => stream, getPortablePdbStreamOpt: null, @@ -458,6 +461,7 @@ internal override ReadOnlyCollection CompileGetLocals( pdbPathOpt: null, metadataOnly: false, isDeterministic: false, + emitTestCoverageData: false, cancellationToken: default(CancellationToken)); if (!diagnostics.HasAnyErrors()) diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ReferencedModulesTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ReferencedModulesTests.cs index 8707ca2bfaea8..0a151ad44936e 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ReferencedModulesTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ReferencedModulesTests.cs @@ -755,7 +755,7 @@ public class Void : Object { } ExpressionCompilerTestHelpers.EmitCorLibWithAssemblyReferences( compCorLib, null, - moduleBuilder => new PEAssemblyBuilderWithAdditionalReferences(moduleBuilder, objectType), + (moduleBuilder, emitOptions) => new PEAssemblyBuilderWithAdditionalReferences(moduleBuilder, emitOptions, objectType), out peBytes, out pdbBytes); @@ -874,7 +874,7 @@ public class Void : Object { } ExpressionCompilerTestHelpers.EmitCorLibWithAssemblyReferences( compCorLib, pdbPath, - moduleBuilder => new PEAssemblyBuilderWithAdditionalReferences(moduleBuilder, objectType), + (moduleBuilder, emitOptions) => new PEAssemblyBuilderWithAdditionalReferences(moduleBuilder, emitOptions, objectType), out peBytes, out pdbBytes); var symReader = SymReaderFactory.CreateReader(pdbBytes); @@ -994,8 +994,8 @@ private sealed class PEAssemblyBuilderWithAdditionalReferences : PEModuleBuilder private readonly CommonPEModuleBuilder _builder; private readonly NamespaceTypeDefinitionNoBase _objectType; - internal PEAssemblyBuilderWithAdditionalReferences(CommonPEModuleBuilder builder, INamespaceTypeDefinition objectType) : - base((SourceModuleSymbol)builder.CommonSourceModule, builder.EmitOptions, builder.OutputKind, builder.SerializationProperties, builder.ManifestResources) + internal PEAssemblyBuilderWithAdditionalReferences(CommonPEModuleBuilder builder, EmitOptions emitOptions, INamespaceTypeDefinition objectType) : + base((SourceModuleSymbol)builder.CommonSourceModule, emitOptions, builder.OutputKind, builder.SerializationProperties, builder.ManifestResources) { _builder = builder; _objectType = new NamespaceTypeDefinitionNoBase(objectType); diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/TupleTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/TupleTests.cs index d3cbea65c3dd7..e329be81b7379 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/TupleTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/TupleTests.cs @@ -133,9 +133,9 @@ public struct ValueTuple AssertEx.SetEqual(new[] { "corlib 2.0", appRef + " 0.0" }, reader.DumpAssemblyReferences()); AssertEx.SetEqual(new[] { - "Object, System, AssemblyRef:corlib", - "ValueTuple`2, System, AssemblyRef:" + appRef, // ValueTuple comes from app, not corlib - ", System, AssemblyRef:" + appRef }, + "Object, System, AssemblyReference:corlib", + "ValueTuple`2, System, AssemblyReference:" + appRef, // ValueTuple comes from app, not corlib + ", System, AssemblyReference:" + appRef }, reader.DumpTypeReferences()); } } diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs index 89f2f4c3f640a..816cb5c64ae04 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs @@ -756,7 +756,7 @@ internal unsafe static MetadataReader ToMetadataReader(this PEMemoryBlock metada internal static void EmitCorLibWithAssemblyReferences( Compilation comp, string pdbPath, - Func getModuleBuilder, + Func getModuleBuilder, out ImmutableArray peBytes, out ImmutableArray pdbBytes) { @@ -774,7 +774,7 @@ internal static void EmitCorLibWithAssemblyReferences( // Wrap the module builder in a module builder that // reports the "System.Object" type as having no base type. - moduleBuilder = getModuleBuilder(moduleBuilder); + moduleBuilder = getModuleBuilder(moduleBuilder, emitOptions); bool result = comp.Compile( moduleBuilder, emittingPdb: pdbPath != null, @@ -787,13 +787,14 @@ internal static void EmitCorLibWithAssemblyReferences( using (var pdbStream = new MemoryStream()) { PeWriter.WritePeToStream( - new EmitContext(moduleBuilder, null, diagnostics), + new EmitContext(moduleBuilder, null, diagnostics, metadataOnly: false, includePrivateMembers: true), comp.MessageProvider, () => peStream, () => pdbStream, null, null, metadataOnly: true, isDeterministic: false, + emitTestCoverageData: false, cancellationToken: default(CancellationToken)); peBytes = peStream.ToImmutable(); diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs index 3349f6420f9a6..4fe32d18b0a5e 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs @@ -31,7 +31,7 @@ internal NamespaceTypeDefinitionNoBase(INamespaceTypeDefinition underlyingType) ISpecializedNestedTypeReference ITypeReference.AsSpecializedNestedTypeReference => UnderlyingType.AsSpecializedNestedTypeReference; - IEnumerable ITypeDefinition.Events => UnderlyingType.Events; + IEnumerable ITypeDefinition.GetEvents(EmitContext context) => UnderlyingType.GetEvents(context); ushort INamedTypeReference.GenericParameterCount => 0; diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb index 39420a5649510..2096308f5bdd8 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb @@ -392,7 +392,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Using stream As New MemoryStream() Cci.PeWriter.WritePeToStream( - New EmitContext(moduleBuilder, Nothing, diagnostics), + New EmitContext(moduleBuilder, Nothing, diagnostics, metadataOnly:=False, includePrivateMembers:=True), context.MessageProvider, Function() stream, getPortablePdbStreamOpt:=Nothing, @@ -400,6 +400,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator pdbPathOpt:=Nothing, metadataOnly:=False, isDeterministic:=False, + emitTestCoverageData:=False, cancellationToken:=Nothing) If diagnostics.HasAnyErrors() Then @@ -439,7 +440,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Using stream As New MemoryStream() Cci.PeWriter.WritePeToStream( - New EmitContext(modulebuilder, Nothing, diagnostics), + New EmitContext(modulebuilder, Nothing, diagnostics, metadataOnly:=False, includePrivateMembers:=True), context.MessageProvider, Function() stream, getPortablePdbStreamOpt:=Nothing, @@ -447,6 +448,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator pdbPathOpt:=Nothing, metadataOnly:=False, isDeterministic:=False, + emitTestCoverageData:=False, cancellationToken:=Nothing) If diagnostics.HasAnyErrors() Then @@ -487,7 +489,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator If modulebuilder IsNot Nothing AndAlso locals.Count > 0 Then Using stream As New MemoryStream() Cci.PeWriter.WritePeToStream( - New EmitContext(modulebuilder, Nothing, diagnostics), + New EmitContext(modulebuilder, Nothing, diagnostics, metadataOnly:=False, includePrivateMembers:=True), context.MessageProvider, Function() stream, getPortablePdbStreamOpt:=Nothing, @@ -495,6 +497,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator pdbPathOpt:=Nothing, metadataOnly:=False, isDeterministic:=False, + emitTestCoverageData:=False, cancellationToken:=Nothing) If Not diagnostics.HasAnyErrors() Then diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ReferencedModulesTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ReferencedModulesTests.vb index 6c823b7032cf5..9bb2dd66ed01c 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ReferencedModulesTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ReferencedModulesTests.vb @@ -491,7 +491,7 @@ End Namespace" ExpressionCompilerTestHelpers.EmitCorLibWithAssemblyReferences( compCorLib, Nothing, - Function(moduleBuilder) New PEAssemblyBuilderWithAdditionalReferences(moduleBuilder, objectType), + Function(moduleBuilder, emitOptions) New PEAssemblyBuilderWithAdditionalReferences(moduleBuilder, emitOptions, objectType), peBytes, pdbBytes) @@ -595,8 +595,8 @@ End Class" Private ReadOnly _builder As CommonPEModuleBuilder Private ReadOnly _objectType As NamespaceTypeDefinitionNoBase - Friend Sub New(builder As CommonPEModuleBuilder, objectType As INamespaceTypeDefinition) - MyBase.New(DirectCast(builder.CommonSourceModule, SourceModuleSymbol), builder.EmitOptions, builder.OutputKind, builder.SerializationProperties, builder.ManifestResources) + Friend Sub New(builder As CommonPEModuleBuilder, emitOptions As EmitOptions, objectType As INamespaceTypeDefinition) + MyBase.New(DirectCast(builder.CommonSourceModule, SourceModuleSymbol), emitOptions, builder.OutputKind, builder.SerializationProperties, builder.ManifestResources) _builder = builder _objectType = New NamespaceTypeDefinitionNoBase(objectType) diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/TupleTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/TupleTests.vb index 300405b68fd12..9405e8870e5fe 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/TupleTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/TupleTests.vb @@ -142,9 +142,9 @@ End Class" Dim appRef = app.Assembly.Identity.Name AssertEx.SetEqual({"corlib 2.0", appRef + " 0.0"}, reader.DumpAssemblyReferences()) - AssertEx.SetEqual({"Object, System, AssemblyRef:corlib", - "ValueTuple`2, System, AssemblyRef:" + appRef, ' ValueTuple comes from app, not corlib - ", System, AssemblyRef:" + appRef}, + AssertEx.SetEqual({"Object, System, AssemblyReference:corlib", + "ValueTuple`2, System, AssemblyReference:" + appRef, ' ValueTuple comes from app, not corlib + ", System, AssemblyReference:" + appRef}, reader.DumpTypeReferences()) End Using End Sub diff --git a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs index f5d30937337f8..124a802cec856 100644 --- a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs +++ b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs @@ -260,22 +260,52 @@ public static IEnumerable DumpTypeReferences(this MetadataReader reader) } public static string Dump(this MetadataReader reader, EntityHandle handle) + { + string value = DumpRec(reader, handle); + string kind = handle.Kind.ToString(); + if (value != null) + { + return $"{kind}:{value}"; + } + else + { + return kind; + } + } + + private static string DumpRec(this MetadataReader reader, EntityHandle handle) { switch (handle.Kind) { case HandleKind.AssemblyReference: - return "AssemblyRef:" + reader.GetString(reader.GetAssemblyReference((AssemblyReferenceHandle)handle).Name); + return reader.GetString(reader.GetAssemblyReference((AssemblyReferenceHandle)handle).Name); case HandleKind.TypeDefinition: - return "TypeDef:" + reader.GetString(reader.GetTypeDefinition((TypeDefinitionHandle)handle).Name); + return reader.GetString(reader.GetTypeDefinition((TypeDefinitionHandle)handle).Name); case HandleKind.MethodDefinition: - var method = reader.GetMethodDefinition((MethodDefinitionHandle)handle); - var blob = reader.GetBlobReader(method.Signature); - var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, reader, genericContext: null); - var signature = decoder.DecodeMethodSignature(ref blob); - var parameters = signature.ParameterTypes.Join(", "); - return $"MethodDef: {signature.ReturnType} {reader.GetString(method.Name)}({parameters})"; + { + var method = reader.GetMethodDefinition((MethodDefinitionHandle)handle); + var blob = reader.GetBlobReader(method.Signature); + var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, reader, genericContext: null); + var signature = decoder.DecodeMethodSignature(ref blob); + var parameters = signature.ParameterTypes.Join(", "); + return $"{signature.ReturnType} {reader.GetString(method.Name)}({parameters})"; + } + case HandleKind.MemberReference: + { + var member = reader.GetMemberReference((MemberReferenceHandle)handle); + var blob = reader.GetBlobReader(member.Signature); + var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, reader, genericContext: null); + var signature = decoder.DecodeMethodSignature(ref blob); + var parameters = signature.ParameterTypes.Join(", "); + return $"{signature.ReturnType} {DumpRec(reader, member.Parent)}{reader.GetString(member.Name)}({parameters})"; + } + case HandleKind.TypeReference: + { + var type = reader.GetTypeReference((TypeReferenceHandle)handle); + return $"{reader.GetString(type.Namespace)}.{reader.GetString(type.Name)}"; + } default: - return handle.Kind.ToString(); + return null; } } From 5a259513a9bae12676ed5009c9d26b2d91c33ce2 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 13 Apr 2017 14:17:49 -0700 Subject: [PATCH 012/284] Filter "missing method body" error when emitting metadata-only (#18542) --- .../Portable/Compilation/CSharpCompilation.cs | 9 ++++- .../Test/Emit/Emit/CompilationEmitTests.cs | 35 +++++++++++++------ .../Core/Portable/Compilation/Compilation.cs | 9 +++-- .../Compilation/VisualBasicCompilation.vb | 2 +- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index fe5d5899819ad..915ff1af34987 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2311,7 +2311,14 @@ internal override bool CompileMethods( { // The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that the emitter // does not attempt to emit if there are declaration errors (but we do insert all errors from method body binding...) - bool hasDeclarationErrors = !FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, true, cancellationToken)); + PooledHashSet excludeDiagnostics = null; + if (emitMetadataOnly) + { + excludeDiagnostics = PooledHashSet.GetInstance(); + excludeDiagnostics.Add((int)ErrorCode.ERR_ConcreteMissingBody); + } + bool hasDeclarationErrors = !FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, true, cancellationToken), excludeDiagnostics); + excludeDiagnostics?.Free(); // TODO (tomat): NoPIA: // EmbeddedSymbolManager.MarkAllDeferredSymbolsAsReferenced(this) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 69dc56b4444cd..22295584ff9bb 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -321,7 +321,6 @@ public void RefAssembly_ReferenceAssemblyAttributeAlsoInSource() [InlineData("public struct S { private int i; }", "public struct S { }", Match.Different)] [InlineData("private int i;", "", Match.RefOut)] [InlineData("public C() { }", "", Match.BothMetadataAndRefOut)] - //[InlineData("public int NoBody();", "public int NoBody() { }", Match.BothMetadataAndRefOut)] // PROTOTYPE(refout) Further refinement https://github.com/dotnet/roslyn/issues/17612 public void RefAssembly_InvariantToSomeChanges(string left, string right, Match expectedMatch) { string sourceTemplate = @" @@ -569,26 +568,40 @@ private static void VerifyRefAssemblyClient(string lib_cs, string source, Action [Theory] [InlineData("public int M() { error(); }", true)] - [InlineData("public int M() { error() }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public int M() { error() }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public int M();", true)] + [InlineData("public int M() { int Local(); }", true)] + [InlineData("public C();", true)] + [InlineData("~ C();", true)] [InlineData("public Error M() { return null; }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public static explicit operator C(int i);", true)] + [InlineData("public async Task M();", false)] + [InlineData("partial void M(); partial void M();", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 public void RefAssembly_IgnoresSomeDiagnostics(string change, bool expectSuccess) { string sourceTemplate = @" -public class C +using System.Threading.Tasks; +public partial class C { CHANGE } "; - string source = sourceTemplate.Replace("CHANGE", change); - string name = GetUniqueName(); - CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source), - options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + VerifyIgnoresDiagnostics(EmitOptions.Default.WithEmitMetadataOnly(false).WithTolerateErrors(false), success: false); + VerifyIgnoresDiagnostics(EmitOptions.Default.WithEmitMetadataOnly(true).WithTolerateErrors(false), success: expectSuccess); - using (var output = new MemoryStream()) + void VerifyIgnoresDiagnostics(EmitOptions emitOptions, bool success) { - var emitResult = comp1.Emit(output, options: EmitOptions.Default.WithEmitMetadataOnly(true)); - Assert.Equal(expectSuccess, emitResult.Success); - Assert.Equal(!expectSuccess, emitResult.Diagnostics.Any()); + string source = sourceTemplate.Replace("CHANGE", change); + string name = GetUniqueName(); + CSharpCompilation comp = CreateCompilationWithMscorlib(Parse(source), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + + using (var output = new MemoryStream()) + { + var emitResult = comp.Emit(output, options: emitOptions); + Assert.Equal(!success, emitResult.Diagnostics.HasAnyErrors()); + Assert.Equal(success, emitResult.Success); + } } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index a550001f4e3d2..d1d232aa1fafc 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1151,7 +1151,7 @@ internal void CompleteCompilationEventQueue_NoLock() /// True if there were no errors or warnings-as-errors. internal bool FilterAndAppendAndFreeDiagnostics(DiagnosticBag accumulator, ref DiagnosticBag incoming) { - bool result = FilterAndAppendDiagnostics(accumulator, incoming.AsEnumerableWithoutResolution()); + bool result = FilterAndAppendDiagnostics(accumulator, incoming.AsEnumerableWithoutResolution(), exclude: null); incoming.Free(); incoming = null; return result; @@ -1161,13 +1161,18 @@ internal bool FilterAndAppendAndFreeDiagnostics(DiagnosticBag accumulator, ref D /// Filter out warnings based on the compiler options (/nowarn, /warn and /warnaserror) and the pragma warning directives. /// /// True when there is no error. - internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable incoming) + internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable incoming, HashSet exclude) { bool hasError = false; bool reportSuppressedDiagnostics = Options.ReportSuppressedDiagnostics; foreach (Diagnostic d in incoming) { + if (exclude?.Contains(d.Code) == true) + { + continue; + } + var filtered = Options.FilterDiagnostic(d); if (filtered == null || (!reportSuppressedDiagnostics && filtered.IsSuppressed)) diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index bbb44f49d5c2b..a0b37688456ff 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2221,7 +2221,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that we don't emit ' metadata if there are declaration errors or method body errors (but we do insert all errors from method body binding...) - Dim hasDeclarationErrors = Not FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, True, cancellationToken)) + Dim hasDeclarationErrors = Not FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, True, cancellationToken), exclude:=Nothing) Dim moduleBeingBuilt = DirectCast(moduleBuilder, PEModuleBuilder) From 80e794ea297c14ae0a371b47d92e0b3ad6f1bcf7 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 14 Apr 2017 13:00:52 -0700 Subject: [PATCH 013/284] Fix refout crash with GetEntryPoint when Main is private (#18696) --- .../Test/Emit/Emit/CompilationEmitTests.cs | 52 +++++++++++++++++++ .../Core/Portable/PEWriter/MetadataWriter.cs | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 22295584ff9bb..05613e9f0c4f3 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -238,6 +238,58 @@ public static void Main() } } + [Fact] + public void EmitRefAssemblyPrivateMain() + { + CSharpCompilation comp = CreateCompilationWithMscorlib(@" +public class C +{ + internal static void Main() + { + System.Console.WriteLine(""hello""); + } +} +", options: TestOptions.DebugExe); + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + // Previously, this would crash when trying to get the entry point for the ref assembly + // (but the Main method is not emitted in the ref assembly...) + EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput, + options: new EmitOptions(includePrivateMembers: false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + VerifyEntryPoint(output, expectZero: false); + VerifyMethods(output, new[] { "void C.Main()", "C..ctor()" }); + + VerifyEntryPoint(metadataOutput, expectZero: true); + VerifyMethods(metadataOutput, new[] { "C..ctor()" }); + } + + void VerifyEntryPoint(MemoryStream stream, bool expectZero) + { + stream.Position = 0; + int entryPoint = new PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress; + Assert.Equal(expectZero, entryPoint == 0); + } + + void VerifyMethods(MemoryStream stream, string[] expectedMethods) + { + stream.Position = 0; + var metadataRef = AssemblyMetadata.CreateFromImage(stream.ToArray()).GetReference(); + + var compWithMetadata = CreateCompilation("", references: new[] { MscorlibRef, metadataRef }, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + AssertEx.Equal( + expectedMethods, + ((NamedTypeSymbol)compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last() + .GlobalNamespace.GetMember("C")).GetMembers().Select(m => m.ToTestDisplayString())); + } + } + [Fact] public void RefAssembly_HasReferenceAssemblyAttribute() { diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 930a5c7d8cdec..8c910f748deaf 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -1846,7 +1846,7 @@ public PortablePdbBuilder GetPortablePdbBuilder(ImmutableArray typeSystemRo internal void GetEntryPoints(out MethodDefinitionHandle entryPointHandle, out MethodDefinitionHandle debugEntryPointHandle) { - if (IsFullMetadata) + if (IsFullMetadata && !MetadataOnly) { // PE entry point is set for executable programs IMethodReference entryPoint = module.PEEntryPoint; From 4060bb346dc0cd9eb31cce1a0886673afe65d49d Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Fri, 14 Apr 2017 13:47:42 -0700 Subject: [PATCH 014/284] Async main doc (#18081) * Add markdown feature documents * Update testing plan --- docs/features/async-main.md | 12 +++++++ docs/features/async-main.test.md | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 docs/features/async-main.md create mode 100644 docs/features/async-main.test.md diff --git a/docs/features/async-main.md b/docs/features/async-main.md new file mode 100644 index 0000000000000..b98068cd8c603 --- /dev/null +++ b/docs/features/async-main.md @@ -0,0 +1,12 @@ +# Async Task Main +## [dotnet/csharplang proposal](https://github.com/dotnet/csharplang/blob/master/proposals/async-main.md) + +## Technical Details + +* The compiler must recognize `Task` and `Task` as valid entrypoint return types in addition to `void` and `int`. +* The compiler must allow `async` to be placed on a main method that returns a `Task` or a `Task` (but not void). +* The compiler must generate a shim method `$EntrypointMain` that mimics the arguments of the user-defined main. + * `static async Task Main(...)` -> `static void $EntrypointMain(...)` + * `static async Task Main(...)` -> `static int $EntrypointMain(...)` + * The parameters between the user-defined main and the generated main should match exactly. +* The body of the generated main should be `return Main(args...).GetAwaiter().GetResult();` \ No newline at end of file diff --git a/docs/features/async-main.test.md b/docs/features/async-main.test.md new file mode 100644 index 0000000000000..9901e16e0bc3e --- /dev/null +++ b/docs/features/async-main.test.md @@ -0,0 +1,56 @@ +Main areas to test: +* Signature acceptance / rejection +* Method body creation + +# Signature acceptance / rejection + +## Single Mains +Classes that contain only a single "main" method + +### Single legal main +* Past: Ok +* New 7: Ok +* New 7.1 Ok + +### Single async (void) main +* Past: ERR (Async can't be main) +* New 7: ERR (Update to get this to work) +* New 7.1: Ok + +### Single async (Task) main +* Past: ERR (No entrypoints found), WARN (has the wrong signature to be an entry point) +* New 7: ERR (Update to get this to work) +* New 7.1 Ok + +### Single async (Task) main +* Past: ERR (No entrypoints found), WARN (has the wrong signature) +* New 7: ERR (Update to get this to work) +* New 7.1: Ok + +## Multiple Mains +Classes that contain more than one main + +### Multiple legal mains +* Past: Err +* New 7: Err +* New 7.1: Err + +### Single legal main, single async (void) main +* Past: Err (an entrypoint cannot be marked with async) +* New 7: Err (new error here?) +* New 7.1: Err (new error here? "void is not an acceptable async return type") + +### Single legal main, single legal Task main +* Past: Ok (warning: task main has wrong signature) +* New 7: Ok (new warning here) +* New 7.1: Ok (new warning here?) + +### Single legal main, single legal Task main +* Past: Ok (warning: task main has wrong signature) +* New 7: Ok (new warning here) +* New 7.1: Ok (new warning here?) + +# Method body creation + +* Inspect IL for correct codegen. +* Make sure that attributes are correctly applied to the synthesized mains. From 306ca441920f60fb2051d12250e55b53131553c4 Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Fri, 14 Apr 2017 15:32:16 -0700 Subject: [PATCH 015/284] Update async-main.test.md --- docs/features/async-main.test.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/features/async-main.test.md b/docs/features/async-main.test.md index 9901e16e0bc3e..4fc7de5c129fc 100644 --- a/docs/features/async-main.test.md +++ b/docs/features/async-main.test.md @@ -54,3 +54,17 @@ Classes that contain more than one main * Inspect IL for correct codegen. * Make sure that attributes are correctly applied to the synthesized mains. + +## Broken methods on Task and Task + +* GetAwatier or GetResult are missing +* GetAwaiter or GetResult don't have the required signature +* Has the right shape, but types are missing + +## Task or Task is missing + +This will be caught during entrypoint detection, should be a binding error. + +## Void or int is missing + +If Task can be found, but void or int can't be found, then the compiler should behave gracefully. From 1f68c4e7a9e9b2fa5305c71530d1428a1db7fa5a Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Tue, 18 Apr 2017 10:39:04 -0700 Subject: [PATCH 016/284] Update async-main.test.md --- docs/features/async-main.test.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/features/async-main.test.md b/docs/features/async-main.test.md index 4fc7de5c129fc..9b4a51467e559 100644 --- a/docs/features/async-main.test.md +++ b/docs/features/async-main.test.md @@ -68,3 +68,32 @@ This will be caught during entrypoint detection, should be a binding error. ## Void or int is missing If Task can be found, but void or int can't be found, then the compiler should behave gracefully. + +# Vlad Test Plan + +## Public interface of compiler APIs + -N/A? + +## General functionality +- `Task` and `Task` returns are allowed. Do we require `async`. +- Multiple valid/invalid async candidates. +- process exit code on success (void and int) and on exception +- `/main:` cmd switch is still functional + +## Old versions, compat +- langver +- when both async and regular Main avaialble. With old/new compiler versions. +- async void Main. With old.new langver. With another applicable Main present. + +## Type and members +- Access modifiers (public, protected, internal, protected internal, private), static modifier +- Parameter modifiers (ref, out, params) +- STA attribute +- Partial method +- Named and optional parameters +- `Task` +- Task-Like types + +## Debug +- F11 works +- stepping through Main From 93d25c60a98172915063ca813d581e199bd946c8 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 18 Apr 2017 11:02:21 -0700 Subject: [PATCH 017/284] Fixing crash with refout on private setter on public property (#18771) --- .../Emitter/Model/PropertySymbolAdapter.cs | 94 ++++++----- .../Test/Emit/Emit/CompilationEmitTests.cs | 156 ++++++++++++++---- .../Emit/NoPia/CommonEmbeddedProperty.cs | 19 +-- .../Core/Portable/PEWriter/Members.cs | 8 +- .../Core/Portable/PEWriter/MetadataVisitor.cs | 2 +- .../Core/Portable/PEWriter/MetadataWriter.cs | 2 +- .../Portable/Emit/PropertySymbolAdapter.vb | 26 ++- .../Test/Emit/Emit/CompilationEmitTests.vb | 125 +++++++++++--- 8 files changed, 297 insertions(+), 135 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs index a9cdc135ae298..d76d3a4868743 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.Cci; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.Emit; @@ -10,41 +11,38 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal partial class PropertySymbol : - Cci.IPropertyDefinition + IPropertyDefinition { #region IPropertyDefinition Members - IEnumerable Cci.IPropertyDefinition.Accessors + IEnumerable IPropertyDefinition.GetAccessors(EmitContext context) { - get - { - CheckDefinitionInvariant(); + CheckDefinitionInvariant(); - var getMethod = this.GetMethod; - if ((object)getMethod != null) - { - yield return getMethod; - } + IMethodReference getMethod = this.GetMethod; + if (getMethod != null && getMethod.GetResolvedMethod(context).ShouldInclude(context)) + { + yield return getMethod; + } - var setMethod = this.SetMethod; - if ((object)setMethod != null) - { - yield return setMethod; - } + IMethodReference setMethod = this.SetMethod; + if (setMethod != null && setMethod.GetResolvedMethod(context).ShouldInclude(context)) + { + yield return setMethod; + } - SourcePropertySymbol sourceProperty = this as SourcePropertySymbol; - if ((object)sourceProperty != null) + SourcePropertySymbol sourceProperty = this as SourcePropertySymbol; + if ((object)sourceProperty != null) + { + SynthesizedSealedPropertyAccessor synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt; + if ((object)synthesizedAccessor != null) { - SynthesizedSealedPropertyAccessor synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt; - if ((object)synthesizedAccessor != null) - { - yield return synthesizedAccessor; - } + yield return synthesizedAccessor; } } } - MetadataConstant Cci.IPropertyDefinition.DefaultValue + MetadataConstant IPropertyDefinition.DefaultValue { get { @@ -53,7 +51,7 @@ MetadataConstant Cci.IPropertyDefinition.DefaultValue } } - Cci.IMethodReference Cci.IPropertyDefinition.Getter + IMethodReference IPropertyDefinition.Getter { get { @@ -68,7 +66,7 @@ Cci.IMethodReference Cci.IPropertyDefinition.Getter } } - bool Cci.IPropertyDefinition.HasDefaultValue + bool IPropertyDefinition.HasDefaultValue { get { @@ -77,7 +75,7 @@ bool Cci.IPropertyDefinition.HasDefaultValue } } - bool Cci.IPropertyDefinition.IsRuntimeSpecial + bool IPropertyDefinition.IsRuntimeSpecial { get { @@ -95,7 +93,7 @@ internal virtual bool HasRuntimeSpecialName } } - bool Cci.IPropertyDefinition.IsSpecialName + bool IPropertyDefinition.IsSpecialName { get { @@ -104,16 +102,16 @@ bool Cci.IPropertyDefinition.IsSpecialName } } - ImmutableArray Cci.IPropertyDefinition.Parameters + ImmutableArray IPropertyDefinition.Parameters { get { CheckDefinitionInvariant(); - return StaticCast.From(this.Parameters); + return StaticCast.From(this.Parameters); } } - Cci.IMethodReference Cci.IPropertyDefinition.Setter + IMethodReference IPropertyDefinition.Setter { get { @@ -142,7 +140,7 @@ private void CheckDefinitionInvariantAllowEmbedded() Debug.Assert(this.ContainingModule is SourceModuleSymbol || this.ContainingAssembly.IsLinked); } - Cci.CallingConvention Cci.ISignature.CallingConvention + CallingConvention ISignature.CallingConvention { get { @@ -151,7 +149,7 @@ Cci.CallingConvention Cci.ISignature.CallingConvention } } - ushort Cci.ISignature.ParameterCount + ushort ISignature.ParameterCount { get { @@ -160,31 +158,31 @@ ushort Cci.ISignature.ParameterCount } } - ImmutableArray Cci.ISignature.GetParameters(EmitContext context) + ImmutableArray ISignature.GetParameters(EmitContext context) { CheckDefinitionInvariant(); - return StaticCast.From(this.Parameters); + return StaticCast.From(this.Parameters); } - ImmutableArray Cci.ISignature.ReturnValueCustomModifiers + ImmutableArray ISignature.ReturnValueCustomModifiers { get { CheckDefinitionInvariantAllowEmbedded(); - return this.TypeCustomModifiers.As(); + return this.TypeCustomModifiers.As(); } } - ImmutableArray Cci.ISignature.RefCustomModifiers + ImmutableArray ISignature.RefCustomModifiers { get { CheckDefinitionInvariantAllowEmbedded(); - return this.RefCustomModifiers.As(); + return this.RefCustomModifiers.As(); } } - bool Cci.ISignature.ReturnValueIsByRef + bool ISignature.ReturnValueIsByRef { get { @@ -193,7 +191,7 @@ bool Cci.ISignature.ReturnValueIsByRef } } - Cci.ITypeReference Cci.ISignature.GetType(EmitContext context) + ITypeReference ISignature.GetType(EmitContext context) { CheckDefinitionInvariantAllowEmbedded(); return ((PEModuleBuilder)context.Module).Translate(this.Type, @@ -205,7 +203,7 @@ Cci.ITypeReference Cci.ISignature.GetType(EmitContext context) #region ITypeDefinitionMember Members - Cci.ITypeDefinition Cci.ITypeDefinitionMember.ContainingTypeDefinition + ITypeDefinition ITypeDefinitionMember.ContainingTypeDefinition { get { @@ -214,7 +212,7 @@ Cci.ITypeDefinition Cci.ITypeDefinitionMember.ContainingTypeDefinition } } - Cci.TypeMemberVisibility Cci.ITypeDefinitionMember.Visibility + TypeMemberVisibility ITypeDefinitionMember.Visibility { get { @@ -227,7 +225,7 @@ Cci.TypeMemberVisibility Cci.ITypeDefinitionMember.Visibility #region ITypeMemberReference Members - Cci.ITypeReference Cci.ITypeMemberReference.GetContainingType(EmitContext context) + ITypeReference ITypeMemberReference.GetContainingType(EmitContext context) { CheckDefinitionInvariant(); return this.ContainingType; @@ -237,13 +235,13 @@ Cci.ITypeReference Cci.ITypeMemberReference.GetContainingType(EmitContext contex #region IReference Members - void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) + void IReference.Dispatch(MetadataVisitor visitor) { CheckDefinitionInvariant(); - visitor.Visit((Cci.IPropertyDefinition)this); + visitor.Visit((IPropertyDefinition)this); } - Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) + IDefinition IReference.AsDefinition(EmitContext context) { CheckDefinitionInvariant(); return this; @@ -253,7 +251,7 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) #region INamedEntity Members - string Cci.INamedEntity.Name + string INamedEntity.Name { get { @@ -264,7 +262,7 @@ string Cci.INamedEntity.Name #endregion - private Cci.IMethodReference GetSynthesizedSealedAccessor(MethodKind targetMethodKind) + private IMethodReference GetSynthesizedSealedAccessor(MethodKind targetMethodKind) { SourcePropertySymbol sourceProperty = this as SourcePropertySymbol; if ((object)sourceProperty != null) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 05613e9f0c4f3..696bf9bcafa7c 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -8,7 +8,6 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; -using System.Text; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -239,7 +238,7 @@ public static void Main() } [Fact] - public void EmitRefAssemblyPrivateMain() + public void EmitRefAssembly_PrivateMain() { CSharpCompilation comp = CreateCompilationWithMscorlib(@" public class C @@ -274,22 +273,114 @@ void VerifyEntryPoint(MemoryStream stream, bool expectZero) int entryPoint = new PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress; Assert.Equal(expectZero, entryPoint == 0); } + } + + [Fact] + public void EmitRefAssembly_PrivatePropertySetter() + { + CSharpCompilation comp = CreateCompilationWithMscorlib(@" +public class C +{ + public int PrivateSetter { get; private set; } +} +"); - void VerifyMethods(MemoryStream stream, string[] expectedMethods) + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) { - stream.Position = 0; - var metadataRef = AssemblyMetadata.CreateFromImage(stream.ToArray()).GetReference(); + EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput, + options: new EmitOptions(includePrivateMembers: false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); - var compWithMetadata = CreateCompilation("", references: new[] { MscorlibRef, metadataRef }, - options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateSetter.get", "void C.PrivateSetter.set", + "C..ctor()", "System.Int32 C.PrivateSetter { get; private set; }" }); + VerifyMethods(metadataOutput, new[] { "System.Int32 C.PrivateSetter.get", "C..ctor()", "System.Int32 C.PrivateSetter { get; }" }); + } + } - AssertEx.Equal( - expectedMethods, - ((NamedTypeSymbol)compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last() - .GlobalNamespace.GetMember("C")).GetMembers().Select(m => m.ToTestDisplayString())); + [Fact] + public void EmitRefAssembly_PrivatePropertyGetter() + { + CSharpCompilation comp = CreateCompilationWithMscorlib(@" +public class C +{ + public int PrivateGetter { private get; set; } +} +"); + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput, + options: new EmitOptions(includePrivateMembers: false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateGetter.get", "void C.PrivateGetter.set", + "C..ctor()", "System.Int32 C.PrivateGetter { private get; set; }" }); + VerifyMethods(metadataOutput, new[] { "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { set; }" }); } } + [Fact] + public void EmitRefAssembly_PrivateIndexerGetter() + { + CSharpCompilation comp = CreateCompilationWithMscorlib(@" +public class C +{ + public int this[int i] { private get { return 0; } set { } } +} +"); + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput, + options: new EmitOptions(includePrivateMembers: false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + VerifyMethods(output, new[] { "System.Int32 C.this[System.Int32 i].get", "void C.this[System.Int32 i].set", + "C..ctor()", "System.Int32 C.this[System.Int32 i] { private get; set; }" }); + VerifyMethods(metadataOutput, new[] { "void C.this[System.Int32 i].set", "C..ctor()", + "System.Int32 C.this[System.Int32 i] { set; }" }); + } + } + + [Fact] + public void EmitRefAssembly_PrivateAccessorOnEvent() + { + CSharpCompilation comp = CreateCompilationWithMscorlib(@" +public class C +{ + public event System.Action PrivateAdder { private add { } remove { } } + public event System.Action PrivateRemover { add { } private remove { } } +} +"); + comp.VerifyDiagnostics( + // (4,47): error CS1609: Modifiers cannot be placed on event accessor declarations + // public event System.Action PrivateAdder { private add { } remove { } } + Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "private").WithLocation(4, 47), + // (5,57): error CS1609: Modifiers cannot be placed on event accessor declarations + // public event System.Action PrivateRemover { add { } private remove { } } + Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "private").WithLocation(5, 57) + ); + } + + private static void VerifyMethods(MemoryStream stream, string[] expectedMethods) + { + stream.Position = 0; + var metadataRef = AssemblyMetadata.CreateFromImage(stream.ToArray()).GetReference(); + + var compWithMetadata = CreateCompilation("", references: new[] { MscorlibRef, metadataRef }, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + AssertEx.Equal( + expectedMethods, + compWithMetadata.GetMember("C").GetMembers().Select(m => m.ToTestDisplayString())); + } + [Fact] public void RefAssembly_HasReferenceAssemblyAttribute() { @@ -679,20 +770,21 @@ public class PublicClass var compWithReal = CreateCompilation("", references: new[] { MscorlibRef, realImage }, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); AssertEx.Equal( - compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString()), - new[] { "", "<>f__AnonymousType0<j__TPar>", "PublicClass" }); + new[] { "", "<>f__AnonymousType0<j__TPar>", "PublicClass" }, + compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString())); AssertEx.Equal( - ((NamedTypeSymbol)compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMember("PublicClass")).GetMembers() - .Select(m => m.ToTestDisplayString()), new[] { "void PublicClass.PublicMethod()", "void PublicClass.PrivateMethod()", "void PublicClass.ProtectedMethod()", "void PublicClass.InternalMethod()", - "PublicClass..ctor()" }); + "PublicClass..ctor()" }, + compWithReal.GetMember("PublicClass").GetMembers() + .Select(m => m.ToTestDisplayString())); - AssertEx.Equal(compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + AssertEx.Equal( new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", - "System.Diagnostics.DebuggableAttribute" }); + "System.Diagnostics.DebuggableAttribute" }, + compWithReal.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString())); // verify metadata (types, members, attributes) of the metadata-only assembly var emitMetadataOnly = EmitOptions.Default.WithEmitMetadataOnly(true); @@ -702,20 +794,20 @@ public class PublicClass var compWithMetadata = CreateCompilation("", references: new[] { MscorlibRef, metadataImage }, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); AssertEx.Equal( - compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString()), - new[] { "", "PublicClass" }); + new[] { "", "PublicClass" }, + compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString())); AssertEx.Equal( - ((NamedTypeSymbol)compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMember("PublicClass")).GetMembers() - .Select(m => m.ToTestDisplayString()), new[] { "void PublicClass.PublicMethod()", "void PublicClass.PrivateMethod()", "void PublicClass.ProtectedMethod()", "void PublicClass.InternalMethod()", - "PublicClass..ctor()" }); + "PublicClass..ctor()" }, + compWithMetadata.GetMember("PublicClass").GetMembers().Select(m => m.ToTestDisplayString())); - AssertEx.Equal(compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + AssertEx.Equal( new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", - "System.Diagnostics.DebuggableAttribute" }); + "System.Diagnostics.DebuggableAttribute" }, + compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString())); MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitMetadataOnly)); @@ -727,20 +819,20 @@ public class PublicClass var compWithRef = CreateCompilation("", references: new[] { MscorlibRef, refImage }, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); AssertEx.Equal( - compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString()), - new[] { "", "PublicClass" }); + new[] { "", "PublicClass" }, + compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMembers().Select(m => m.ToDisplayString())); AssertEx.Equal( - ((NamedTypeSymbol)compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GlobalNamespace.GetMember("PublicClass")).GetMembers() - .Select(m => m.ToTestDisplayString()), - new[] { "void PublicClass.PublicMethod()", "void PublicClass.ProtectedMethod()", "PublicClass..ctor()" }); + new[] { "void PublicClass.PublicMethod()", "void PublicClass.ProtectedMethod()", "PublicClass..ctor()" }, + compWithRef.GetMember("PublicClass").GetMembers().Select(m => m.ToTestDisplayString())); - AssertEx.Equal(compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + AssertEx.Equal( new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", "System.Diagnostics.DebuggableAttribute", - "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" }); + "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" }, + compWithRef.SourceModule.GetReferencedAssemblySymbols().Last().GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString())); MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitRefOnly)); } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedProperty.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedProperty.cs index a942fb11e0125..84bbd6142418c 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedProperty.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedProperty.cs @@ -81,19 +81,16 @@ Cci.IMethodReference Cci.IPropertyDefinition.Setter get { return _setter; } } - IEnumerable Cci.IPropertyDefinition.Accessors + IEnumerable Cci.IPropertyDefinition.GetAccessors(EmitContext context) { - get + if (_getter != null) + { + yield return _getter; + } + + if (_setter != null) { - if (_getter != null) - { - yield return _getter; - } - - if (_setter != null) - { - yield return _setter; - } + yield return _setter; } } diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index 4c0ffc23e73b7..7d7ae2e42e0f5 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Emit; -using Roslyn.Utilities; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; namespace Microsoft.Cci @@ -714,7 +713,7 @@ internal interface IPropertyDefinition : ISignature, ITypeDefinitionMember /// /// A list of methods that are associated with the property. /// - IEnumerable Accessors { get; } + IEnumerable GetAccessors(EmitContext context); /// /// A compile time constant value that provides the default value for the property. (Who uses this and why?) @@ -985,6 +984,11 @@ internal static bool HasBody(this IMethodDefinition methodDef) /// public static bool ShouldInclude(this ITypeDefinitionMember member, EmitContext context) { + if (context.IncludePrivateMembers) + { + return true; + } + var method = member as IMethodDefinition; if (method != null && method.IsVirtual) { diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs index 35e844b80a736..165b05231543d 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs @@ -464,7 +464,7 @@ public void Visit(IEnumerable properties) public virtual void Visit(IPropertyDefinition propertyDefinition) { - this.Visit(propertyDefinition.Accessors); + this.Visit(propertyDefinition.GetAccessors(Context)); this.Visit(propertyDefinition.Parameters); } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 8c910f748deaf..3b558b9396401 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -2579,7 +2579,7 @@ private void PopulateMethodSemanticsTableRows() foreach (IPropertyDefinition propertyDef in this.GetPropertyDefs()) { var association = GetPropertyDefIndex(propertyDef); - foreach (IMethodReference accessorMethod in propertyDef.Accessors) + foreach (IMethodReference accessorMethod in propertyDef.GetAccessors(Context)) { MethodSemanticsAttributes semantics; if (accessorMethod == propertyDef.Setter) diff --git a/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb index 432f7f5ea9dee..b98377bbda743 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb @@ -11,21 +11,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Partial Class PropertySymbol Implements IPropertyDefinition - Private ReadOnly Property IPropertyDefinitionAccessors As IEnumerable(Of IMethodReference) Implements IPropertyDefinition.Accessors - Get - CheckDefinitionInvariant() + Private Iterator Function IPropertyDefinitionAccessors(context As EmitContext) As IEnumerable(Of IMethodReference) Implements IPropertyDefinition.GetAccessors + CheckDefinitionInvariant() - If Me.GetMethod IsNot Nothing And Me.SetMethod IsNot Nothing Then - Return {Me.GetMethod, Me.SetMethod} - ElseIf Me.GetMethod IsNot Nothing Then - Return SpecializedCollections.SingletonEnumerable(Me.GetMethod) - ElseIf Me.SetMethod IsNot Nothing Then - Return SpecializedCollections.SingletonEnumerable(Me.SetMethod) - Else - Return SpecializedCollections.EmptyEnumerable(Of IMethodReference)() - End If - End Get - End Property + Dim getter As IMethodReference = Me.GetMethod + If getter IsNot Nothing AndAlso getter.GetResolvedMethod(context).ShouldInclude(context) Then + Yield getter + End If + + Dim setter As IMethodReference = Me.SetMethod + If setter IsNot Nothing AndAlso setter.GetResolvedMethod(context).ShouldInclude(context) Then + Yield setter + End If + End Function Private ReadOnly Property IPropertyDefinitionDefaultValue As MetadataConstant Implements IPropertyDefinition.DefaultValue Get diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index 68f38a335d6cb..f79bc3827226a 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -297,6 +297,73 @@ End Class End Using End Sub + + Private Sub EmitRefAssembly_PrivatePropertyGetter() + Dim source As String = " +Public Class C + Property P As Integer + Private Get + Return 0 + End Get + Set + End Set + End Property +End Class" + + Dim comp = CreateCompilationWithMscorlib(source, options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using metadataOutput As New MemoryStream() + Dim emitResult = comp.Emit(output, metadataPEStream:=metadataOutput, + options:=EmitOptions.Default.WithIncludePrivateMembers(False)) + Assert.True(emitResult.Success) + emitResult.Diagnostics.Verify() + + VerifyMethod(output, {"Sub C..ctor()", "Function C.get_P() As System.Int32", "Sub C.set_P(Value As System.Int32)", "Property C.P As System.Int32"}) + VerifyMethod(metadataOutput, {"Sub C..ctor()", "Sub C.set_P(Value As System.Int32)", "WriteOnly Property C.P As System.Int32"}) + End Using + End Using + End Sub + + + Private Sub EmitRefAssembly_PrivatePropertySetter() + Dim source As String = " +Public Class C + Property P As Integer + Get + Return 0 + End Get + Private Set + End Set + End Property +End Class" + + Dim comp = CreateCompilationWithMscorlib(source, options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using metadataOutput As New MemoryStream() + Dim emitResult = comp.Emit(output, metadataPEStream:=metadataOutput, + options:=EmitOptions.Default.WithIncludePrivateMembers(False)) + Assert.True(emitResult.Success) + emitResult.Diagnostics.Verify() + + VerifyMethod(output, {"Sub C..ctor()", "Function C.get_P() As System.Int32", "Sub C.set_P(Value As System.Int32)", "Property C.P As System.Int32"}) + VerifyMethod(metadataOutput, {"Sub C..ctor()", "Function C.get_P() As System.Int32", "ReadOnly Property C.P As System.Int32"}) + End Using + End Using + End Sub + + Private Shared Sub VerifyMethod(stream As MemoryStream, expectedMethods As String()) + stream.Position = 0 + Dim metadataRef = AssemblyMetadata.CreateFromImage(stream.ToArray()).GetReference() + + Dim compWithMetadata = CreateCompilation("", references:={MscorlibRef, metadataRef}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + + AssertEx.Equal(expectedMethods, + compWithMetadata.GetMember(Of NamedTypeSymbol)("C").GetMembers().Select(Function(m) m.ToTestDisplayString())) + End Sub + Public Sub RefAssembly_HasReferenceAssemblyAttribute() Dim emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False) @@ -734,20 +801,22 @@ End Class" Dim compWithReal = CreateCompilation("", references:={MscorlibRef, realImage}, options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) Dim realAssembly = compWithReal.SourceModule.GetReferencedAssemblySymbols().Last() - AssertEx.SetEqual(realAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString()), - {"", "PublicClass"}) + AssertEx.SetEqual( + {"", "PublicClass"}, + realAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString())) AssertEx.SetEqual( - DirectCast(realAssembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). - Select(Function(m) m.ToTestDisplayString()), - {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", - "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", - "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}) + {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", + "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", + "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}, + compWithReal.GetMember(Of NamedTypeSymbol)("PublicClass").GetMembers(). + Select(Function(m) m.ToTestDisplayString())) - AssertEx.SetEqual(realAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + AssertEx.SetEqual( {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", - "System.Diagnostics.DebuggableAttribute"}) + "System.Diagnostics.DebuggableAttribute"}, + realAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString())) ' verify metadata (types, members, attributes) of the metadata-only assembly Dim emitMetadataOnly = EmitOptions.Default.WithEmitMetadataOnly(True) @@ -757,20 +826,22 @@ End Class" Dim compWithMetadata = CreateCompilation("", references:={MscorlibRef, metadataImage}, options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) Dim metadataAssembly As AssemblySymbol = compWithMetadata.SourceModule.GetReferencedAssemblySymbols().Last() - AssertEx.SetEqual(metadataAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString()), - {"", "PublicClass"}) + AssertEx.SetEqual( + {"", "PublicClass"}, + metadataAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString())) AssertEx.SetEqual( - DirectCast(metadataAssembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). - Select(Function(m) m.ToTestDisplayString()), - {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", - "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", - "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}) + {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", + "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", + "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}, + compWithMetadata.GetMember(Of NamedTypeSymbol)("PublicClass").GetMembers(). + Select(Function(m) m.ToTestDisplayString())) - AssertEx.SetEqual(metadataAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + AssertEx.SetEqual( {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", - "System.Diagnostics.DebuggableAttribute"}) + "System.Diagnostics.DebuggableAttribute"}, + metadataAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString())) MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitMetadataOnly)) @@ -782,20 +853,22 @@ End Class" Dim compWithRef = CreateCompilation("", references:={MscorlibRef, refImage}, options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) Dim refAssembly As AssemblySymbol = compWithRef.SourceModule.GetReferencedAssemblySymbols().Last() - AssertEx.SetEqual(refAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString()), - {"", "PublicClass"}) + AssertEx.SetEqual( + {"", "PublicClass"}, + refAssembly.GlobalNamespace.GetMembers().Select(Function(m) m.ToDisplayString())) AssertEx.SetEqual( - DirectCast(refAssembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). - Select(Function(m) m.ToTestDisplayString()), - {"Sub PublicClass..ctor()", "Sub PublicClass.PublicMethod()", - "Sub PublicClass.ProtectedMethod()", "Sub PublicClass.AbstractMethod()"}) + {"Sub PublicClass..ctor()", "Sub PublicClass.PublicMethod()", + "Sub PublicClass.ProtectedMethod()", "Sub PublicClass.AbstractMethod()"}, + compWithRef.GetMember(Of NamedTypeSymbol)("PublicClass").GetMembers(). + Select(Function(m) m.ToTestDisplayString())) - AssertEx.SetEqual(refAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + AssertEx.SetEqual( {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", "System.Diagnostics.DebuggableAttribute", - "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"}) + "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"}, + refAssembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString())) MetadataReaderUtils.AssertEmptyOrThrowNull(comp.EmitToArray(emitRefOnly)) End Sub From 8058a70b432911829e88f7aea2736ae4f1c4fd60 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 19 Apr 2017 16:39:59 -0700 Subject: [PATCH 018/284] Address additional feedback on PR #18771 (#18828) --- .../Emitter/Model/PropertySymbolAdapter.cs | 10 +++---- .../Test/Emit/Emit/CompilationEmitTests.cs | 28 +++++++++++++++++++ .../Portable/Emit/PropertySymbolAdapter.vb | 8 +++--- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs index d76d3a4868743..217a3dccd7f62 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs @@ -19,20 +19,20 @@ IEnumerable IPropertyDefinition.GetAccessors(EmitContext conte { CheckDefinitionInvariant(); - IMethodReference getMethod = this.GetMethod; - if (getMethod != null && getMethod.GetResolvedMethod(context).ShouldInclude(context)) + MethodSymbol getMethod = this.GetMethod; + if (getMethod != null && getMethod.ShouldInclude(context)) { yield return getMethod; } - IMethodReference setMethod = this.SetMethod; - if (setMethod != null && setMethod.GetResolvedMethod(context).ShouldInclude(context)) + MethodSymbol setMethod = this.SetMethod; + if (setMethod != null && setMethod.ShouldInclude(context)) { yield return setMethod; } SourcePropertySymbol sourceProperty = this as SourcePropertySymbol; - if ((object)sourceProperty != null) + if ((object)sourceProperty != null && sourceProperty.ShouldInclude(context)) { SynthesizedSealedPropertyAccessor synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt; if ((object)synthesizedAccessor != null) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 5c14eeabdaa8f..abc39c4f664aa 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -348,6 +348,34 @@ public class C } } + [Fact] + public void EmitRefAssembly_SealedPropertyWithInternalInheritedGetter() + { + CSharpCompilation comp = CreateStandardCompilation(@" +public class Base +{ + public virtual int Property { internal get { return 0; } set { } } +} +public class C : Base +{ + public sealed override int Property { set { } } +} +"); + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput, + options: new EmitOptions(includePrivateMembers: false)); + emitResult.Diagnostics.Verify(); + Assert.True(emitResult.Success); + + VerifyMethods(output, new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); + // A getter is synthesized on C.Property so that it can be marked as sealed. It is emitted despite being internal because it is virtual. + VerifyMethods(metadataOutput, new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); + } + } + [Fact] public void EmitRefAssembly_PrivateAccessorOnEvent() { diff --git a/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb index b98377bbda743..761e6fd41cbff 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PropertySymbolAdapter.vb @@ -14,13 +14,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Iterator Function IPropertyDefinitionAccessors(context As EmitContext) As IEnumerable(Of IMethodReference) Implements IPropertyDefinition.GetAccessors CheckDefinitionInvariant() - Dim getter As IMethodReference = Me.GetMethod - If getter IsNot Nothing AndAlso getter.GetResolvedMethod(context).ShouldInclude(context) Then + Dim getter As MethodSymbol = Me.GetMethod + If getter IsNot Nothing AndAlso getter.ShouldInclude(context) Then Yield getter End If - Dim setter As IMethodReference = Me.SetMethod - If setter IsNot Nothing AndAlso setter.GetResolvedMethod(context).ShouldInclude(context) Then + Dim setter As MethodSymbol = Me.SetMethod + If setter IsNot Nothing AndAlso setter.ShouldInclude(context) Then Yield setter End If End Function From 3c12e98413d27d1a759a34ff62226a40a87fe78e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 24 Apr 2017 17:19:03 -0700 Subject: [PATCH 019/284] Move DocumentHighlights (i.e. HighlightReferences) out of process. --- .../CSharp/CSharpEditorFeatures.csproj | 2 - .../CSharpDocumentHighlightsService.cs | 13 --- src/EditorFeatures/Core/EditorFeatures.csproj | 19 ++-- ...viewReferenceHighlightingTaggerProvider.cs | 2 +- ...HighlightingAdditionalReferenceProvider.cs | 14 --- ...htReferenceCommandHandler.StartComparer.cs | 2 +- ...ivateToHighlightReferenceCommandHandler.cs | 2 +- ...ReferenceHighlightingViewTaggerProvider.cs | 3 +- .../Tags}/DefinitionHighlightTag.cs | 2 +- .../Tags}/DefinitionHighlightTagDefinition.cs | 2 +- .../Tags}/ReferenceHighlightTag.cs | 2 +- .../Tags}/WrittenReferenceHighlightTag.cs | 2 +- .../WrittenReferenceHighlightTagDefinition.cs | 2 +- .../AbstractReferenceHighlightingTests.vb | 11 ++- .../VisualBasic/BasicEditorFeatures.vbproj | 2 - ...HighlightingAdditionalReferenceProvider.vb | 18 ---- .../CSharp/Portable/CSharpFeatures.csproj | 1 + .../CSharpDocumentHighlightsService.cs} | 26 ++--- .../AbstractDocumentHighlightsService.cs | 61 ++++++++---- ...bstractDocumentHighlightsService_Remote.cs | 30 ++++++ .../DocumentHighlightingOptions.cs | 15 +++ .../IDocumentHighlightsService.cs | 3 +- .../IRemoteDocumentHighlights.cs | 94 +++++++++++++++++++ src/Features/Core/Portable/Features.csproj | 5 + .../VisualBasic/Portable/BasicFeatures.vbproj | 1 + .../VisualBasicDocumentHighlightsService.vb | 6 +- ...bstractTableDataSourceFindUsagesContext.cs | 2 +- .../WithReferencesFindUsagesContext.cs | 2 +- .../WithoutReferencesFindUsagesContext.cs | 2 +- .../Entries/DocumentSpanEntry.cs | 4 +- .../CSharp/CSharpReferenceHighlighting.cs | 3 +- .../VisualBasic/BasicReferenceHighlighting.cs | 3 +- .../TestUtilities/WellKnownTagNames.cs | 2 +- .../Remote/ServiceHub/ServiceHub.csproj | 1 + .../CodeAnalysisService_DocumentHighlights.cs | 28 ++++++ 35 files changed, 270 insertions(+), 117 deletions(-) delete mode 100644 src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs delete mode 100644 src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs rename src/EditorFeatures/Core/{Implementation => }/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs (87%) rename src/EditorFeatures/Core/{Implementation => }/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs (98%) rename src/EditorFeatures/Core/{Implementation => }/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs (98%) rename src/EditorFeatures/Core/{Implementation/ReferenceHighlighting => ReferenceHighlighting/Tags}/DefinitionHighlightTag.cs (88%) rename src/EditorFeatures/Core/{Implementation/ReferenceHighlighting => ReferenceHighlighting/Tags}/DefinitionHighlightTagDefinition.cs (92%) rename src/EditorFeatures/Core/{Implementation/ReferenceHighlighting => ReferenceHighlighting/Tags}/ReferenceHighlightTag.cs (87%) rename src/EditorFeatures/Core/{Implementation/ReferenceHighlighting => ReferenceHighlighting/Tags}/WrittenReferenceHighlightTag.cs (88%) rename src/EditorFeatures/Core/{Implementation/ReferenceHighlighting => ReferenceHighlighting/Tags}/WrittenReferenceHighlightTagDefinition.cs (91%) delete mode 100644 src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb rename src/{EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs => Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs} (70%) rename src/{EditorFeatures/Core/Implementation/ReferenceHighlighting => Features/Core/Portable/DocumentHighlighting}/AbstractDocumentHighlightsService.cs (82%) create mode 100644 src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs create mode 100644 src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs rename src/{EditorFeatures/Core/Implementation/ReferenceHighlighting => Features/Core/Portable/DocumentHighlighting}/IDocumentHighlightsService.cs (95%) create mode 100644 src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs rename src/{EditorFeatures/VisualBasic/HighlightReferences => Features/VisualBasic/Portable/DocumentHighlighting}/VisualBasicDocumentHighlightsService.vb (73%) create mode 100644 src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs diff --git a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj index 516c7b22ee5eb..2fd0b81875d7f 100644 --- a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj +++ b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj @@ -136,14 +136,12 @@ - - diff --git a/src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs b/src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs deleted file mode 100644 index c1f3baf669ed0..0000000000000 --- a/src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Composition; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.HighlightReferences -{ - [ExportLanguageService(typeof(IDocumentHighlightsService), LanguageNames.CSharp), Shared] - internal class CSharpDocumentHighlightsService : AbstractDocumentHighlightsService - { - } -} diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 8d01f690249fd..101e4d12ec8ed 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -118,6 +118,14 @@ + + + + + + + + @@ -582,8 +590,6 @@ - - @@ -595,15 +601,6 @@ - - - - - - - - - diff --git a/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs index 29a2060e254e3..5c90ff6b8f179 100644 --- a/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs @@ -6,7 +6,7 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview { diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs deleted file mode 100644 index 694a5b6e505a2..0000000000000 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting -{ - internal interface IReferenceHighlightingAdditionalReferenceProvider : ILanguageService - { - Task> GetAdditionalReferencesAsync(Document document, ISymbol symbol, CancellationToken cancellationToken); - } -} diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs b/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs similarity index 87% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs index 76b72d5268f3b..125416fd75162 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Microsoft.VisualStudio.Text; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { internal partial class NavigateToHighlightReferenceCommandHandler { diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs b/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs similarity index 98% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs index a0997e4009c8a..07a6b67793a85 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs @@ -14,7 +14,7 @@ using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { [ExportCommandHandler(PredefinedCommandHandlerNames.NavigateToHighlightedReference, ContentTypeNames.RoslynContentType)] diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs similarity index 98% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs index cbad756b7bd6a..e2367ad5f5d3d 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs @@ -6,6 +6,7 @@ using System.ComponentModel.Composition; using System.Linq; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Tagging; @@ -21,7 +22,7 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { [Export(typeof(IViewTaggerProvider))] [ContentType(ContentTypeNames.RoslynContentType)] diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTag.cs b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTag.cs similarity index 88% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTag.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTag.cs index 2f5873a759429..0b974c620bfa2 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTag.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTag.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { internal class DefinitionHighlightTag : NavigableHighlightTag { diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTagDefinition.cs b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTagDefinition.cs similarity index 92% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTagDefinition.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTagDefinition.cs index b5a8023f8297e..5580291757cf8 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTagDefinition.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTagDefinition.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { [Export(typeof(EditorFormatDefinition))] [Name(DefinitionHighlightTag.TagId)] diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightTag.cs b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/ReferenceHighlightTag.cs similarity index 87% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightTag.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/Tags/ReferenceHighlightTag.cs index 6c7d2cddc337b..299c701a98780 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightTag.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/ReferenceHighlightTag.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { internal class ReferenceHighlightTag : NavigableHighlightTag { diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTag.cs b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTag.cs similarity index 88% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTag.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTag.cs index b4d35d64ae6b1..d6f8df3342be5 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTag.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTag.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { internal class WrittenReferenceHighlightTag : NavigableHighlightTag { diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTagDefinition.cs b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTagDefinition.cs similarity index 91% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTagDefinition.cs rename to src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTagDefinition.cs index 8db19cc5cb2b4..74323824a5056 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTagDefinition.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTagDefinition.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting { [Export(typeof(EditorFormatDefinition))] [Name(WrittenReferenceHighlightTag.TagId)] diff --git a/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb b/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb index ab5ab6a8ab80b..7c9591274611f 100644 --- a/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb +++ b/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb @@ -1,7 +1,8 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading -Imports Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +Imports Microsoft.CodeAnalysis.DocumentHighlighting +Imports Microsoft.CodeAnalysis.Editor.ReferenceHighlighting Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions Imports Microsoft.CodeAnalysis.Editor.Shared.Options Imports Microsoft.CodeAnalysis.Editor.Shared.Tagging @@ -16,9 +17,17 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting Public MustInherit Class AbstractReferenceHighlightingTests Protected Async Function VerifyHighlightsAsync(test As XElement, Optional optionIsEnabled As Boolean = True) As Tasks.Task + Await VerifyHighlightsAsync(test, optionIsEnabled, outOfProcess:=False) + Await VerifyHighlightsAsync(test, optionIsEnabled, outOfProcess:=True) + End Function + + Private Async Function VerifyHighlightsAsync(test As XElement, optionIsEnabled As Boolean, outOfProcess As Boolean) As Tasks.Task Using workspace = TestWorkspace.Create(test) WpfTestCase.RequireWpfFact($"{NameOf(AbstractReferenceHighlightingTests)}.VerifyHighlightsAsync creates asynchronous taggers") + workspace.Options = workspace.Options.WithChangedOption( + DocumentHighlightingOptions.OutOfProcessAllowed, outOfProcess) + Dim tagProducer = New ReferenceHighlightingViewTaggerProvider( workspace.GetService(Of IForegroundNotificationService), workspace.GetService(Of ISemanticChangeNotificationService), diff --git a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj index e80f4930b6093..6154c915e22c6 100644 --- a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj +++ b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj @@ -129,7 +129,6 @@ - @@ -155,7 +154,6 @@ - diff --git a/src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb b/src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb deleted file mode 100644 index 2c95b09d3a2fb..0000000000000 --- a/src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb +++ /dev/null @@ -1,18 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports System.Composition -Imports System.Threading -Imports System.Threading.Tasks -Imports Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting -Imports Microsoft.CodeAnalysis.Host.Mef - -Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.ReferenceHighlighting - - Friend Class ReferenceHighlightingAdditionalReferenceProvider - Implements IReferenceHighlightingAdditionalReferenceProvider - - Public Function GetAdditionalReferencesAsync(document As Document, symbol As ISymbol, cancellationToken As CancellationToken) As Task(Of IEnumerable(Of Location)) Implements IReferenceHighlightingAdditionalReferenceProvider.GetAdditionalReferencesAsync - Return SpecializedTasks.EmptyEnumerable(Of Location)() - End Function - End Class -End Namespace diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 653a38493d46f..6e565f66676d7 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -81,6 +81,7 @@ + diff --git a/src/EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs b/src/Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs similarity index 70% rename from src/EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs rename to src/Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs index 2321c81cc552e..4788978fd5719 100644 --- a/src/EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs +++ b/src/Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs @@ -1,32 +1,31 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.CSharp.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.CSharp.DocumentHighlighting { - [ExportLanguageService(typeof(IReferenceHighlightingAdditionalReferenceProvider), LanguageNames.CSharp), Shared] - internal class ReferenceHighlightingAdditionalReferenceProvider : IReferenceHighlightingAdditionalReferenceProvider + [ExportLanguageService(typeof(IDocumentHighlightsService), LanguageNames.CSharp), Shared] + internal class CSharpDocumentHighlightsService : AbstractDocumentHighlightsService { - public async Task> GetAdditionalReferencesAsync( + protected override async Task> GetAdditionalReferencesAsync( Document document, ISymbol symbol, CancellationToken cancellationToken) { // The FindRefs engine won't find references through 'var' for performance reasons. // Also, they are not needed for things like rename/sig change, and the normal find refs - // feature. However, we would lke the results to be highlighted to get a good experience + // feature. However, we would like the results to be highlighted to get a good experience // while editing (especially since highlighting may have been invoked off of 'var' in // the first place). // // So we look for the references through 'var' directly in this file and add them to the // results found by the engine. - List results = null; + var results = ArrayBuilder.GetInstance(); if (symbol is INamedTypeSymbol && symbol.Name != "var") { @@ -52,18 +51,13 @@ public async Task> GetAdditionalReferencesAsync( if (originalSymbol.Equals(boundSymbol)) { - if (results == null) - { - results = new List(); - } - results.Add(type.GetLocation()); } } } } - return results ?? SpecializedCollections.EmptyEnumerable(); + return results.ToImmutableAndFree(); } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/AbstractDocumentHighlightsService.cs b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs similarity index 82% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/AbstractDocumentHighlightsService.cs rename to src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs index 8a90b0062620f..91b1933bd7692 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/AbstractDocumentHighlightsService.cs +++ b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs @@ -14,12 +14,47 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.DocumentHighlighting { - internal abstract class AbstractDocumentHighlightsService : IDocumentHighlightsService + internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService { public async Task> GetDocumentHighlightsAsync( Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken) + { + var (succeeded, highlights) = await GetDocumentHighlightsInRemoteProcessAsync( + document, position, documentsToSearch, cancellationToken).ConfigureAwait(false); + + if (succeeded) + { + return highlights; + } + + return await GetDocumentHighlightsInCurrentProcessAsync( + document, position, documentsToSearch, cancellationToken).ConfigureAwait(false); + } + + private async Task<(bool succeeded, ImmutableArray highlights)> GetDocumentHighlightsInRemoteProcessAsync( + Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken) + { + using (var session = await TryGetRemoteSessionAsync( + document.Project.Solution, cancellationToken).ConfigureAwait(false)) + { + if (session == null) + { + return (succeeded: false, ImmutableArray.Empty); + } + + var result = await session.InvokeAsync( + nameof(IRemoteDocumentHighlights.GetDocumentHighlightsAsync), + document.Id, + position, + documentsToSearch.Select(d => d.Id).ToArray()).ConfigureAwait(false); + return (true, SerializableDocumentHighlights.Rehydrate(result, document.Project.Solution)); + } + } + + private async Task> GetDocumentHighlightsInCurrentProcessAsync( + Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken) { // use speculative semantic model to see whether we are on a symbol we can do HR var span = new TextSpan(position, 0); @@ -41,11 +76,11 @@ public async Task> GetDocumentHighlightsAsync // Get unique tags for referenced symbols return await GetTagsForReferencedSymbolAsync( - new SymbolAndProjectId(symbol, document.Project.Id), documentsToSearch, + new SymbolAndProjectId(symbol, document.Project.Id), documentsToSearch, solution, cancellationToken).ConfigureAwait(false); } - private async Task GetSymbolToSearchAsync(Document document, int position, SemanticModel semanticModel, ISymbol symbol, CancellationToken cancellationToken) + private static async Task GetSymbolToSearchAsync(Document document, int position, SemanticModel semanticModel, ISymbol symbol, CancellationToken cancellationToken) { // see whether we can use the symbol as it is var currentSemanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -82,7 +117,7 @@ await SymbolFinder.FindReferencesAsync( return ImmutableArray.Empty; } - private bool ShouldConsiderSymbol(ISymbol symbol) + private static bool ShouldConsiderSymbol(ISymbol symbol) { switch (symbol.Kind) { @@ -132,19 +167,13 @@ private async Task> FilterAndCreateSpansAsync documentsToSearch, cancellationToken).ConfigureAwait(false); } - private Task> GetAdditionalReferencesAsync( + protected virtual Task> GetAdditionalReferencesAsync( Document document, ISymbol symbol, CancellationToken cancellationToken) { - var additionalReferenceProvider = document.Project.LanguageServices.GetService(); - if (additionalReferenceProvider != null) - { - return additionalReferenceProvider.GetAdditionalReferencesAsync(document, symbol, cancellationToken); - } - - return Task.FromResult(SpecializedCollections.EmptyEnumerable()); + return SpecializedTasks.EmptyImmutableArray(); } - private async Task> CreateSpansAsync( + private static async Task> CreateSpansAsync( Solution solution, ISymbol symbol, IEnumerable references, @@ -252,7 +281,7 @@ private static bool ShouldIncludeDefinition(ISymbol symbol) return true; } - private async Task AddLocationSpan(Location location, Solution solution, HashSet spanSet, MultiDictionary tagList, HighlightSpanKind kind, CancellationToken cancellationToken) + private static async Task AddLocationSpan(Location location, Solution solution, HashSet spanSet, MultiDictionary tagList, HighlightSpanKind kind, CancellationToken cancellationToken) { var span = await GetLocationSpanAsync(solution, location, cancellationToken).ConfigureAwait(false); if (span != null && !spanSet.Contains(span.Value)) @@ -262,7 +291,7 @@ private async Task AddLocationSpan(Location location, Solution solution, HashSet } } - private async Task GetLocationSpanAsync( + private static async Task GetLocationSpanAsync( Solution solution, Location location, CancellationToken cancellationToken) { try diff --git a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs new file mode 100644 index 0000000000000..7efad4a9663df --- /dev/null +++ b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; + +namespace Microsoft.CodeAnalysis.DocumentHighlighting +{ + internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService + { + private static async Task TryGetRemoteSessionAsync( + Solution solution, CancellationToken cancellationToken) + { + var outOfProcessAllowed = solution.Workspace.Options.GetOption(DocumentHighlightingOptions.OutOfProcessAllowed); + if (!outOfProcessAllowed) + { + return null; + } + + var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + if (client == null) + { + return null; + } + + return await client.TryCreateCodeAnalysisServiceSessionAsync( + solution, cancellationToken).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs b/src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs new file mode 100644 index 0000000000000..0363999301f11 --- /dev/null +++ b/src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.DocumentHighlighting +{ + internal static class DocumentHighlightingOptions + { + private const string LocalRegistryPath = @"Roslyn\Features\DocumentHighlighting\"; + + public static readonly Option OutOfProcessAllowed = new Option( + nameof(DocumentHighlightingOptions), nameof(OutOfProcessAllowed), defaultValue: false, + storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OutOfProcessAllowed))); + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs b/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs similarity index 95% rename from src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs rename to src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs index f86be931963c1..0e08d4418c4f5 100644 --- a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs +++ b/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs @@ -1,13 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.Editor +namespace Microsoft.CodeAnalysis.DocumentHighlighting { internal enum HighlightSpanKind { diff --git a/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs b/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs new file mode 100644 index 0000000000000..b58c6f35a18fd --- /dev/null +++ b/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.DocumentHighlighting +{ + internal interface IRemoteDocumentHighlights + { + Task GetDocumentHighlightsAsync( + DocumentId documentId, int position, DocumentId[] documentIdsToSearch); + } + + internal struct SerializableDocumentHighlights + { + public DocumentId DocumentId; + public SerializableHighlightSpan[] HighlightSpans; + + public static ImmutableArray Rehydrate(SerializableDocumentHighlights[] array, Solution solution) + { + var result = ArrayBuilder.GetInstance(array.Length); + foreach (var dehydrated in array) + { + result.Push(dehydrated.Rehydrate(solution)); + } + + return result.ToImmutableAndFree(); + } + + private DocumentHighlights Rehydrate(Solution solution) + => new DocumentHighlights(solution.GetDocument(DocumentId), SerializableHighlightSpan.Rehydrate(HighlightSpans)); + + public static SerializableDocumentHighlights[] Dehydrate(ImmutableArray array) + { + var result = new SerializableDocumentHighlights[array.Length]; + var index = 0; + foreach (var highlights in array) + { + result[index] = Dehydrate(highlights); + index++; + } + + return result; + } + + private static SerializableDocumentHighlights Dehydrate(DocumentHighlights highlights) + => new SerializableDocumentHighlights + { + DocumentId = highlights.Document.Id, + HighlightSpans = SerializableHighlightSpan.Dehydrate(highlights.HighlightSpans) + }; + } + + internal struct SerializableHighlightSpan + { + public TextSpan TextSpan; + public HighlightSpanKind Kind; + + internal static SerializableHighlightSpan[] Dehydrate(ImmutableArray array) + { + var result = new SerializableHighlightSpan[array.Length]; + var index = 0; + foreach (var span in array) + { + result[index] = Dehydrate(span); + index++; + } + + return result; + } + + private static SerializableHighlightSpan Dehydrate(HighlightSpan span) + => new SerializableHighlightSpan + { + Kind = span.Kind, + TextSpan = span.TextSpan + }; + + internal static ImmutableArray Rehydrate(SerializableHighlightSpan[] array) + { + var result = ArrayBuilder.GetInstance(array.Length); + foreach (var dehydrated in array) + { + result.Push(dehydrated.Rehydrate()); + } + + return result.ToImmutableAndFree(); + } + + private HighlightSpan Rehydrate() + => new HighlightSpan(TextSpan, Kind); + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 4d756be89c071..f34cebb3a0a61 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -128,6 +128,7 @@ + @@ -153,6 +154,10 @@ + + + + diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj index 4f643a168c0ea..1e6d8416210af 100644 --- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj @@ -66,6 +66,7 @@ InternalUtilities\LambdaUtilities.vb + diff --git a/src/EditorFeatures/VisualBasic/HighlightReferences/VisualBasicDocumentHighlightsService.vb b/src/Features/VisualBasic/Portable/DocumentHighlighting/VisualBasicDocumentHighlightsService.vb similarity index 73% rename from src/EditorFeatures/VisualBasic/HighlightReferences/VisualBasicDocumentHighlightsService.vb rename to src/Features/VisualBasic/Portable/DocumentHighlighting/VisualBasicDocumentHighlightsService.vb index b0693439b5de7..2f925d3749ed3 100644 --- a/src/EditorFeatures/VisualBasic/HighlightReferences/VisualBasicDocumentHighlightsService.vb +++ b/src/Features/VisualBasic/Portable/DocumentHighlighting/VisualBasicDocumentHighlightsService.vb @@ -1,13 +1,13 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Composition -Imports Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +Imports Microsoft.CodeAnalysis.DocumentHighlighting Imports Microsoft.CodeAnalysis.Host.Mef -Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.HighlightReferences +Namespace Microsoft.CodeAnalysis.VisualBasic.DocumentHighlighting Friend Class VisualBasicDocumentHighlightsService Inherits AbstractDocumentHighlightsService End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index efc411f7e6c11..299f790bcbb34 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -4,11 +4,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs index 9973893c2464d..765612f4c86b2 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.VisualStudio.Shell.FindAllReferences; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs index 5a5f6a2b02bf8..2456a495cfef6 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs @@ -3,7 +3,7 @@ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.VisualStudio.Shell.FindAllReferences; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs index 1293153a2734e..c1069f8a25855 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs @@ -7,16 +7,16 @@ using System.Windows.Media; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Preview; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.PlatformUI; -using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs index e216ac66345ab..047419eff0750 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs index 4520ab6b597c7..04ba08bdc2e46 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs b/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs index d765685a409f3..98dd6cba799a0 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; namespace Microsoft.VisualStudio.IntegrationTest.Utilities { diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj index 26d66f271df3b..d6f317e7361a8 100644 --- a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -62,6 +62,7 @@ + diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs new file mode 100644 index 0000000000000..b98c8e58a9146 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.Remote +{ + // root level service for all Roslyn services + internal partial class CodeAnalysisService : IRemoteDocumentHighlights + { + public async Task GetDocumentHighlightsAsync( + DocumentId documentId, int position, DocumentId[] documentIdsToSearch) + { + var solution = await GetSolutionAsync().ConfigureAwait(false); + var document = solution.GetDocument(documentId); + var documentsToSearch = ImmutableHashSet.CreateRange(documentIdsToSearch.Select(solution.GetDocument)); + + var service = document.GetLanguageService(); + var result = await service.GetDocumentHighlightsAsync( + document, position, documentsToSearch, CancellationToken).ConfigureAwait(false); + + return SerializableDocumentHighlights.Dehydrate(result); + } + } +} \ No newline at end of file From f904ea01466986e7746571f3f878d97551b6e416 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Wed, 29 Mar 2017 10:23:14 -0700 Subject: [PATCH 020/284] Ensure non-fatal exceptions are actually thrown When a process crashes due to an unhandled exception or an explicit `Environment.FailFast()` or similar, the collected call stack and (possible) memory dump will be very close to the point of failure. This is obviously desirable when you need to diagnose an issue from crash reports. When we catch an exception and report it as a non-fatal event, however, that collection may happen well after the failure. There are a couple of things we need to do to ensure we get the proper data. First, we need to ensure that the exceptions we report have actually been thrown. The call stack within an exception is populated _when it is thrown_, not when it is created. As the analysis tools on the back end use the exception stack trace to bucketize failures, we currently have a large number of reports where we know something went wrong, but we don't know where. Second, our reporting mechanisms are best called from within an exception filter. This is because the filter runs at the point where the exception was thrown, before control flow has passed to a catch block. If we collect a memory dump at this point it will preserve the stack frames that led to the exception which is very useful for debugging. The resulting pattern is often a little odd: we throw an exception, report it through the exception filter, immediately catch it, and then do nothing in the catch block. However, this ensures we collect actionable data. --- .../Preview/DifferenceViewerPreview.cs | 8 ++++- .../SymbolSearchUpdateEngine.Update.cs | 33 ++++++++++++------- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 8 ++++- ...tServiceFactory.RemoteHostClientService.cs | 8 ++++- ...sEditorAdaptersFactoryServiceExtensions.cs | 11 +++++-- .../FindSymbols/DeclaredSymbolInfo.cs | 10 +++++- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 9 ++++- 7 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs b/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs index 488e64fd63437..21a4d20ef56a9 100644 --- a/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs +++ b/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs @@ -41,7 +41,13 @@ public void Dispose() return; } - FatalError.ReportWithoutCrash(new Exception($"Dispose is not called how? viewer state : {_viewer.IsClosed}")); + try + { + throw new Exception($"Dispose is not called how? viewer state : {_viewer.IsClosed}"); + } + catch (Exception e) when (FatalError.ReportWithoutCrash(e)) + { + } } } } \ No newline at end of file diff --git a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs index 50414749dba46..1033192212910 100644 --- a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs +++ b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs @@ -564,15 +564,14 @@ private async Task RepeatIOAsync(Func action) await action().ConfigureAwait(false); return; } - catch (Exception e) + catch (Exception e) when (IOUtilities.IsNormalIOException(e) || _service._reportAndSwallowException(e)) { - // Normal IO exception. Don't bother reporting it. We don't want to get - // lots of hits just because we couldn't write a file because of something - // like an anti-virus tool lockign the file. - if (!IOUtilities.IsNormalIOException(e)) - { - _service._reportAndSwallowException(e); - } + // The exception filter above might be a little funny looking. We always + // want to enter this catch block, but if we ran into a normal IO exception + // we shouldn't bother reporting it. We don't want to get lots of hits just + // because something like an anti-virus tool locked the file and we + // couldn't write to it. The call to IsNormalIOException will shortcut + // around the reporting in this case. var delay = _service._delayService.FileWriteDelay; await _service.LogExceptionAsync(e, $"Operation failed. Trying again after {delay}").ConfigureAwait(false); @@ -587,8 +586,13 @@ private async Task> TryParseDatabaseElementAsync(XEleme var contentsAttribute = element.Attribute(ContentAttributeName); if (contentsAttribute == null) { - _service._reportAndSwallowException( - new FormatException($"Database element invalid. Missing '{ContentAttributeName}' attribute")); + try + { + throw new FormatException($"Database element invalid. Missing '{ContentAttributeName}' attribute"); + } + catch (FormatException e) when (_service._reportAndSwallowException(e)) + { + } return ValueTuple.Create(false, (byte[])null); } @@ -607,8 +611,13 @@ private async Task> TryParseDatabaseElementAsync(XEleme if (!StringComparer.Ordinal.Equals(expectedChecksum, actualChecksum)) { - _service._reportAndSwallowException( - new FormatException($"Checksum mismatch: expected != actual. {expectedChecksum} != {actualChecksum}")); + try + { + throw new FormatException($"Checksum mismatch: expected != actual. {expectedChecksum} != {actualChecksum}"); + } + catch (FormatException e) when (_service._reportAndSwallowException(e)) + { + } return ValueTuple.Create(false, (byte[])null); } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index f183ebfde0bc6..2377e4d1dde03 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -232,7 +232,13 @@ private void VerifyDiagnostics(SemanticModel model) if (!AreEquivalent(rangeDiagnostics, wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases - FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); + try + { + throw new Exception("Bug in GetDiagnostics"); + } + catch (Exception e) when (FatalError.ReportWithoutCrash(e)) + { + } // make sure we hold onto these for debugging. GC.KeepAlive(rangeDeclaractionDiagnostics); diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index be22435e65026..1adee03e9c3ba 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -248,7 +248,13 @@ private void OnConnectionChanged(object sender, bool connected) // s_lastRemoteClientTask info should be saved in the dump // report NFW when connection is closed unless it is proper shutdown - FatalError.ReportWithoutCrash(new Exception("Connection to remote host closed")); + try + { + throw new Exception("Connection to remote host closed"); + } + catch (Exception e) when (FatalError.ReportWithoutCrash(e)) + { + } // use info bar to show warning to users var infoBarUIs = new List(); diff --git a/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs b/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs index 191931c59a046..4160826763987 100644 --- a/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs +++ b/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs @@ -30,8 +30,15 @@ public static IOleUndoManager TryGetUndoManager( var message = contextDocumentId == null ? $"{nameof(contextDocumentId)} was null." : $"{nameof(contextDocumentId)} was not null."; - FatalError.ReportWithoutCrash(new InvalidOperationException( - "Could not retrieve document. " + message)); + + try + { + throw new InvalidOperationException("Could not retrieve document. " + message); + } + catch(InvalidOperationException e) when (FatalError.ReportWithoutCrash(e)) + { + } + return null; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs index 0afb72e6fc75f..253e34c9bbaa4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs @@ -188,7 +188,15 @@ public ISymbol TryResolve(SemanticModel semanticModel, CancellationToken cancell $@"Invalid span in {nameof(DeclaredSymbolInfo)}. {nameof(this.Span)} = {this.Span} {nameof(root.FullSpan)} = {root.FullSpan}"; - FatalError.ReportWithoutCrash(new InvalidOperationException(message)); + + try + { + throw new InvalidOperationException(message); + } + catch (InvalidOperationException e) when (FatalError.ReportWithoutCrash(e)) + { + } + return null; } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 376ea7aa56007..b2ad099a71c6e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -88,7 +88,14 @@ private static async Task CreateInfoAsync(Document document, Ca $@"Invalid span in {nameof(declaredSymbolInfo)}. {nameof(declaredSymbolInfo.Span)} = {declaredSymbolInfo.Span} {nameof(root.FullSpan)} = {root.FullSpan}"; - FatalError.ReportWithoutCrash(new InvalidOperationException(message)); + + try + { + throw new InvalidOperationException(message); + } + catch (InvalidOperationException e) when (FatalError.ReportWithoutCrash(e)) + { + } } } } From ea6bb946691d558689eb32749a26bc9f63ee04ae Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Fri, 14 Apr 2017 13:27:33 -0700 Subject: [PATCH 021/284] Make sure non-fatal exceptions have been thrown We have the `FatalError.ReportWithoutCrash` method for reporting non-fatal exceptions. There have been cases where this method is passed a new, unthrown exception. These exceptions won't have a stack trace, which makes the resulting telemetry and Watson reports more or less useless. Here we check if the exception has a trace, and if it doesn't we simply throw and catch it before reporting it. This will generally be good enough, on the assumption that the underlying error occurred (and thus the exception created) near the call to `ReportWithoutCrash`. --- .../Portable/InternalUtilities/FatalError.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs index 21ed2ec0e0cb3..966321dde88d9 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs @@ -138,6 +138,22 @@ public static bool Report(Exception exception) [DebuggerHidden] public static bool ReportWithoutCrash(Exception exception) { + // There have been cases where a new, unthrown exception has been passed to this method. + // In these cases the exception won't have a stack trace, which isn't very helpful. We + // throw and catch the exception here as that will result in a stack trace that is + // better than nothing. + if (exception.StackTrace == null) + { + try + { + throw exception; + } + catch + { + // Empty; we just need the exception to have a stack trace. + } + } + Report(exception, s_nonFatalHandler); return true; } From db8b8e9ad3a73e229f5fcb75df2559cc898663e2 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Mon, 24 Apr 2017 14:31:18 -0700 Subject: [PATCH 022/284] Remove unnecessary try/catches Now that `FatalError.ReportWithoutCrash` throws and catches unthrown exceptions itself there is no need to do this at every call site. --- .../Portable/InternalUtilities/FatalError.cs | 7 ++++++- .../Preview/DifferenceViewerPreview.cs | 8 +------- .../SymbolSearchUpdateEngine.Update.cs | 16 ++-------------- ...cIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 8 +------- ...ientServiceFactory.RemoteHostClientService.cs | 8 +------- .../IVsEditorAdaptersFactoryServiceExtensions.cs | 8 +------- .../Portable/FindSymbols/DeclaredSymbolInfo.cs | 8 +------- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 8 +------- 8 files changed, 14 insertions(+), 57 deletions(-) diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs index 966321dde88d9..a2c8d6028893c 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs @@ -131,8 +131,13 @@ public static bool Report(Exception exception) } /// - /// Use in an exception filter to report a non fatal error. + /// Report a non-fatal error. /// Calls and doesn't pass the exception through (the method returns true). + /// This is generally expected to be used within an exception filter as that allows us to + /// capture data at the point the exception is thrown rather than when it is handled. + /// However, it can also be used outside of an exception filter. If the exception has not + /// already been thrown the method will throw and catch it itself to ensure we get a useful + /// stack trace. /// /// True to catch the exception. [DebuggerHidden] diff --git a/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs b/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs index 21a4d20ef56a9..488e64fd63437 100644 --- a/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs +++ b/src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs @@ -41,13 +41,7 @@ public void Dispose() return; } - try - { - throw new Exception($"Dispose is not called how? viewer state : {_viewer.IsClosed}"); - } - catch (Exception e) when (FatalError.ReportWithoutCrash(e)) - { - } + FatalError.ReportWithoutCrash(new Exception($"Dispose is not called how? viewer state : {_viewer.IsClosed}")); } } } \ No newline at end of file diff --git a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs index 1033192212910..05bba13ca17c3 100644 --- a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs +++ b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs @@ -586,13 +586,7 @@ private async Task> TryParseDatabaseElementAsync(XEleme var contentsAttribute = element.Attribute(ContentAttributeName); if (contentsAttribute == null) { - try - { - throw new FormatException($"Database element invalid. Missing '{ContentAttributeName}' attribute"); - } - catch (FormatException e) when (_service._reportAndSwallowException(e)) - { - } + _service._reportAndSwallowException(new FormatException($"Database element invalid. Missing '{ContentAttributeName}' attribute")); return ValueTuple.Create(false, (byte[])null); } @@ -611,13 +605,7 @@ private async Task> TryParseDatabaseElementAsync(XEleme if (!StringComparer.Ordinal.Equals(expectedChecksum, actualChecksum)) { - try - { - throw new FormatException($"Checksum mismatch: expected != actual. {expectedChecksum} != {actualChecksum}"); - } - catch (FormatException e) when (_service._reportAndSwallowException(e)) - { - } + _service._reportAndSwallowException(new FormatException($"Checksum mismatch: expected != actual. {expectedChecksum} != {actualChecksum}")); return ValueTuple.Create(false, (byte[])null); } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 2377e4d1dde03..f183ebfde0bc6 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -232,13 +232,7 @@ private void VerifyDiagnostics(SemanticModel model) if (!AreEquivalent(rangeDiagnostics, wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases - try - { - throw new Exception("Bug in GetDiagnostics"); - } - catch (Exception e) when (FatalError.ReportWithoutCrash(e)) - { - } + FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); // make sure we hold onto these for debugging. GC.KeepAlive(rangeDeclaractionDiagnostics); diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index 1adee03e9c3ba..be22435e65026 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -248,13 +248,7 @@ private void OnConnectionChanged(object sender, bool connected) // s_lastRemoteClientTask info should be saved in the dump // report NFW when connection is closed unless it is proper shutdown - try - { - throw new Exception("Connection to remote host closed"); - } - catch (Exception e) when (FatalError.ReportWithoutCrash(e)) - { - } + FatalError.ReportWithoutCrash(new Exception("Connection to remote host closed")); // use info bar to show warning to users var infoBarUIs = new List(); diff --git a/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs b/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs index 4160826763987..d849297702205 100644 --- a/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs +++ b/src/VisualStudio/Core/Def/Utilities/IVsEditorAdaptersFactoryServiceExtensions.cs @@ -31,13 +31,7 @@ public static IOleUndoManager TryGetUndoManager( ? $"{nameof(contextDocumentId)} was null." : $"{nameof(contextDocumentId)} was not null."; - try - { - throw new InvalidOperationException("Could not retrieve document. " + message); - } - catch(InvalidOperationException e) when (FatalError.ReportWithoutCrash(e)) - { - } + FatalError.ReportWithoutCrash(new InvalidOperationException("Could not retrieve document. " + message)); return null; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs index 253e34c9bbaa4..ce5f3adb3a451 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/DeclaredSymbolInfo.cs @@ -189,13 +189,7 @@ public ISymbol TryResolve(SemanticModel semanticModel, CancellationToken cancell {nameof(this.Span)} = {this.Span} {nameof(root.FullSpan)} = {root.FullSpan}"; - try - { - throw new InvalidOperationException(message); - } - catch (InvalidOperationException e) when (FatalError.ReportWithoutCrash(e)) - { - } + FatalError.ReportWithoutCrash(new InvalidOperationException(message)); return null; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index b2ad099a71c6e..ce8aa6bbaba60 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -89,13 +89,7 @@ private static async Task CreateInfoAsync(Document document, Ca {nameof(declaredSymbolInfo.Span)} = {declaredSymbolInfo.Span} {nameof(root.FullSpan)} = {root.FullSpan}"; - try - { - throw new InvalidOperationException(message); - } - catch (InvalidOperationException e) when (FatalError.ReportWithoutCrash(e)) - { - } + FatalError.ReportWithoutCrash(new InvalidOperationException(message)); } } } From 05391555f1fa8a74b1c78d7f92bacb6b401067d2 Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Fri, 28 Apr 2017 09:08:53 -0700 Subject: [PATCH 023/284] Async main codegen (#18472) * attempt to get main entrypoints working * async main codegen * remove garbage file * fix vb tests * add some comments * moduleBeingBuilt can sometimes be null apparently * fix style nits * more whitespace fixes * address feedback * automated style fixer * move most work out into the constructor * protect against null in error condition * add comment * address cston review * autoformat * use cast * check module being built * add another object cast * address review * check for old cases where async methods called Main also exist * more suggestions * autoformat * mostly cleanup * use shorter Count and Single * remove superfluous diagnostics add * check exit code / revert to old entrypoint finding code * remove language version check in entrypoint compiler * fix debug.assert * switch to assert equal * codereview * add more tests; change conditions for test execution * fix up emit methods * narrow down error location * ammend GetAwaitableExpressionInfo to return a bound call to GetAwaiter().GetResult() * more comments * find entrypoint methods uses the async binder * autoformat * use diagnostics from async binder * relocate tests * fix suggestions for review 8346 * add back no-async tests with better location * only allow named types * remove empty file; remove null checks from helper * forgot to add csproj file * emit into warning diagnostics instead of regular diagnostics * autoformat * remove unused file from merge * use diagnostic locations * move some methods around, split some tests up * fix non-returning async tests, remove bool return from check availability * add params test * style and expected output fix * switch back to null because default emits warnings * add partial method tests * fix locations for unix * better partial syntax tests * use expected length of 0 * use default length of 0 for expected output * check implementation symbol --- .../CSharp/Portable/Binder/Binder_Await.cs | 35 +- .../Portable/Compilation/CSharpCompilation.cs | 100 +- .../Portable/Compiler/MethodCompiler.cs | 24 +- .../CSharp/Portable/Symbols/MethodSymbol.cs | 86 +- .../Symbols/MethodSymbolExtensions.cs | 15 + .../SynthesizedEntryPointSymbol.cs | 122 ++- .../Test/Emit/CSharpCompilerEmitTest.csproj | 3 +- .../Emit/CodeGen/CodeGenAsyncMainTests.cs | 912 ++++++++++++++++++ .../Semantic/Semantics/BindingAsyncTests.cs | 375 ------- .../Test/Symbol/Symbols/Source/MethodTests.cs | 105 ++ .../Test/Utilities/CSharp/CSharpTestBase.cs | 8 + .../Utilities/VisualBasic/BasicTestBase.vb | 31 + .../CoreClr/CoreCLRRuntimeEnvironment.cs | 15 +- .../CoreClr/TestExecutionLoadContext.cs | 6 +- .../CodeRuntime/DesktopRuntimeEnvironment.cs | 28 +- .../CodeRuntime/RuntimeAssemblyManager.cs | 6 +- .../CommonTestBase.CompilationVerifier.cs | 11 +- src/Test/Utilities/Portable/CommonTestBase.cs | 12 +- .../Compilation/IRuntimeEnvironment.cs | 2 +- 19 files changed, 1366 insertions(+), 530 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 371874bcdeae1..61611318c2954 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -29,7 +29,7 @@ private BoundExpression BindAwait(BoundExpression expression, SyntaxNode node, D bool hasErrors = ReportBadAwaitWithoutAsync(node, diagnostics) | ReportBadAwaitContext(node, diagnostics) | - !GetAwaitableExpressionInfo(expression, out getAwaiter, out isCompleted, out getResult, node, diagnostics); + !GetAwaitableExpressionInfo(expression, out getAwaiter, out isCompleted, out getResult, out _, node, diagnostics); // Spec 7.7.7.2: // The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). Thus, @@ -209,17 +209,19 @@ private bool ReportBadAwaitContext(SyntaxNode node, DiagnosticBag diagnostics) /// Finds and validates the required members of an awaitable expression, as described in spec 7.7.7.1. /// /// True if the expression is awaitable; false otherwise. - private bool GetAwaitableExpressionInfo( + internal bool GetAwaitableExpressionInfo( BoundExpression expression, out MethodSymbol getAwaiter, out PropertySymbol isCompleted, out MethodSymbol getResult, + out BoundExpression getAwaiterGetResultCall, SyntaxNode node, DiagnosticBag diagnostics) { getAwaiter = null; isCompleted = null; getResult = null; + getAwaiterGetResultCall = null; if (!ValidateAwaitedExpression(expression, node, diagnostics)) { @@ -231,7 +233,8 @@ private bool GetAwaitableExpressionInfo( return true; } - if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter)) + BoundExpression getAwaiterCall = null; + if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter, out getAwaiterCall)) { return false; } @@ -239,7 +242,7 @@ private bool GetAwaitableExpressionInfo( TypeSymbol awaiterType = getAwaiter.ReturnType; return GetIsCompletedProperty(awaiterType, node, expression.Type, diagnostics, out isCompleted) && AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics) - && GetGetResultMethod(awaiterType, node, expression.Type, diagnostics, out getResult); + && GetGetResultMethod(getAwaiterCall, node, expression.Type, diagnostics, out getResult, out getAwaiterGetResultCall); } /// @@ -273,19 +276,21 @@ private static bool ValidateAwaitedExpression(BoundExpression expression, Syntax /// NOTE: this is an error in the spec. An extension method of the form /// Awaiter<T> GetAwaiter<T>(this Task<T>) may be used. /// - private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, DiagnosticBag diagnostics, out MethodSymbol getAwaiterMethod) + private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, DiagnosticBag diagnostics, out MethodSymbol getAwaiterMethod, out BoundExpression getAwaiterCall) { if (expression.Type.SpecialType == SpecialType.System_Void) { Error(diagnostics, ErrorCode.ERR_BadAwaitArgVoidCall, node); getAwaiterMethod = null; + getAwaiterCall = null; return false; } - var getAwaiterCall = MakeInvocationExpression(node, expression, WellKnownMemberNames.GetAwaiter, ImmutableArray.Empty, diagnostics); + getAwaiterCall = MakeInvocationExpression(node, expression, WellKnownMemberNames.GetAwaiter, ImmutableArray.Empty, diagnostics); if (getAwaiterCall.HasAnyErrors) // && !expression.HasAnyErrors? { getAwaiterMethod = null; + getAwaiterCall = null; return false; } @@ -293,6 +298,7 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Di { Error(diagnostics, ErrorCode.ERR_BadAwaitArg, node, expression.Type); getAwaiterMethod = null; + getAwaiterCall = null; return false; } @@ -303,6 +309,7 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Di { Error(diagnostics, ErrorCode.ERR_BadAwaitArg, node, expression.Type); getAwaiterMethod = null; + getAwaiterCall = null; return false; } @@ -383,28 +390,31 @@ private bool AwaiterImplementsINotifyCompletion(TypeSymbol awaiterType, SyntaxNo /// Spec 7.7.7.1: /// An Awaiter A has an accessible instance method GetResult with no parameters and no type parameters. /// - private bool GetGetResultMethod(TypeSymbol awaiterType, SyntaxNode node, TypeSymbol awaitedExpressionType, DiagnosticBag diagnostics, out MethodSymbol getResultMethod) + private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode node, TypeSymbol awaitedExpressionType, DiagnosticBag diagnostics, out MethodSymbol getResultMethod, out BoundExpression getAwaiterGetResultCall) { - var receiver = new BoundLiteral(node, ConstantValue.Null, awaiterType); - var getResultCall = MakeInvocationExpression(node, receiver, WellKnownMemberNames.GetResult, ImmutableArray.Empty, diagnostics); - if (getResultCall.HasAnyErrors) + var awaiterType = awaiterExpression.Type; + getAwaiterGetResultCall = MakeInvocationExpression(node, awaiterExpression, WellKnownMemberNames.GetResult, ImmutableArray.Empty, diagnostics); + if (getAwaiterGetResultCall.HasAnyErrors) { getResultMethod = null; + getAwaiterGetResultCall = null; return false; } - if (getResultCall.Kind != BoundKind.Call) + if (getAwaiterGetResultCall.Kind != BoundKind.Call) { Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.GetResult); getResultMethod = null; + getAwaiterGetResultCall = null; return false; } - getResultMethod = ((BoundCall)getResultCall).Method; + getResultMethod = ((BoundCall)getAwaiterGetResultCall).Method; if (getResultMethod.IsExtensionMethod) { Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.GetResult); getResultMethod = null; + getAwaiterGetResultCall = null; return false; } @@ -412,6 +422,7 @@ private bool GetGetResultMethod(TypeSymbol awaiterType, SyntaxNode node, TypeSym { Error(diagnostics, ErrorCode.ERR_BadAwaiterPattern, node, awaiterType, awaitedExpressionType); getResultMethod = null; + getAwaiterGetResultCall = null; return false; } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index dc84d30bf09d2..33be4e8231c4d 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1457,7 +1457,7 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm var viableEntryPoints = ArrayBuilder.GetInstance(); foreach (var candidate in entryPointCandidates) { - if (!candidate.HasEntryPointSignature(this)) + if (!HasEntryPointSignature(candidate, warnings)) { // a single error for partial methods warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); @@ -1471,10 +1471,13 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm continue; } + // PROTOTYPE(async-main): CheckFeatureAvailability should be called on non-async methods + // that return Task or Task if (candidate.IsAsync) { // PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. - CheckFeatureAvailability(candidate.DeclaringSyntaxReferences.Single().GetSyntax(), MessageID.IDS_FeatureAsyncMain, diagnostics); + // PROTOTYPE(async-main): Switch diagnostics around if the type is not Task or Task + CheckFeatureAvailability(candidate.GetNonNullSyntaxNode(), MessageID.IDS_FeatureAsyncMain, diagnostics); } viableEntryPoints.Add(candidate); @@ -1525,6 +1528,99 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm } } + internal bool ReturnsAwaitableToVoidOrInt(MethodSymbol method, DiagnosticBag diagnostics) + { + // Common case optimization + if (method.ReturnType.SpecialType == SpecialType.System_Void || method.ReturnType.SpecialType == SpecialType.System_Int32) + { + return false; + } + + if (!(method.ReturnType is NamedTypeSymbol namedType)) + { + return false; + } + + // Early bail so we only even check things that are System.Threading.Tasks.Task() + if (!(namedType.ConstructedFrom == GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) || + namedType.ConstructedFrom == GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T))) + { + return false; + } + + var syntax = method.ExtractReturnTypeSyntax(); + var dumbInstance = new BoundLiteral(syntax, ConstantValue.Null, method.ReturnType); + // PROTOTYPE(async-main): We might need to adjust the containing member of the binder to be the Main method + var binder = GetBinder(syntax); + BoundExpression result; + var success = binder.GetAwaitableExpressionInfo(dumbInstance, out _, out _, out _, out result, syntax, diagnostics); + + return success && + (result.Type.SpecialType == SpecialType.System_Void || result.Type.SpecialType == SpecialType.System_Int32); + } + + /// + /// Checks if the method has an entry point compatible signature, i.e. + /// - the return type is either void, int, , + /// or where T is an int. + /// - has either no parameter or a single parameter of type string[] + /// + private bool HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) + { + if (method.IsVararg) + { + return false; + } + + TypeSymbol returnType = method.ReturnType; + bool returnsTaskOrTaskOfInt = false; + if (returnType.SpecialType != SpecialType.System_Int32 && returnType.SpecialType != SpecialType.System_Void) + { + // Never look for ReturnsAwaitableToVoidOrInt on int32 or void + returnsTaskOrTaskOfInt = ReturnsAwaitableToVoidOrInt(method, bag); + if (!returnsTaskOrTaskOfInt) + { + return false; + } + } + + // Prior to 7.1, async methods were considered to "have the entrypoint signature". + // In order to keep back-compat, we need to let these through if compiling using a previous language version. + if (!returnsTaskOrTaskOfInt && method.IsAsync && this.LanguageVersion >= LanguageVersion.CSharp7_1) + { + return false; + } + + if (method.RefKind != RefKind.None) + { + return false; + } + + if (method.Parameters.Length == 0) + { + return true; + } + + if (method.Parameters.Length > 1) + { + return false; + } + + if (!method.ParameterRefKinds.IsDefault) + { + return false; + } + + var firstType = method.Parameters[0].Type; + if (firstType.TypeKind != TypeKind.Array) + { + return false; + } + + var array = (ArrayTypeSymbol)firstType; + return array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String; + } + internal override bool IsUnreferencedAssemblyIdentityDiagnosticCode(int code) => code == (int)ErrorCode.ERR_NoTypeDef; diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 8daff0363d5a4..ef0557b8635bb 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -184,6 +184,8 @@ public static void CompileMethodBodies( } } + // Returns the MethodSymbol for the assembly entrypoint. If the user has a Task returning main, + // this function returns the synthesized Main MethodSymbol. private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, DiagnosticBag diagnostics, CancellationToken cancellationToken) { var entryPointAndDiagnostics = compilation.GetEntryPointAndDiagnostics(cancellationToken); @@ -194,9 +196,26 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul Debug.Assert(!entryPointAndDiagnostics.Diagnostics.IsDefault); diagnostics.AddRange(entryPointAndDiagnostics.Diagnostics); - var entryPoint = entryPointAndDiagnostics.MethodSymbol; - var synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol; + + if ((object)entryPoint == null) + { + Debug.Assert(entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty); + return null; + } + + // entryPoint can be a SynthesizedEntryPointSymbol if a script is being compiled. + SynthesizedEntryPointSymbol synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol; + if ((object)synthesizedEntryPoint == null && compilation.ReturnsAwaitableToVoidOrInt(entryPoint, diagnostics)) + { + synthesizedEntryPoint = new SynthesizedEntryPointSymbol.AsyncForwardEntryPoint(compilation, diagnostics, entryPoint.ContainingType, entryPoint); + entryPoint = synthesizedEntryPoint; + if ((object)moduleBeingBuilt != null) + { + moduleBeingBuilt.AddSynthesizedDefinition(entryPoint.ContainingType, synthesizedEntryPoint); + } + } + if (((object)synthesizedEntryPoint != null) && (moduleBeingBuilt != null) && !hasDeclarationErrors && @@ -221,7 +240,6 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody); } - Debug.Assert((object)entryPoint != null || entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty); return entryPoint; } diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index ef63dda0f2e10..dae263a5acd7f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; @@ -595,91 +596,6 @@ internal bool IsEntryPointCandidate get { return IsStatic && Name == WellKnownMemberNames.EntryPointMethodName; } } - /// - /// Checks if the method has an entry point compatible signature, i.e. - /// - the return type is either void, int, , - /// or where T is an int. - /// - has either no parameter or a single parameter of type string[] - /// - internal bool HasEntryPointSignature(CSharpCompilation compilation) - { - - bool IsAsyncMainReturnType(TypeSymbol type) - { - var namedType = type as NamedTypeSymbol; - if (namedType == null) - { - return false; - } - else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)) - { - // Change this to `namedType.IsNonGenericTaskType` if you want to support "task-like" objects. - return true; - } - else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) - { - // Change this to `namedType.IsGenericTaskType` if you want to support "task-like" objects. - return namedType.TypeArguments[0].SpecialType == SpecialType.System_Int32; - } - else - { - return false; - } - } - - if (IsVararg) - { - return false; - } - - TypeSymbol returnType = ReturnType; - bool isAsyncMainReturnType = IsAsyncMainReturnType(returnType); - if (returnType.SpecialType != SpecialType.System_Int32 && returnType.SpecialType != SpecialType.System_Void && !isAsyncMainReturnType) - { - return false; - } - - // Prior to 7.1, async methods were considered to "have the entrypoint signature". - // In order to keep back-compat, we need to let these through if compiling using a previous language version. - if (!isAsyncMainReturnType && IsAsync && compilation.LanguageVersion >= LanguageVersion.CSharp7_1) - { - return false; - } - - if (RefKind != RefKind.None) - { - return false; - } - - if (RefKind != RefKind.None) - { - return false; - } - - if (Parameters.Length == 0) - { - return true; - } - - if (Parameters.Length > 1) - { - return false; - } - - if (!ParameterRefKinds.IsDefault) - { - return false; - } - - var firstType = Parameters[0].Type; - if (firstType.TypeKind != TypeKind.Array) - { - return false; - } - - var array = (ArrayTypeSymbol)firstType; - return array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String; - } internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument argument) { diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index a651f6f1b3a9e..8924222d9878d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -300,5 +301,19 @@ public static bool IsGenericTaskReturningAsync(this MethodSymbol method, CSharpC return method.IsAsync && method.ReturnType.IsGenericTaskType(compilation); } + + internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol method) + { + method = method.PartialDefinitionPart ?? method; + foreach (var reference in method.DeclaringSyntaxReferences) + { + if (reference.GetSyntax() is MethodDeclarationSyntax methodDeclaration) + { + return methodDeclaration.ReturnType; + } + } + + return (CSharpSyntaxNode)CSharpSyntaxTree.Dummy.GetRoot(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 1d45cc848a95f..58187a5827b59 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -17,7 +19,8 @@ internal abstract class SynthesizedEntryPointSymbol : MethodSymbol internal const string FactoryName = ""; private readonly NamedTypeSymbol _containingType; - private readonly TypeSymbol _returnType; + // PROTOTYPE(async-main): remove this and move it out into inheriting classes? + private TypeSymbol _returnType; internal static SynthesizedEntryPointSymbol Create(SynthesizedInteractiveInitializerMethod initializerMethod, DiagnosticBag diagnostics) { @@ -54,10 +57,9 @@ internal static SynthesizedEntryPointSymbol Create(SynthesizedInteractiveInitial } } - private SynthesizedEntryPointSymbol(NamedTypeSymbol containingType, TypeSymbol returnType) + private SynthesizedEntryPointSymbol(NamedTypeSymbol containingType, TypeSymbol returnType = null) { Debug.Assert((object)containingType != null); - Debug.Assert((object)returnType != null); _containingType = containingType; _returnType = returnType; @@ -286,7 +288,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l throw ExceptionUtilities.Unreachable; } - private CSharpSyntaxNode GetSyntax() + private static CSharpSyntaxNode DummySyntax() { var syntaxTree = CSharpSyntaxTree.Dummy; return (CSharpSyntaxNode)syntaxTree.GetRoot(); @@ -329,6 +331,104 @@ private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundE { WasCompilerGenerated = true }; } + /// A synthesized entrypoint that forwards all calls to an async Main Method + internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol + { + /// The user-defined asynchronous main method. + private readonly CSharpSyntaxNode _userMainReturnTypeSyntax; + + private readonly BoundExpression _getAwaiterGetResultCall; + + private readonly ImmutableArray _parameters; + + internal AsyncForwardEntryPoint(CSharpCompilation compilation, DiagnosticBag diagnosticBag, NamedTypeSymbol containingType, MethodSymbol userMain) : + base(containingType) + { + // There should be no way for a userMain to be passed in unless it already passed the + // parameter checks for determining entrypoint validity. + Debug.Assert(userMain.ParameterCount == 0 || userMain.ParameterCount == 1); + + _userMainReturnTypeSyntax = userMain.ExtractReturnTypeSyntax(); + // PROTOTYPE(async-main): we might need to adjust the containing member of the binder to be the Main method. + var binder = compilation.GetBinder(_userMainReturnTypeSyntax); + _parameters = SynthesizedParameterSymbol.DeriveParameters(userMain, this); + + var arguments = Parameters.SelectAsArray((p, s) => (BoundExpression)new BoundParameter(s, p, p.Type), _userMainReturnTypeSyntax); + + // Main(args) or Main() + BoundCall userMainInvocation = new BoundCall( + syntax: _userMainReturnTypeSyntax, + receiverOpt: null, + method: userMain, + arguments: arguments, + argumentNamesOpt: default(ImmutableArray), + argumentRefKindsOpt: default(ImmutableArray), + isDelegateCall: false, + expanded: false, + invokedAsExtensionMethod: false, + argsToParamsOpt: default(ImmutableArray), + resultKind: LookupResultKind.Viable, + type: userMain.ReturnType) + { WasCompilerGenerated = true }; + + // PROTOTYPE(async-main): lower the tree. + var success = binder.GetAwaitableExpressionInfo(userMainInvocation, out _, out _, out _, out _getAwaiterGetResultCall, _userMainReturnTypeSyntax, diagnosticBag); + _returnType = _getAwaiterGetResultCall.Type; + + Debug.Assert( + ReturnType.SpecialType == SpecialType.System_Void || + ReturnType.SpecialType == SpecialType.System_Int32); + } + + public override string Name => MainName; + + public override ImmutableArray Parameters => _parameters; + + internal override BoundBlock CreateBody() + { + var syntax = _userMainReturnTypeSyntax; + + + if (ReturnsVoid) + { + return new BoundBlock( + syntax: syntax, + locals: ImmutableArray.Empty, + statements: ImmutableArray.Create( + new BoundExpressionStatement( + syntax: syntax, + expression: _getAwaiterGetResultCall + ) + { WasCompilerGenerated = true }, + new BoundReturnStatement( + syntax: syntax, + refKind: RefKind.None, + expressionOpt: null + ) + { WasCompilerGenerated = true } + ) + ) + { WasCompilerGenerated = true }; + + } + else + { + return new BoundBlock( + syntax: syntax, + locals: ImmutableArray.Empty, + statements: ImmutableArray.Create( + new BoundReturnStatement( + syntax: syntax, + refKind: RefKind.None, + expressionOpt: _getAwaiterGetResultCall + ) + ) + ) + { WasCompilerGenerated = true }; + } + } + } + private sealed class ScriptEntryPoint : SynthesizedEntryPointSymbol { private readonly MethodSymbol _getAwaiterMethod; @@ -344,15 +444,9 @@ internal ScriptEntryPoint(NamedTypeSymbol containingType, TypeSymbol returnType, _getResultMethod = getResultMethod; } - public override string Name - { - get { return MainName; } - } + public override string Name => MainName; - public override ImmutableArray Parameters - { - get { return ImmutableArray.Empty; } - } + public override ImmutableArray Parameters => ImmutableArray.Empty; // private static void
() // { @@ -365,7 +459,7 @@ internal override BoundBlock CreateBody() Debug.Assert((object)_getAwaiterMethod != null); Debug.Assert((object)_getResultMethod != null); - var syntax = this.GetSyntax(); + var syntax = DummySyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 0); @@ -449,7 +543,7 @@ public override ImmutableArray Parameters // } internal override BoundBlock CreateBody() { - var syntax = this.GetSyntax(); + var syntax = DummySyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index 55b77172ef679..5d82edc5b9f06 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -67,6 +67,7 @@ + @@ -190,4 +191,4 @@ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs new file mode 100644 index 0000000000000..3dd02d4548495 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs @@ -0,0 +1,912 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Xunit; +using System.Threading; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen +{ + public class CodeGenAsyncMainTests : EmitMetadataTestBase + { + + [Fact] + public void MultipleMainsOneOfWhichHasBadTaskType_WithMainType() + { + var source = @" +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } + static void Main(string[] args) { } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe.WithMainTypeName("Program"), parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12)); + } + + [Fact] + public void MultipleMainsOneOfWhichHasBadTaskType() + { + var source = @" +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } + static void Main(string[] args) { } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12), + // (11,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // static Task Main() { + Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(11, 12), + // (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(11, 22)); + } + + [Fact] + public void GetResultReturnsSomethingElse() + { + var source = @" +using System.Threading.Tasks; +using System; + +namespace System.Runtime.CompilerServices { + public interface INotifyCompletion { + void OnCompleted(Action action); + } +} + +namespace System.Threading.Tasks { + public class Awaiter: System.Runtime.CompilerServices.INotifyCompletion { + public double GetResult() { return 0.0; } + public bool IsCompleted => true; + public void OnCompleted(Action action) {} + } + public class Task { + public Awaiter GetAwaiter() { + return new Awaiter(); + } + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (25,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(25, 12), + // (25,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(25, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1) +); + } + + [Fact] + public void TaskOfTGetAwaiterReturnsVoid() + { + var source = @" +using System; +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyDiagnostics( + // (12,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // static Task Main() { + Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(12, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), + // (2,1): hidden CS8019: Unnecessary using directive. + // using System; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(2, 1)); + } + [Fact] + public void TaskGetAwaiterReturnsVoid() + { + var source = @" +using System; +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (12,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // static Task Main() { + Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,17): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(12, 17), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void MissingMethodsOnTask() + { + var source = @" +using System; +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task {} +} + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (10,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(10, 12), + // (10,12): error CS1061: 'Task' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly reference?) + // static Task Main() { + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Task").WithArguments("System.Threading.Tasks.Task", "GetAwaiter").WithLocation(10, 12), + // (10,17): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(10, 17), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void EmitTaskOfIntReturningMainWithoutInt() + { + var corAssembly = @" +namespace System { + public class Object {} +}"; + var corCompilation = CreateCompilation(corAssembly, options: TestOptions.DebugDll); + corCompilation.VerifyDiagnostics(); + + var taskAssembly = @" +namespace System.Threading.Tasks { + public class Task{} +}"; + var taskCompilation = CreateCompilationWithMscorlib45(taskAssembly, options: TestOptions.DebugDll); + taskCompilation.VerifyDiagnostics(); + + var source = @" +using System; +using System.Threading.Tasks; + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateCompilation(source, new[] { corCompilation.ToMetadataReference(), taskCompilation.ToMetadataReference() }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // (6,17): error CS0518: Predefined type 'System.Int32' is not defined or imported + // static Task Main() { + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(6, 17), + // (6,12): error CS1061: 'Task' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly reference?) + // static Task Main() { + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Task").WithArguments("System.Threading.Tasks.Task", "GetAwaiter").WithLocation(6, 12), + // (6,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(6, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void EmitTaskReturningMainWithoutVoid() + { + var corAssembly = @" +namespace System { + public class Object {} +}"; + var corCompilation = CreateCompilation(corAssembly, options: TestOptions.DebugDll); + corCompilation.VerifyDiagnostics(); + + var taskAssembly = @" +namespace System.Threading.Tasks { + public class Task{} +}"; + var taskCompilation = CreateCompilationWithMscorlib45(taskAssembly, options: TestOptions.DebugDll); + taskCompilation.VerifyDiagnostics(); + + var source = @" +using System; +using System.Threading.Tasks; + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateCompilation(source, new[] { corCompilation.ToMetadataReference(), taskCompilation.ToMetadataReference() }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // (6,12): error CS1061: 'Task' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly reference?) + // static Task Main() { + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Task").WithArguments("System.Threading.Tasks.Task", "GetAwaiter").WithLocation(6, 12), + // (6,17): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(6, 17), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void AsyncEmitMainOfIntTest() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main() { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(""async main""); + return 10; + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 10); + + } + + [Fact] + public void AsyncEmitMainTest() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main() { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(""async main""); + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 0); + } + + [Fact] + public void AsyncMainTestCodegenWithErrors() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main() { + Console.WriteLine(""hello""); + await Task.Factory.StartNew(() => 5); + Console.WriteLine(""async main""); + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + c.VerifyEmitDiagnostics( + // (6,28): error CS0161: 'Program.Main()': not all code paths return a value + // static async Task Main() { + Diagnostic(ErrorCode.ERR_ReturnExpected, "Main").WithArguments("Program.Main()").WithLocation(6, 28)); + } + + + [Fact] + public void AsyncEmitMainOfIntTest_StringArgs() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main(string[] args) { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(""async main""); + return 10; + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 10); + } + + [Fact] + public void AsyncEmitMainOfIntTest_ParamsStringArgs() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main(params string[] args) { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(""async main""); + return 10; + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 10); + } + + [Fact] + public void AsyncEmitMainTest_StringArgs() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main(string[] args) { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(""async main""); + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 0); + } + + [Fact] + public void AsyncEmitMainTestCodegenWithErrors_StringArgs() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main(string[] args) { + Console.WriteLine(""hello""); + await Task.Factory.StartNew(() => 5); + Console.WriteLine(""async main""); + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + c.VerifyEmitDiagnostics( + // (6,28): error CS0161: 'Program.Main()': not all code paths return a value + // static async Task Main() { + Diagnostic(ErrorCode.ERR_ReturnExpected, "Main").WithArguments("Program.Main(string[])").WithLocation(6, 28)); + } + + [Fact] + public void AsyncEmitMainOfIntTest_StringArgs_WithArgs() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main(string[] args) { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(args[0]); + return 10; + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 10, args: new string[] { "async main" }); + } + + [Fact] + public void AsyncEmitMainTest_StringArgs_WithArgs() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program { + static async Task Main(string[] args) { + Console.Write(""hello ""); + await Task.Factory.StartNew(() => 5); + Console.Write(args[0]); + } +}"; + var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 0, args: new string[] { "async main" }); + } + + [Fact] + public void MainCanBeAsyncWithArgs() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); + + CompileAndVerify(compilation, expectedReturnCode: 0); + } + + [Fact] + public void MainCanReturnTaskWithArgs_NoAsync() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static Task Main(string[] args) + { + return Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); + + CompileAndVerify(compilation, expectedReturnCode: 0); + } + + + [Fact] + public void MainCantBeAsyncWithRefTask() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static ref Task Main(string[] args) + { + throw new System.Exception(); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics( + // (6,21): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point + // static ref Task Main(string[] args) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(6, 21), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void MainCantBeAsyncWithArgs_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main(string[] args) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + }").WithArguments("async main", "7.1").WithLocation(6, 5)); + } + + [Fact] + public void MainCanReturnTask() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); + } + [Fact] + public void MainCanReturnTask_NoAsync() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static Task Main() + { + return Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); + } + + [Fact] + public void MainCantBeAsyncVoid() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main() + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics( + // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // async static void Main() + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.Null(entry); + } + + [Fact] + public void MainCantBeAsyncVoid_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main() + { + await Task.Factory.StartNew(() => { }); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static void Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static void Main() + { + await Task.Factory.StartNew(() => { }); + }").WithArguments("async main", "7.1").WithLocation(6, 5)); + } + + [Fact] + public void MainCantBeAsyncInt() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static int Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + 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 + // async static int Main() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), + // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // async static void Main() + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.Null(entry); + } + + [Fact] + public void MainCantBeAsyncInt_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static int Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,22): error CS1983: The return type of an async method must be void, Task or Task + // async static int Main() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main"), + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static int Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static int Main() + { + return await Task.Factory.StartNew(() => 5); + }").WithArguments("async main", "7.1").WithLocation(6, 5) +); + } + + [Fact] + public void MainCanReturnTaskAndGenericOnInt_WithArgs() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); + } + + [Fact] + public void MainCanReturnTaskAndGenericOnInt_WithArgs_NoAsync() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static Task Main(string[] args) + { + return Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); + } + + [Fact] + public void MainCantBeAsyncAndGenericOnInt_WithArgs_Csharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main(string[] args) + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main(string[] args) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) + { + return await Task.Factory.StartNew(() => 5); + }").WithArguments("async main", "7.1").WithLocation(6, 5) +); + } + + [Fact] + public void MainCanReturnTaskAndGenericOnInt() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); + } + [Fact] + public void MainCanReturnTaskAndGenericOnInt_NoAsync() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static Task Main() + { + return Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics(); + var entry = compilation.GetEntryPoint(CancellationToken.None); + Assert.NotNull(entry); + Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); + } + + [Fact] + public void MainCantBeAsyncAndGenericOnInt_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + return await Task.Factory.StartNew(() => 5); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + compilation.VerifyDiagnostics( + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() + { + return await Task.Factory.StartNew(() => 5); + }").WithArguments("async main", "7.1").WithLocation(6, 5) + ); + } + + [Fact] + public void MainCantBeAsyncAndGenericOverFloats() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + await Task.Factory.StartNew(() => { }); + return 0; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + compilation.VerifyDiagnostics( + // (6,30): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // async static Task Main() + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 30), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void MainCantBeAsync_AndGeneric() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main() + { + await Task.Factory.StartNew(() => { }); + } +}"; + CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( + // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // async static void Main() + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void MainCantBeAsync_AndBadSig() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main(bool truth) + { + await Task.Factory.StartNew(() => { }); + } +}"; + CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + // (6,23): warning CS0028: 'A.Main(bool)' has the wrong signature to be an entry point + // async static void Main(bool truth) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(bool)").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void MainCantBeAsync_AndGeneric_AndBadSig() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main(bool truth) + { + await Task.Factory.StartNew(() => { }); + } +}"; + CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + // (6,23): warning CS0028: 'A.Main(bool)' has the wrong signature to be an entry point + // async static void Main(bool truth) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(bool)").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index 692b282000480..1860dcfaa6482 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -1222,382 +1222,7 @@ interface IInterface } [Fact] - public void MainCanBeAsyncWithArgs() - { - var origSource = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main(string[] args) - { - await Task.Factory.StartNew(() => { }); - } -}"; - var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "return") }; - foreach (var source in sources) - { - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics(); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.NotNull(entry); - Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); - } - } - - [Fact] - public void MainCantBeAsyncWithRefTask() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - static ref Task Main(string[] args) - { - throw new System.Exception(); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics( - // (6,21): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point - // static ref Task Main(string[] args) - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(6, 21), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); - } - - [Fact] - public void MainCantBeAsyncWithArgs_CSharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main(string[] args) - { - await Task.Factory.StartNew(() => { }); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static Task Main(string[] args) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) - { - await Task.Factory.StartNew(() => { }); - }").WithArguments("async main", "7.1").WithLocation(6, 5)); - } - - [Fact] - public void MainCanBeAsync() - { - var origSource = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main() - { - await Task.Factory.StartNew(() => { }); - } -}"; - var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "return") }; - foreach (var source in sources) - { - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics(); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.NotNull(entry); - Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); - } - } - - [Fact] - public void MainCantBeAsyncVoid() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main() - { - await Task.Factory.StartNew(() => { }); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics( - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point - // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.Null(entry); - } - - [Fact] - public void MainCantBeAsyncVoid_CSharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main() - { - await Task.Factory.StartNew(() => { }); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static void Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static void Main() - { - await Task.Factory.StartNew(() => { }); - }").WithArguments("async main", "7.1").WithLocation(6, 5)); - } - - [Fact] - public void MainCantBeAsyncInt() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static int Main() - { - return await Task.Factory.StartNew(() => 5); - } -}"; - 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 - // async static int Main() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point - // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 22), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.Null(entry); - } - - [Fact] - public void MainCantBeAsyncInt_CSharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static int Main() - { - return await Task.Factory.StartNew(() => 5); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( - // (6,22): error CS1983: The return type of an async method must be void, Task or Task - // async static int Main() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main"), - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static int Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static int Main() - { - return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) -); - } - - [Fact] - public void MainCanBeAsyncAndGenericOnIntWithArgs() - { - var origSource = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main(string[] args) - { - return await Task.Factory.StartNew(() => 5); - } -}"; - var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "") }; - foreach (var source in sources) - { - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics(); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.NotNull(entry); - Assert.Equal("System.Threading.Tasks.Task A.Main(System.String[] args)", entry.ToTestDisplayString()); - } - } - - [Fact] - public void MainCantBeAsyncAndGenericOnIntWithArgs_Csharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main(string[] args) - { - return await Task.Factory.StartNew(() => 5); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( -// (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static Task Main(string[] args) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) - { - return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) -); - } - - [Fact] - public void MainCanBeAsyncAndGenericOnInt() - { - var origSource = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main() - { - return await Task.Factory.StartNew(() => 5); - } -}"; - var sources = new string[] { origSource, origSource.Replace("async ", "").Replace("await", "") }; - foreach (var source in sources) - { - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics(); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.NotNull(entry); - Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); - } - } - - [Fact] - public void MainCantBeAsyncAndGenericOnInt_CSharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main() - { - return await Task.Factory.StartNew(() => 5); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static Task Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() - { - return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) - ); - } - - [Fact] - public void MainCantBeAsyncAndGenericOverFloats() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static Task Main() - { - await Task.Factory.StartNew(() => { }); - return 0; - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - compilation.VerifyDiagnostics( - // (6,30): warning CS0028: 'A.Main()' has the wrong signature to be an entry point - // async static Task Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 30), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1) ); - } - - [Fact] - public void MainCantBeAsync_AndGeneric() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main() - { - await Task.Factory.StartNew(() => { }); - } -}"; - CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( - // (4,23): warning CS0402: 'A.Main()': an entry point cannot be generic or in a generic type - // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()"), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint)); - } - [Fact] - public void MainCantBeAsync_AndBadSig() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main(bool truth) - { - await Task.Factory.StartNew(() => { }); - } -}"; - CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( - // (4,23): warning CS0028: 'A.Main(bool)' has the wrong signature to be an entry point - // async static void Main(bool truth) - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(bool)"), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint)); - } - - [Fact] - public void MainCantBeAsync_AndGeneric_AndBadSig() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main(bool truth) - { - await Task.Factory.StartNew(() => { }); - } -}"; - CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( - // (4,23): warning CS0028: 'A.Main(bool)' has the wrong signature to be an entry point - // async static void Main(bool truth) - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(bool)"), - // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint)); - } - - [Fact] public void AwaitInQuery_FirstCollectionExpressionOfInitialFrom() { var source = @" diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index a0ca5014afd75..e0b17fba31631 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -202,6 +203,110 @@ partial void M() {} Assert.Equal(1, m.First().Locations.Length); } + [Fact] + public void PartialExtractSyntaxLocation_DeclBeforeDef() + { + var text = +@"public partial class A { + partial void M(); +} +public partial class A { + partial void M() {} +} +"; + var comp = CreateStandardCompilation(text); + var global = comp.GlobalNamespace; + var a = global.GetTypeMembers("A", 0).Single(); + var m = (MethodSymbol) a.GetMembers("M").Single(); + Assert.True(m.IsPartialDefinition()); + var returnSyntax = m.ExtractReturnTypeSyntax(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var voidTokens = tree.GetRoot().DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).ToList(); + var node = tree.GetRoot().FindNode(voidTokens[0].Span); + + var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList(); + var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[1].Span)); + + Assert.Equal(node, returnSyntax); + Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax()); + } + + [Fact] + public void PartialExtractSyntaxLocation_DefBeforeDecl() + { + var text = +@"public partial class A { + partial void M() {} +} +public partial class A { + partial void M(); +} +"; + var comp = CreateStandardCompilation(text); + var global = comp.GlobalNamespace; + var a = global.GetTypeMembers("A", 0).Single(); + var m = (MethodSymbol) a.GetMembers("M").Single(); + Assert.True(m.IsPartialDefinition()); + var returnSyntax = m.ExtractReturnTypeSyntax(); + + 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); + + var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList(); + var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[0].Span)); + + Assert.Equal(node, returnSyntax); + Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax()); + } + + [Fact] + public void PartialExtractSyntaxLocation_OnlyDef() + { + var text = +@"public partial class A { + partial void M() {} +} +"; + var comp = CreateStandardCompilation(text); + var global = comp.GlobalNamespace; + var a = global.GetTypeMembers("A", 0).Single(); + var m = (MethodSymbol) a.GetMembers("M").Single(); + Assert.True(m.IsPartialImplementation()); + var returnSyntax = m.ExtractReturnTypeSyntax(); + + var tree = comp.SyntaxTrees.Single().GetRoot(); + var token = tree.DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).First(); + var node = tree.FindNode(token.Span); + + Assert.Equal(node, returnSyntax); + } + + [Fact] + public void PartialExtractSyntaxLocation_OnlyDecl() + { + var text = +@"public partial class A { + partial void M(); +} +"; + var comp = CreateStandardCompilation(text); + var global = comp.GlobalNamespace; + var a = global.GetTypeMembers("A", 0).Single(); + var m = (MethodSymbol) a.GetMembers("M").Single(); + Assert.True(m.IsPartialDefinition()); + var returnSyntax = m.ExtractReturnTypeSyntax(); + + var tree = comp.SyntaxTrees.Single().GetRoot(); + var token = tree.DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).First(); + var node = tree.FindNode(token.Span); + + Assert.Equal(node, returnSyntax); + } + [Fact] public void FullName() { diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 6e3c7bbbf80c9..917788c4d65bb 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -105,6 +105,8 @@ internal CompilationVerifier CompileAndVerify( Action symbolValidator = null, SignatureDescription[] expectedSignatures = null, string expectedOutput = null, + int? expectedReturnCode = null, + string[] args = null, CompilationOptions options = null, ParseOptions parseOptions = null, EmitOptions emitOptions = null, @@ -119,6 +121,8 @@ internal CompilationVerifier CompileAndVerify( Translate2(symbolValidator), expectedSignatures, expectedOutput, + expectedReturnCode, + args, options, parseOptions, emitOptions, @@ -185,6 +189,8 @@ internal CompilationVerifier CompileAndVerify( Action symbolValidator = null, SignatureDescription[] expectedSignatures = null, string expectedOutput = null, + int? expectedReturnCode = null, + string[] args = null, EmitOptions emitOptions = null, bool verify = true) { @@ -197,6 +203,8 @@ internal CompilationVerifier CompileAndVerify( Translate2(symbolValidator), expectedSignatures, expectedOutput, + expectedReturnCode, + args, emitOptions, verify); } diff --git a/src/Compilers/Test/Utilities/VisualBasic/BasicTestBase.vb b/src/Compilers/Test/Utilities/VisualBasic/BasicTestBase.vb index b4d2d8109833c..1f2063c4ad3bc 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/BasicTestBase.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/BasicTestBase.vb @@ -37,6 +37,8 @@ Public MustInherit Class BasicTestBase Friend Shadows Function CompileAndVerify( source As XElement, expectedOutput As XCData, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional additionalRefs As MetadataReference() = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, @@ -52,6 +54,8 @@ Public MustInherit Class BasicTestBase Return CompileAndVerify( source, XCDataToString(expectedOutput), + expectedReturnCode, + args, additionalRefs, dependencies, sourceSymbolValidator, @@ -73,6 +77,8 @@ Public MustInherit Class BasicTestBase Optional symbolValidator As Action(Of ModuleSymbol) = Nothing, Optional expectedSignatures As SignatureDescription() = Nothing, Optional expectedOutput As String = Nothing, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional emitOptions As EmitOptions = Nothing, Optional verify As Boolean = True) As CompilationVerifier @@ -85,6 +91,8 @@ Public MustInherit Class BasicTestBase Translate(symbolValidator), expectedSignatures, expectedOutput, + expectedReturnCode, + args, emitOptions, verify) End Function @@ -92,6 +100,7 @@ Public MustInherit Class BasicTestBase Friend Shadows Function CompileAndVerify( compilation As Compilation, expectedOutput As XCData, + Optional args As String() = Nothing, Optional manifestResources As IEnumerable(Of ResourceDescription) = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, @@ -110,6 +119,8 @@ Public MustInherit Class BasicTestBase symbolValidator, expectedSignatures, XCDataToString(expectedOutput), + Nothing, + args, emitOptions, verify) End Function @@ -117,6 +128,8 @@ Public MustInherit Class BasicTestBase Friend Shadows Function CompileAndVerify( source As XElement, Optional expectedOutput As String = Nothing, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional additionalRefs As MetadataReference() = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, @@ -136,6 +149,8 @@ Public MustInherit Class BasicTestBase Return Me.CompileAndVerify(source, allReferences, expectedOutput, + expectedReturnCode, + args, dependencies, sourceSymbolValidator, validator, @@ -152,6 +167,8 @@ Public MustInherit Class BasicTestBase source As XElement, allReferences As IEnumerable(Of MetadataReference), Optional expectedOutput As String = Nothing, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, Optional validator As Action(Of PEAssembly) = Nothing, @@ -180,6 +197,8 @@ Public MustInherit Class BasicTestBase Translate(symbolValidator), expectedSignatures, expectedOutput, + expectedReturnCode, + args, emitOptions, verify) End Function @@ -188,6 +207,8 @@ Public MustInherit Class BasicTestBase source As String, allReferences As IEnumerable(Of MetadataReference), Optional expectedOutput As String = Nothing, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, Optional validator As Action(Of PEAssembly) = Nothing, @@ -215,6 +236,8 @@ Public MustInherit Class BasicTestBase Translate(symbolValidator), expectedSignatures, expectedOutput, + expectedReturnCode, + args, emitOptions, verify) End Function @@ -223,6 +246,8 @@ Public MustInherit Class BasicTestBase source As XElement, allReferences As IEnumerable(Of MetadataReference), Optional expectedOutput As String = Nothing, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, Optional validator As Action(Of PEAssembly) = Nothing, @@ -236,6 +261,8 @@ Public MustInherit Class BasicTestBase source, allReferences, If(OSVersion.IsWin8, expectedOutput, Nothing), + If(OSVersion.IsWin8, expectedReturnCode, Nothing), + args, dependencies, sourceSymbolValidator, validator, @@ -249,6 +276,8 @@ Public MustInherit Class BasicTestBase Friend Shadows Function CompileAndVerifyOnWin8Only( source As XElement, expectedOutput As XCData, + Optional expectedReturnCode As Integer? = Nothing, + Optional args As String() = Nothing, Optional allReferences() As MetadataReference = Nothing, Optional dependencies As IEnumerable(Of ModuleData) = Nothing, Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing, @@ -263,6 +292,8 @@ Public MustInherit Class BasicTestBase source, allReferences, XCDataToString(expectedOutput), + expectedReturnCode, + args, dependencies, sourceSymbolValidator, validator, diff --git a/src/Test/Utilities/CoreClr/CoreCLRRuntimeEnvironment.cs b/src/Test/Utilities/CoreClr/CoreCLRRuntimeEnvironment.cs index 344c8311e0ee6..ff42a70857f42 100644 --- a/src/Test/Utilities/CoreClr/CoreCLRRuntimeEnvironment.cs +++ b/src/Test/Utilities/CoreClr/CoreCLRRuntimeEnvironment.cs @@ -72,23 +72,18 @@ public void Emit( } } - public (int ExitCode, string Output) Execute(string moduleName, int expectedOutputLength) + public int Execute(string moduleName, string[] args, string expectedOutput) { var emitData = GetEmitData(); emitData.RuntimeData.ExecuteRequested = true; - return emitData.LoadContext.Execute(GetMainImage(), expectedOutputLength); - } - - public int Execute(string moduleName, string expectedOutput) - { - var (exitCode, actualOutput) = Execute(moduleName, expectedOutput.Length); + var (ExitCode, Output) = emitData.LoadContext.Execute(GetMainImage(), args, expectedOutput?.Length); - if (expectedOutput.Trim() != actualOutput.Trim()) + if (expectedOutput != null && expectedOutput.Trim() != Output.Trim()) { - throw new ExecutionException(expectedOutput, actualOutput, moduleName); + throw new ExecutionException(expectedOutput, Output, moduleName); } - return exitCode; + return ExitCode; } private EmitData GetEmitData() => _emitData ?? throw new InvalidOperationException("Must call Emit before calling this method"); diff --git a/src/Test/Utilities/CoreClr/TestExecutionLoadContext.cs b/src/Test/Utilities/CoreClr/TestExecutionLoadContext.cs index 152eae58ea14a..ccc7c6b986657 100644 --- a/src/Test/Utilities/CoreClr/TestExecutionLoadContext.cs +++ b/src/Test/Utilities/CoreClr/TestExecutionLoadContext.cs @@ -95,7 +95,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray mainImage) } } - internal (int ExitCode, string Output) Execute(ImmutableArray mainImage, int expectedOutputLength) + internal (int ExitCode, string Output) Execute(ImmutableArray mainImage, string[] mainArgs, int? expectedOutputLength) { var mainAssembly = LoadImageAsAssembly(mainImage); var entryPoint = mainAssembly.EntryPoint; @@ -113,7 +113,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray mainImage) } else if (count == 1) { - args = new[] { Array.Empty() }; + args = new[] { mainArgs ?? Array.Empty() }; } else { @@ -121,7 +121,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray mainImage) } exitCode = entryPoint.Invoke(null, args) is int exit ? exit : 0; - }, expectedOutputLength, out var stdOut, out var stdErr); + }, expectedOutputLength ?? 0, out var stdOut, out var stdErr); var output = stdOut + stdErr; return (exitCode, output); diff --git a/src/Test/Utilities/Desktop/CodeRuntime/DesktopRuntimeEnvironment.cs b/src/Test/Utilities/Desktop/CodeRuntime/DesktopRuntimeEnvironment.cs index 0ce20aa65f134..d53f86217b336 100644 --- a/src/Test/Utilities/Desktop/CodeRuntime/DesktopRuntimeEnvironment.cs +++ b/src/Test/Utilities/Desktop/CodeRuntime/DesktopRuntimeEnvironment.cs @@ -236,13 +236,22 @@ public void Emit( } } - public int Execute(string moduleName, int expectedOutputLength, out string processOutput) + public int Execute(string moduleName, string[] args, string expectedOutput) { 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); + + if (expectedOutput != null && expectedOutput.Trim() != output.Trim()) + { + string dumpDir; + GetEmitData().Manager.DumpAssemblyData(out dumpDir); + throw new ExecutionException(expectedOutput, output, dumpDir); + } + + return resultCode; } catch (TargetInvocationException tie) { @@ -257,21 +266,6 @@ public int Execute(string moduleName, int expectedOutputLength, out string proce } } - public int Execute(string moduleName, string expectedOutput) - { - string actualOutput; - int exitCode = Execute(moduleName, expectedOutput.Length, out actualOutput); - - if (expectedOutput.Trim() != actualOutput.Trim()) - { - string dumpDir; - GetEmitData().Manager.DumpAssemblyData(out dumpDir); - throw new ExecutionException(expectedOutput, actualOutput, dumpDir); - } - - return exitCode; - } - private EmitData GetEmitData() { if (_emitData == null) diff --git a/src/Test/Utilities/Desktop/CodeRuntime/RuntimeAssemblyManager.cs b/src/Test/Utilities/Desktop/CodeRuntime/RuntimeAssemblyManager.cs index 6da88bfff43f6..d293baeee4b72 100644 --- a/src/Test/Utilities/Desktop/CodeRuntime/RuntimeAssemblyManager.cs +++ b/src/Test/Utilities/Desktop/CodeRuntime/RuntimeAssemblyManager.cs @@ -367,7 +367,7 @@ private SortedSet GetFullyQualifiedTypeNames(string assemblyName) return typeNames; } - public int Execute(string moduleName, int expectedOutputLength, out string output) + public int Execute(string moduleName, string[] mainArgs, int? expectedOutputLength, out string output) { ImmutableArray bytes = GetModuleBytesByName(moduleName); Assembly assembly = DesktopRuntimeUtil.LoadAsAssembly(moduleName, bytes); @@ -386,7 +386,7 @@ public int Execute(string moduleName, int expectedOutputLength, out string outpu } else if (count == 1) { - args = new object[] { new string[0] }; + args = new object[] { mainArgs ?? new string[0] }; } else { @@ -394,7 +394,7 @@ public int Execute(string moduleName, int expectedOutputLength, out string outpu } result = entryPoint.Invoke(null, args); - }, expectedOutputLength, out stdOut, out stdErr); + }, expectedOutputLength ?? 0, out stdOut, out stdErr); output = stdOut + stdErr; return result is int ? (int)result : 0; diff --git a/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs b/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs index 67a41c836262f..a6d74a7b1b800 100644 --- a/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs +++ b/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs @@ -88,7 +88,7 @@ internal ImmutableArray GetAllModuleMetadata() return modules; } - public void Emit(string expectedOutput, IEnumerable manifestResources, EmitOptions emitOptions, bool peVerify, SignatureDescription[] expectedSignatures) + public void Emit(string expectedOutput, int? expectedReturnCode, string[] args, IEnumerable manifestResources, EmitOptions emitOptions, bool peVerify, SignatureDescription[] expectedSignatures) { using (var testEnvironment = RuntimeEnvironmentFactory.Create(_dependencies)) { @@ -105,9 +105,14 @@ public void Emit(string expectedOutput, IEnumerable manifes MetadataSignatureUnitTestHelper.VerifyMemberSignatures(testEnvironment, expectedSignatures); } - if (expectedOutput != null) + if (expectedOutput != null || expectedReturnCode != null) { - testEnvironment.Execute(mainModuleName, expectedOutput); + var returnCode = testEnvironment.Execute(mainModuleName, args, expectedOutput); + + if (expectedReturnCode is int exCode) + { + Assert.Equal(exCode, returnCode); + } } } } diff --git a/src/Test/Utilities/Portable/CommonTestBase.cs b/src/Test/Utilities/Portable/CommonTestBase.cs index 1b944cf6208fd..d1adacc3af754 100644 --- a/src/Test/Utilities/Portable/CommonTestBase.cs +++ b/src/Test/Utilities/Portable/CommonTestBase.cs @@ -73,6 +73,8 @@ internal CompilationVerifier CompileAndVerify( Action symbolValidator = null, SignatureDescription[] expectedSignatures = null, string expectedOutput = null, + int? expectedReturnCode = null, + string[] args = null, CompilationOptions options = null, ParseOptions parseOptions = null, EmitOptions emitOptions = null, @@ -94,6 +96,8 @@ internal CompilationVerifier CompileAndVerify( symbolValidator, expectedSignatures, expectedOutput, + expectedReturnCode, + args, emitOptions, verify); } @@ -107,6 +111,8 @@ internal CompilationVerifier CompileAndVerify( Action symbolValidator = null, SignatureDescription[] expectedSignatures = null, string expectedOutput = null, + int? expectedReturnCode = null, + string[] args = null, EmitOptions emitOptions = null, bool verify = true) { @@ -136,6 +142,8 @@ internal CompilationVerifier CompileAndVerify( manifestResources, expectedSignatures, expectedOutput, + expectedReturnCode, + args ?? Array.Empty(), assemblyValidator, symbolValidator, emitOptions, @@ -199,6 +207,8 @@ internal CompilationVerifier Emit( IEnumerable manifestResources, SignatureDescription[] expectedSignatures, string expectedOutput, + int? expectedReturnCode, + string[] args, Action assemblyValidator, Action symbolValidator, EmitOptions emitOptions, @@ -208,7 +218,7 @@ internal CompilationVerifier Emit( verifier = new CompilationVerifier(this, compilation, dependencies); - verifier.Emit(expectedOutput, manifestResources, emitOptions, verify, expectedSignatures); + verifier.Emit(expectedOutput, expectedReturnCode, args, manifestResources, emitOptions, verify, expectedSignatures); // We're dual-purposing emitters here. In this context, it // tells the validator the version of Emit that is calling it. diff --git a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs index 11b640f8dcfa6..30d39f1b4cb0c 100644 --- a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs +++ b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs @@ -367,7 +367,7 @@ public interface IRuntimeEnvironmentFactory public interface IRuntimeEnvironment : IDisposable { void Emit(Compilation mainCompilation, IEnumerable manifestResources, EmitOptions emitOptions, bool usePdbForDebugging = false); - int Execute(string moduleName, string expectedOutput); + int Execute(string moduleName, string[] args, string expectedOutput); ImmutableArray GetMainImage(); ImmutableArray GetMainPdb(); ImmutableArray GetDiagnostics(); From 01b6ea3321f99724454c7cfb7044fb9cb8d57cc8 Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Fri, 28 Apr 2017 13:24:42 -0700 Subject: [PATCH 024/284] fix resx file --- src/Compilers/CSharp/Portable/CSharpResources.resx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 2967dd9950625..4812dab66b164 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5061,6 +5061,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ async main + Tuple element name '{0}' is inferred. Please use language version {1} or greater to access an element by its inferred name. From 32a9e283e257ca9a42f95ca0010153fb6f924486 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 2 May 2017 15:41:59 -0700 Subject: [PATCH 025/284] Add .mvid section to PE and remove dependency on MetadataReader (#19133) --- docs/compilers/CSharp/CommandLine.md | 2 + docs/compilers/Visual Basic/CommandLine.md | 2 + docs/features/refout.md | 9 +- .../Test/Emit/CSharpCompilerEmitTest.csproj | 3 + .../Test/Emit/Emit/CompilationEmitTests.cs | 124 ++++++++++- .../Test/Emit/Emit/DeterministicTests.cs | 25 ++- .../Core/MSBuildTask/CopyRefAssembly.cs | 6 +- .../Core/MSBuildTask/MSBuildTask.csproj | 1 + src/Compilers/Core/MSBuildTask/MvidReader.cs | 210 ++++++++++++++++++ src/Compilers/Core/MSBuildTask/project.json | 1 - .../Core/Portable/CodeAnalysis.csproj | 1 + .../Portable/PEWriter/ExtendedPEBuilder.cs | 100 +++++++++ .../Core/Portable/PEWriter/PeWriter.cs | 21 +- 13 files changed, 485 insertions(+), 20 deletions(-) create mode 100755 src/Compilers/Core/MSBuildTask/MvidReader.cs create mode 100755 src/Compilers/Core/Portable/PEWriter/ExtendedPEBuilder.cs diff --git a/docs/compilers/CSharp/CommandLine.md b/docs/compilers/CSharp/CommandLine.md index 8244ce7bfc71e..5f78b84f8a6ae 100644 --- a/docs/compilers/CSharp/CommandLine.md +++ b/docs/compilers/CSharp/CommandLine.md @@ -4,6 +4,7 @@ | ---- | ---- | | **OUTPUT FILES** | | `/out:`*file* | Specify output file name (default: base name of file with main class or first file) +| `/refout:`*file* | Specify the reference assembly's output file name | `/target:exe` | Build a console executable (default) (Short form: `/t:exe`) | `/target:winexe` | Build a Windows executable (Short form: `/t:winexe` ) | `/target:library` | Build a library (Short form: `/t:library`) @@ -32,6 +33,7 @@ | `/debug`:{`full`|`pdbonly`|`portable`} | Specify debugging type (`full` is default, and enables attaching a debugger to a running program. `portable` is a cross-platform format) | `/optimize`{`+`|`-`} | Enable optimizations (Short form: `/o`) | `/deterministic` | Produce a deterministic assembly (including module version GUID and timestamp) +| `/refonly | Produce a reference assembly, instead of a full assembly, as the primary output | **ERRORS AND WARNINGS** | `/warnaserror`{`+`|`-`} | Report all warnings as errors | `/warnaserror`{`+`|`-`}`:`*warn list* | Report specific warnings as errors diff --git a/docs/compilers/Visual Basic/CommandLine.md b/docs/compilers/Visual Basic/CommandLine.md index 0a7afcefd67da..e7c44304aa4fa 100644 --- a/docs/compilers/Visual Basic/CommandLine.md +++ b/docs/compilers/Visual Basic/CommandLine.md @@ -4,6 +4,7 @@ | ---- | ---- | | **OUTPUT FILE** | `/out:`*file* | Specifies the output file name. +| `/refout:`*file* | Specify the reference assembly's output file name | `/target:exe` | Create a console application (default). (Short form: `/t`) | `/target:winexe` | Create a Windows application. | `/target:library` | Create a library assembly. @@ -34,6 +35,7 @@ | `/debug:portable` | Emit debugging information in the portable format. | `/debug:pdbonly` | Emit PDB file only. | `/deterministic` | Produce a deterministic assembly (including module version GUID and timestamp) +| `/refonly | Produce a reference assembly, instead of a full assembly, as the primary output | **ERRORS AND WARNINGS** | `/nowarn` | Disable all warnings. | `/nowarn:`*number_list* | Disable a list of individual warnings. diff --git a/docs/features/refout.md b/docs/features/refout.md index 9e533c52c72f7..9ab450afc9ffc 100644 --- a/docs/features/refout.md +++ b/docs/features/refout.md @@ -32,11 +32,12 @@ Two mutually exclusive command-line parameters will be added to `csc.exe` and `v - `/refout` - `/refonly` -The `/refout` parameter specifies a file path where the ref assembly should be output. This translates to `metadataPeStream` in the `Emit` API (see details below). +The `/refout` parameter specifies a file path where the ref assembly should be output. This translates to `metadataPeStream` in the `Emit` API (see details below). The filename for the ref assembly should generally match that of the primary assembly, but it can be in a different folder. The `/refonly` parameter is a flag that indicates that a ref assembly should be output instead of an implementation assembly. The `/refonly` parameter is not allowed together with the `/refout` parameter, as it doesn't make sense to have both the primary and secondary outputs be ref assemblies. Also, the `/refonly` parameter silently disables outputting PDBs, as ref assemblies cannot be executed. The `/refonly` parameter translates to `EmitMetadataOnly` being `true`, and `IncludePrivateMembers` being `false` in the `Emit` API (see details below). +Neither `/refonly` nor `/refout` are permitted with `/target:module` or `/addmodule` options. When the compiler produces documentation, the contents produced will match the APIs that go into the primary output. In other words, the documentation will be filtered down when using the `/refonly` parameter. @@ -47,13 +48,14 @@ The `CoreCompile` target will support a new output, called `IntermediateRefAssem The `Csc` task will support a new output, called `OutputRefAssembly`, which parallels the existing `OutputAssembly`. Both of those basically map to the `/refout` command-line parameter. -An additional task, called `CopyRefAssembly`, will be provided along with the existing `Csc` task. It takes a `SourcePath` and a `DestinationPath` and generally copies the file from the source over to the destination. But if it can determine that the contents of those two files match, then the destination file is left untouched. +An additional task, called `CopyRefAssembly`, will be provided along with the existing `Csc` task. It takes a `SourcePath` and a `DestinationPath` and generally copies the file from the source over to the destination. But if it can determine that the contents of those two files match (by comparing their MVIDs, see details below), then the destination file is left untouched. ### CodeAnalysis APIs It is already possible to produce metadata-only assemblies by using `EmitOptions.EmitMetadataOnly`, which is used in IDE scenarios with cross-language dependencies. The compiler will be updated to honour the `EmitOptions.IncludePrivateMembers` flag as well. When combined with `EmitMetadataOnly` or a `metadataPeStream` in `Emit`, a ref assembly will be produced. -The diagnostic check for emitting methods lacking a body (`void M();`) will be moved from declaration diagnostics to regular diagnostics, so that code will successfully emit with `EmitMetadataOnly`. +The diagnostic check for emitting methods lacking a body (`void M();`) will be filtered from declaration diagnostics, so that code will successfully emit with `EmitMetadataOnly`. Later on, the `EmitOptions.TolerateErrors` flag will allow emitting error types as well. +`Emit` is also modified to produce a new PE section called ".mvid" containing a copy of the MVID, when producing ref assemblies. This makes it easy for `CopyRefAssembly` to extract and compare MVIDs from ref assemblies. Going back to the 4 driving scenarios: 1. For a regular compilation, `EmitMetadataOnly` is left to `false` and no `metadataPeStream` is passed into `Emit`. @@ -70,7 +72,6 @@ As mentioned above, there may be further refinements after C# 7.1: - should explicit method implementations be included in ref assemblies? - Non-public attributes on public APIs (emit attribute based on accessibility rule) - ref assemblies and NoPia -- `/refout` and `/addmodule`, should we disallow this combination? ## Related issues - Produce ref assemblies from command-line and msbuild (https://github.com/dotnet/roslyn/issues/2184) diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index 2b69608a19d3d..89673c5a909f2 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -55,6 +55,9 @@ + + Emit\MvidReader.cs + diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index abc39c4f664aa..77ed494cf7dc7 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; @@ -49,7 +50,6 @@ public void Main() Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "x")); } - [Fact] public void CompilationEmitWithQuotedMainType() { @@ -262,9 +262,11 @@ internal static void Main() VerifyEntryPoint(output, expectZero: false); VerifyMethods(output, new[] { "void C.Main()", "C..ctor()" }); + VerifyMvid(output, hasMvidSection: false); VerifyEntryPoint(metadataOutput, expectZero: true); VerifyMethods(metadataOutput, new[] { "C..ctor()" }); + VerifyMvid(metadataOutput, hasMvidSection: true); } void VerifyEntryPoint(MemoryStream stream, bool expectZero) @@ -275,6 +277,110 @@ void VerifyEntryPoint(MemoryStream stream, bool expectZero) } } + private class TestResourceSectionBuilder : ResourceSectionBuilder + { + public TestResourceSectionBuilder() + { + } + + protected override void Serialize(BlobBuilder builder, SectionLocation location) + { + builder.WriteInt32(0x12345678); + builder.WriteInt32(location.PointerToRawData); + builder.WriteInt32(location.RelativeVirtualAddress); + } + } + + private class TestPEBuilder : ManagedPEBuilder + { + public static readonly Guid s_mvid = Guid.Parse("a78fa2c3-854e-42bf-8b8d-75a450a6dc18"); + + public TestPEBuilder(PEHeaderBuilder header, + MetadataRootBuilder metadataRootBuilder, + BlobBuilder ilStream, + ResourceSectionBuilder nativeResources) + : base(header, metadataRootBuilder, ilStream, nativeResources: nativeResources) + { + } + + protected override ImmutableArray
CreateSections() + { + return base.CreateSections().Add( + new Section(".mvid", SectionCharacteristics.MemRead | + SectionCharacteristics.ContainsInitializedData | + SectionCharacteristics.MemDiscardable)); + } + + protected override BlobBuilder SerializeSection(string name, SectionLocation location) + { + if (name.Equals(".mvid", StringComparison.Ordinal)) + { + var sectionBuilder = new BlobBuilder(); + sectionBuilder.WriteGuid(s_mvid); + return sectionBuilder; + } + + return base.SerializeSection(name, location); + } + } + + [Fact] + public void MvidSectionNotFirst() + { + var ilBuilder = new BlobBuilder(); + var metadataBuilder = new MetadataBuilder(); + + var peBuilder = new TestPEBuilder( + PEHeaderBuilder.CreateLibraryHeader(), + new MetadataRootBuilder(metadataBuilder), + ilBuilder, + nativeResources: new TestResourceSectionBuilder()); + + var peBlob = new BlobBuilder(); + peBuilder.Serialize(peBlob); + + var peStream = new MemoryStream(); + peBlob.WriteContentTo(peStream); + + peStream.Position = 0; + using (var peReader = new PEReader(peStream)) + { + AssertEx.Equal(new[] { ".text", ".rsrc", ".reloc", ".mvid" }, + peReader.PEHeaders.SectionHeaders.Select(h => h.Name)); + + peStream.Position = 0; + var mvid = BuildTasks.MvidReader.ReadAssemblyMvidOrEmpty(peStream); + Assert.Equal(TestPEBuilder.s_mvid, mvid); + } + } + + /// + /// Extract the MVID using two different methods (PEReader and MvidReader) and compare them. + /// We only expect an .mvid section in ref assemblies. + /// + private void VerifyMvid(MemoryStream stream, bool hasMvidSection) + { + stream.Position = 0; + using (var reader = new PEReader(stream)) + { + var metadataReader = reader.GetMetadataReader(); + Guid mvidFromModuleDefinition = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + + stream.Position = 0; + var mvidFromMvidReader = BuildTasks.MvidReader.ReadAssemblyMvidOrEmpty(stream); + + Assert.NotEqual(Guid.Empty, mvidFromModuleDefinition); + if (hasMvidSection) + { + Assert.Equal(mvidFromModuleDefinition, mvidFromMvidReader); + } + else + { + Assert.Equal(Guid.Empty, mvidFromMvidReader); + } + } + } + [Fact] public void EmitRefAssembly_PrivatePropertySetter() { @@ -296,6 +402,8 @@ public class C VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateSetter.get", "void C.PrivateSetter.set", "C..ctor()", "System.Int32 C.PrivateSetter { get; private set; }" }); VerifyMethods(metadataOutput, new[] { "System.Int32 C.PrivateSetter.get", "C..ctor()", "System.Int32 C.PrivateSetter { get; }" }); + VerifyMvid(output, hasMvidSection: false); + VerifyMvid(metadataOutput, hasMvidSection: true); } } @@ -559,6 +667,20 @@ private void CompareAssemblies(string sourceTemplate, string change1, string cha { AssertEx.NotEqual(image1, image2, message: $"Expecting difference for includePrivateMembers={includePrivateMembers} case, but they matched."); } + + var mvid1 = BuildTasks.MvidReader.ReadAssemblyMvidOrEmpty(new MemoryStream(image1.DangerousGetUnderlyingArray())); + var mvid2 = BuildTasks.MvidReader.ReadAssemblyMvidOrEmpty(new MemoryStream(image2.DangerousGetUnderlyingArray())); + + if (!includePrivateMembers) + { + Assert.NotEqual(Guid.Empty, mvid1); + Assert.Equal(expectMatch, mvid1 == mvid2); + } + else + { + Assert.Equal(Guid.Empty, mvid1); + Assert.Equal(Guid.Empty, mvid2); + } } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs index 2d98c539a82f6..a3a6be5cbb7b4 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs @@ -18,14 +18,19 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit public class DeterministicTests : EmitMetadataTestBase { private Guid CompiledGuid(string source, string assemblyName, bool debug) + { + return CompiledGuid(source, assemblyName, options: debug ? TestOptions.DebugExe : TestOptions.ReleaseExe); + } + + private Guid CompiledGuid(string source, string assemblyName, CSharpCompilationOptions options, EmitOptions emitOptions = null) { var compilation = CreateCompilation(source, assemblyName: assemblyName, references: new[] { MscorlibRef }, - options: (debug ? TestOptions.DebugExe : TestOptions.ReleaseExe).WithDeterministic(true)); + options: options.WithDeterministic(true)); Guid result = default(Guid); - base.CompileAndVerify(compilation, validator: a => + base.CompileAndVerify(compilation, emitOptions: emitOptions, validator: a => { var module = a.Modules[0]; result = module.GetModuleVersionIdOrThrow(); @@ -104,6 +109,22 @@ public static void Main(string[] args) {} Assert.NotEqual(mvid3, mvid7); } + [Fact] + public void RefAssembly() + { + var source = +@"class Program +{ + public static void Main(string[] args) {} + CHANGE +}"; + var emitRefAssembly = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + + var mvid1 = CompiledGuid(source.Replace("CHANGE", ""), "X1", TestOptions.DebugDll, emitRefAssembly); + var mvid2 = CompiledGuid(source.Replace("CHANGE", "private void M() { }"), "X1", TestOptions.DebugDll, emitRefAssembly); + Assert.Equal(mvid1, mvid2); + } + const string CompareAllBytesEmitted_Source = @" using System; using System.Linq; diff --git a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs index 1c2f34c70646a..5b12dc838c517 100644 --- a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs +++ b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs @@ -2,8 +2,6 @@ using System; using System.IO; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -85,10 +83,8 @@ private bool Copy() private Guid ExtractMvid(string path) { using (FileStream source = File.OpenRead(path)) - using (var reader = new PEReader(source)) { - var metadataReader = reader.GetMetadataReader(); - return metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + return MvidReader.ReadAssemblyMvidOrEmpty(source); } } } diff --git a/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj b/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj index cbced79b1b505..9c161e6aa519b 100644 --- a/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj +++ b/src/Compilers/Core/MSBuildTask/MSBuildTask.csproj @@ -55,6 +55,7 @@ + diff --git a/src/Compilers/Core/MSBuildTask/MvidReader.cs b/src/Compilers/Core/MSBuildTask/MvidReader.cs new file mode 100755 index 0000000000000..745fdf24fbb11 --- /dev/null +++ b/src/Compilers/Core/MSBuildTask/MvidReader.cs @@ -0,0 +1,210 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.CodeAnalysis.BuildTasks +{ + public static class MvidReader + { + private static readonly Guid s_empty = Guid.Empty; + + public static Guid ReadAssemblyMvidOrEmpty(Stream stream) + { + return ReadAssemblyMvidOrEmpty(new BinaryReader(stream)); + } + + private static Guid ReadAssemblyMvidOrEmpty(BinaryReader reader) + { + // DOS Header: Magic number (2) + if (!ReadUInt16(reader, out ushort magicNumber) || magicNumber != 0x5a4d) // "MZ" + { + return s_empty; + } + + // DOS Header: Address of PE Signature (at 0x3C) + if (!MoveTo(0x3C, reader)) + { + return s_empty; + } + if (!ReadUInt32(reader, out uint pointerToPeSignature)) + { + return s_empty; + } + + // jump over the MS DOS Stub to the PE Signature + if (!MoveTo(pointerToPeSignature, reader)) + { + return s_empty; + } + + // PE Signature ('P' 'E' null null) + if (!ReadUInt32(reader, out uint peSig) || peSig != 0x00004550) + { + return s_empty; + } + + // COFF Header: Machine (2) + if (!Skip(2, reader)) + { + return s_empty; + } + + // COFF Header: NumberOfSections (2) + if (!ReadUInt16(reader, out ushort sections)) + { + return s_empty; + } + + // COFF Header: TimeDateStamp (4), PointerToSymbolTable (4), NumberOfSymbols (4) + if (!Skip(12, reader)) + { + return s_empty; + } + + // COFF Header: OptionalHeaderSize (2) + if (!ReadUInt16(reader, out ushort optionalHeaderSize)) + { + return s_empty; + } + + // COFF Header: Characteristics (2) + if (!Skip(2, reader)) + { + return s_empty; + } + + // Optional header + if (!Skip(optionalHeaderSize, reader)) + { + return s_empty; + } + + // Section headers + return FindMvidInSections(sections, reader); + } + + private static Guid FindMvidInSections(ushort count, BinaryReader reader) + { + for (int i = 0; i < count; i++) + { + // Section: Name (8) + if (!ReadBytes(reader, 8, out byte[] name)) + { + return s_empty; + } + + if (name.Length == 8 && name[0] == '.' && + name[1] == 'm' && name[2] == 'v' && name[3] == 'i' && name[4] == 'd' && name[5] == '\0') + { + // Section: VirtualSize (4) + if (!ReadUInt32(reader, out uint virtualSize) || virtualSize != 16) + { + // The .mvid section only stores a Guid + return s_empty; + } + + // Section: VirtualAddress (4), SizeOfRawData (4) + if (!Skip(8, reader)) + { + return s_empty; + } + + // Section: PointerToRawData (4) + if (!ReadUInt32(reader, out uint pointerToRawData)) + { + return s_empty; + } + + return ReadMvidSection(reader, pointerToRawData); + } + else + { + // Section: VirtualSize (4), VirtualAddress (4), SizeOfRawData (4), + // PointerToRawData (4), PointerToRelocations (4), PointerToLineNumbers (4), + // NumberOfRelocations (2), NumberOfLineNumbers (2), Characteristics (4) + if (!Skip(4 + 4 + 4 + 4 + 4 + 4 + 2 + 2 + 4, reader)) + { + return s_empty; + } + } + } + + return s_empty; + } + + private static Guid ReadMvidSection(BinaryReader reader, uint pointerToMvidSection) + { + if (!MoveTo(pointerToMvidSection, reader)) + { + return s_empty; + } + + if (!ReadBytes(reader, 16, out byte[] guidBytes)) + { + return s_empty; + } + + return new Guid(guidBytes); + } + + private static bool ReadUInt16(BinaryReader reader, out ushort output) + { + if (reader.BaseStream.Position + 2 >= reader.BaseStream.Length) + { + output = 0; + return false; + } + + output = reader.ReadUInt16(); + return true; + } + + private static bool ReadUInt32(BinaryReader reader, out uint output) + { + if (reader.BaseStream.Position + 4 >= reader.BaseStream.Length) + { + output = 0; + return false; + } + + output = reader.ReadUInt32(); + return true; + } + + private static bool ReadBytes(BinaryReader reader, int count, out byte[] output) + { + if (reader.BaseStream.Position + count >= reader.BaseStream.Length) + { + output = null; + return false; + } + + output = reader.ReadBytes(count); + return true; + } + + private static bool Skip(int bytes, BinaryReader reader) + { + if (reader.BaseStream.Position + bytes >= reader.BaseStream.Length) + { + return false; + } + + reader.BaseStream.Seek(bytes, SeekOrigin.Current); + return true; + } + + private static bool MoveTo(uint position, BinaryReader reader) + { + if (position >= reader.BaseStream.Length) + { + return false; + } + + reader.BaseStream.Seek(position, SeekOrigin.Begin); + return true; + } + } +} diff --git a/src/Compilers/Core/MSBuildTask/project.json b/src/Compilers/Core/MSBuildTask/project.json index c9efa1ac00175..ac0c51a5b3082 100644 --- a/src/Compilers/Core/MSBuildTask/project.json +++ b/src/Compilers/Core/MSBuildTask/project.json @@ -15,7 +15,6 @@ "System.IO.Pipes": "4.3.0", "System.Linq": "4.3.0", "System.Reflection": "4.3.0", - "System.Reflection.Metadata": "1.4.2", "System.Security.AccessControl": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Principal.Windows": "4.3.0", diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 1581cbd140c2f..d871379ace22a 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -140,6 +140,7 @@ + diff --git a/src/Compilers/Core/Portable/PEWriter/ExtendedPEBuilder.cs b/src/Compilers/Core/Portable/PEWriter/ExtendedPEBuilder.cs new file mode 100755 index 0000000000000..608d66faed3c2 --- /dev/null +++ b/src/Compilers/Core/Portable/PEWriter/ExtendedPEBuilder.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using Microsoft.CodeAnalysis; + +namespace Microsoft.Cci +{ + /// + /// This PEBuilder adds an .mvid section. + /// + internal sealed class ExtendedPEBuilder + : ManagedPEBuilder + { + private const string MvidSectionName = ".mvid"; + public const int SizeOfGuid = 16; + + // When the section is built with a placeholder, the placeholder blob is saved for later fixing up. + private Blob _mvidSectionFixup = default(Blob); + + // Only include the .mvid section in ref assemblies + private readonly bool _withMvidSection; + + public ExtendedPEBuilder( + PEHeaderBuilder header, + MetadataRootBuilder metadataRootBuilder, + BlobBuilder ilStream, + BlobBuilder mappedFieldData, + BlobBuilder managedResources, + ResourceSectionBuilder nativeResources, + DebugDirectoryBuilder debugDirectoryBuilder, + int strongNameSignatureSize, + MethodDefinitionHandle entryPoint, + CorFlags flags, + Func, BlobContentId> deterministicIdProvider, + bool withMvidSection) + : base(header, metadataRootBuilder, ilStream, mappedFieldData, managedResources, nativeResources, + debugDirectoryBuilder, strongNameSignatureSize, entryPoint, flags, deterministicIdProvider) + { + _withMvidSection = withMvidSection; + } + + protected override ImmutableArray
CreateSections() + { + var baseSections = base.CreateSections(); + + if (_withMvidSection) + { + var builder = ArrayBuilder
.GetInstance(baseSections.Length + 1); + + builder.Add(new Section(MvidSectionName, SectionCharacteristics.MemRead | + SectionCharacteristics.ContainsInitializedData | + SectionCharacteristics.MemDiscardable)); + + builder.AddRange(baseSections); + return builder.ToImmutableAndFree(); + } + else + { + return baseSections; + } + } + + protected override BlobBuilder SerializeSection(string name, SectionLocation location) + { + if (name.Equals(MvidSectionName, StringComparison.Ordinal)) + { + Debug.Assert(_withMvidSection); + return SerializeMvidSection(location); + } + + return base.SerializeSection(name, location); + } + + internal BlobContentId Serialize(BlobBuilder peBlob, out Blob mvidSectionFixup) + { + var result = base.Serialize(peBlob); + mvidSectionFixup = _mvidSectionFixup; + return result; + } + + private BlobBuilder SerializeMvidSection(SectionLocation location) + { + var sectionBuilder = new BlobBuilder(); + + // The guid will be filled in later: + _mvidSectionFixup = sectionBuilder.ReserveBytes(SizeOfGuid); + var mvidWriter = new BlobWriter(_mvidSectionFixup); + mvidWriter.WriteBytes(0, _mvidSectionFixup.Length); + Debug.Assert(mvidWriter.RemainingBytes == 0); + + return sectionBuilder; + } + } +} diff --git a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs index cd9011bb617ab..a84e660f48385 100644 --- a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; @@ -69,7 +68,7 @@ internal static bool WritePeToStream( MethodDefinitionHandle entryPointHandle; MethodDefinitionHandle debugEntryPointHandle; mdWriter.GetEntryPoints(out entryPointHandle, out debugEntryPointHandle); - + if (!debugEntryPointHandle.IsNil) { nativePdbWriterOpt?.SetEntryPoint((uint)MetadataTokens.GetToken(debugEntryPointHandle)); @@ -186,7 +185,7 @@ internal static bool WritePeToStream( debugDirectoryBuilder = null; } - var peBuilder = new ManagedPEBuilder( + var peBuilder = new ExtendedPEBuilder( peHeaderBuilder, metadataRootBuilder, ilBuilder, @@ -197,12 +196,13 @@ internal static bool WritePeToStream( CalculateStrongNameSignatureSize(context.Module), entryPointHandle, properties.CorFlags, - deterministicIdProvider); + deterministicIdProvider, + metadataOnly && !context.IncludePrivateMembers); var peBlob = new BlobBuilder(); - var peContentId = peBuilder.Serialize(peBlob); + var peContentId = peBuilder.Serialize(peBlob, out Blob mvidSectionFixup); - PatchModuleVersionIds(mvidFixup, mvidStringFixup, peContentId.Guid); + PatchModuleVersionIds(mvidFixup, mvidSectionFixup, mvidStringFixup, peContentId.Guid); try { @@ -216,7 +216,7 @@ internal static bool WritePeToStream( return true; } - private static void PatchModuleVersionIds(Blob guidFixup, Blob stringFixup, Guid mvid) + private static void PatchModuleVersionIds(Blob guidFixup, Blob guidSectionFixup, Blob stringFixup, Guid mvid) { if (!guidFixup.IsDefault) { @@ -225,6 +225,13 @@ private static void PatchModuleVersionIds(Blob guidFixup, Blob stringFixup, Guid Debug.Assert(writer.RemainingBytes == 0); } + if (!guidSectionFixup.IsDefault) + { + var writer = new BlobWriter(guidSectionFixup); + writer.WriteGuid(mvid); + Debug.Assert(writer.RemainingBytes == 0); + } + if (!stringFixup.IsDefault) { var writer = new BlobWriter(stringFixup); From a255d49117941fbf404ebdbbe6b2635ba1546bd8 Mon Sep 17 00:00:00 2001 From: Rainer Sigwald Date: Wed, 3 May 2017 17:11:48 -0500 Subject: [PATCH 026/284] Introduce ReferencePathWithRefAssemblies parameter to CoreCompile target, and shim (#19146) * Use refasm-only items for compilation Use the new item provided by common targets to avoid recompiling a project when a reference has changed only in implementation, not interface. Include a shim so that these targets can continue to work on older MSBuild distributions. That keeps the NuGet-package-for-downlevel scenario working, as well as making an easy transition period to the new logic. Requires Microsoft/msbuild#2039 to enable end-to-end use of reference assemblies, but is compatible with earlier MSBuilds (without using reference assemblies). * Use CopyRefAssembly from NuGet package The Microsoft.Net.Compilers package delivers UsingTasks to override the "standard" ones provided by MSBuild's Microsoft.Common.tasks. That should include the new CopyRefAssembly. --- .../Microsoft.Net.Compilers.props | 2 ++ .../MSBuildTask/Microsoft.CSharp.Core.targets | 20 +++++++++++++++---- .../Microsoft.VisualBasic.Core.targets | 20 +++++++++++++++---- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/build/NuGetAdditionalFiles/Microsoft.Net.Compilers.props b/build/NuGetAdditionalFiles/Microsoft.Net.Compilers.props index 769370f3a3252..d65f4c022464d 100644 --- a/build/NuGetAdditionalFiles/Microsoft.Net.Compilers.props +++ b/build/NuGetAdditionalFiles/Microsoft.Net.Compilers.props @@ -23,6 +23,8 @@ AssemblyFile="$(MSBuildThisFileDirectory)..\tools\Microsoft.Build.Tasks.CodeAnalysis.dll" /> + false diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets index 5612c758c3d68..885d32aaaa60c 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets @@ -1,13 +1,25 @@  + + + + + + + - + - + @@ -117,7 +129,7 @@ Prefer32Bit="$(Prefer32Bit)" PreferredUILang="$(PreferredUILang)" ProvideCommandLineArgs="$(ProvideCommandLineArgs)" - References="@(ReferencePath)" + References="@(ReferencePathWithRefAssemblies)" ReportAnalyzer="$(ReportAnalyzer)" Resources="@(_CoreCompileResourceInputs);@(CompiledLicenseFile)" ResponseFiles="$(CompilerResponseFile)" diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets index eef272a4b8c59..89249ef700b6b 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets @@ -1,13 +1,25 @@  + + + + + + + - + - + @@ -109,7 +121,7 @@ Prefer32Bit="$(Prefer32Bit)" PreferredUILang="$(PreferredUILang)" ProvideCommandLineArgs="$(ProvideCommandLineArgs)" - References="@(ReferencePath)" + References="@(ReferencePathWithRefAssemblies)" RemoveIntegerChecks="$(RemoveIntegerChecks)" ReportAnalyzer="$(ReportAnalyzer)" Resources="@(_CoreCompileResourceInputs);@(CompiledLicenseFile)" From 5a155a4fa72bfc518402830a99899cbf6307f0a9 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 4 May 2017 07:49:14 -0500 Subject: [PATCH 027/284] Verify member is in scope before trying to replace it --- .../Extensions/ExpressionSyntaxExtensions.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs index 8ebf9a4812d29..87a897aebe7b6 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs @@ -799,6 +799,25 @@ private static bool TryReduce( } } + // Try to eliminate cases without actually calling CanReplaceWithReducedName. For expressions of the form + // 'this.Name' or 'base.Name', no additional check here is required. + if (!memberAccess.Expression.IsKind(SyntaxKind.ThisExpression, SyntaxKind.BaseExpression)) + { + var actualSymbol = semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken); + if (actualSymbol.GetAnySymbol() is null) + { + return false; + } + + // Get a syntax node that's not part of the tree to make sure we speculate on just the name. + var speculativeTree = memberAccess.Name.WithoutTrivia(); + var symbolInfo = semanticModel.GetSpeculativeSymbolInfo(memberAccess.SpanStart, speculativeTree, SpeculativeBindingOption.BindAsExpression); + if (!IsReplacementCandidate(actualSymbol, symbolInfo)) + { + return false; + } + } + replacementNode = memberAccess.Name .WithLeadingTrivia(memberAccess.GetLeadingTriviaForSimplifiedMemberAccess()) .WithTrailingTrivia(memberAccess.GetTrailingTrivia()); @@ -812,6 +831,38 @@ private static bool TryReduce( return memberAccess.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken); } + /// + /// Determines if contains a superset of the symbols in + /// . + /// + private static bool IsReplacementCandidate(SymbolInfo actualSymbol, SymbolInfo speculativeSymbol) + { + if (!(speculativeSymbol.Symbol is null)) + { + return Equals(actualSymbol.Symbol, speculativeSymbol.Symbol); + } + + if (speculativeSymbol.CandidateSymbols.IsDefault) + { + return false; + } + + if (!(actualSymbol.Symbol is null)) + { + return speculativeSymbol.CandidateSymbols.Contains(actualSymbol.Symbol); + } + + foreach (var symbol in actualSymbol.CandidateSymbols) + { + if (!speculativeSymbol.CandidateSymbols.Contains(symbol)) + { + return false; + } + } + + return true; + } + private static SyntaxTriviaList GetLeadingTriviaForSimplifiedMemberAccess(this MemberAccessExpressionSyntax memberAccess) { // We want to include any user-typed trivia that may be present between the 'Expression', 'OperatorToken' and 'Identifier' of the MemberAccessExpression. From 5d027884c5db500f0cecea0d8b76d9e1dd4b65ba Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 4 May 2017 12:21:51 -0500 Subject: [PATCH 028/284] Allow the Simplify Type Names analyzer to run concurrently --- .../Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs index cda34277c5b9f..d2c93404d8418 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/SimplifyTypeNamesDiagnosticAnalyzerBase.cs @@ -88,6 +88,8 @@ public bool OpenFileOnly(Workspace workspace) public sealed override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeNode, _kindsOfInterest); } From 5546f8614809ac00a7b0502957a11b5a54f87212 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Thu, 4 May 2017 23:07:09 +0430 Subject: [PATCH 029/284] Preserve trivia when converting a numeric literal base --- .../ConvertNumericLiteralTests.cs | 27 ++++++++++++++++++- .../ConvertNumericLiteralTests.vb | 24 ++++++++++++++++- ...rtNumericLiteralCodeRefactoringProvider.cs | 3 ++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.cs index 6d0dc701e9814..a1045549a699f 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.cs @@ -26,7 +26,7 @@ private async Task TestFixOneAsync(string initial, string expected, Refactoring await TestInRegularAndScriptAsync(CreateTreeText("[||]" + initial), CreateTreeText(expected), index: (int)refactoring); } - private string CreateTreeText(string initial) + private static string CreateTreeText(string initial) { return @"class X { void F() { var x = " + initial + @"; } }"; } @@ -96,5 +96,30 @@ public async Task TestTypeCharacter() { await TestFixOneAsync("0x1e5UL", "0b111100101UL", Refactoring.ChangeBase2); } + + [WorkItem(19225, "https://github.com/dotnet/roslyn/issues/19225")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertNumericLiteral)] + public async Task TestPreserveWhitespaces() + { + await TestInRegularAndScriptAsync( +@"class Program +{ + void M() + { + var numbers = new int[] { + [||]0x1, 0x2 + }; + } +}", +@"class Program +{ + void M() + { + var numbers = new int[] { + 0b1, 0x2 + }; + } +}", index: (int)Refactoring.ChangeBase2, ignoreTrivia: false); + } } } diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb index 9af183e810f99..b38d228341454 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb @@ -26,7 +26,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.Conver Await TestInRegularAndScriptAsync(CreateTreeText("[||]" + initial), CreateTreeText(expected), index:=DirectCast(refactoring, Integer)) End Function - Private Function CreateTreeText(initial As String) As String + Private Shared Function CreateTreeText(initial As String) As String Return " Class X Sub M() @@ -89,5 +89,27 @@ End Class" Public Async Function TestTypeCharacter() As Task Await TestFixOneAsync("&H1e5UL", "&B111100101UL", Refactoring.ChangeBase2) End Function + + + + Public Async Function TestPreserveTrivia() As Task + Await TestInRegularAndScriptAsync( +"Class X + Sub M() + Dim x As Integer() = + { + [||]&H1, &H2 + } + End Sub +End Class", +"Class X + Sub M() + Dim x As Integer() = + { + &B1, &H2 + } + End Sub +End Class", index := Refactoring.ChangeBase2, ignoreTrivia := False) + End Function End Class End Namespace diff --git a/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs index 7d88b14b68cc7..51306e6df2871 100644 --- a/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs @@ -114,7 +114,8 @@ void RegisterRefactoringWithResult(string text, string title) context.RegisterRefactoring(new MyCodeAction(title, c => { var generator = SyntaxGenerator.GetGenerator(document); - var updatedToken = generator.NumericLiteralToken(text + suffix, (ulong)value); + var updatedToken = generator.NumericLiteralToken(text + suffix, (ulong)value) + .WithTriviaFrom(numericToken); var updatedRoot = root.ReplaceToken(numericToken, updatedToken); return Task.FromResult(document.WithSyntaxRoot(updatedRoot)); })); From e846ff7a8c2322d4a760ba03805aeff8e46cf678 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Thu, 4 May 2017 15:15:15 -0700 Subject: [PATCH 030/284] stop exposing Workspace from WorkspaceAnalyzerOptions. exposing workspace directly cause people to depends on data/service analyzer is not supposed to depend on, also, it sometime access mutable data instead of snapshot of it which analyzer is not supposed to use. this cut the connection, and expose specific info analyzer is allowed to use in IDE case. --- .../Portable/Diagnostics/AnalyzerHelper.cs | 23 +++--------- ...cIncrementalAnalyzer.CompilationManager.cs | 2 +- .../Diagnostics/WorkspaceAnalyzerOptions.cs | 36 ++++++++++++++++--- ...oveUnnecessaryImportsDiagnosticAnalyzer.cs | 5 ++- .../Portable/Workspace/Solution/Document.cs | 13 +++++-- 5 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs b/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs index c5529b2a81153..131f5a12edec2 100644 --- a/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs +++ b/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs @@ -120,28 +120,15 @@ public static string GetAnalyzerAssemblyName(this DiagnosticAnalyzer analyzer) return typeInfo.Assembly.GetName().Name; } - public static async Task GetDocumentOptionSetAsync(this AnalyzerOptions analyzerOptions, SyntaxTree syntaxTree, CancellationToken cancellationToken) + public static Task GetDocumentOptionSetAsync(this AnalyzerOptions analyzerOptions, SyntaxTree syntaxTree, CancellationToken cancellationToken) { - var workspace = (analyzerOptions as WorkspaceAnalyzerOptions)?.Workspace; - if (workspace == null) + var workspaceAnalyzerOptions = analyzerOptions as WorkspaceAnalyzerOptions; + if (workspaceAnalyzerOptions == null) { - return null; - } - - var documentId = workspace.CurrentSolution.GetDocumentId(syntaxTree); - if (documentId == null) - { - return workspace.Options; - } - - var document = workspace.CurrentSolution.GetDocument(documentId); - if (document == null) - { - return workspace.Options; + return SpecializedTasks.Default(); } - var documentOptionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return documentOptionSet ?? workspace.Options; + return workspaceAnalyzerOptions.GetDocumentOptionSetAsync(syntaxTree, cancellationToken); } internal static void OnAnalyzerException_NoTelemetryLogging( diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 4e79297b92810..1f1c6b52a9ea4 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -118,7 +118,7 @@ private CompilationWithAnalyzersOptions GetAnalyzerOptions( // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with syncronous blocking concurrency. return new CompilationWithAnalyzersOptions( - options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Workspace), + options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Workspace, project.Solution.Options), onAnalyzerException: GetOnAnalyzerException(project.Id), analyzerExceptionFilter: GetAnalyzerExceptionFilter(project), concurrentAnalysis: false, diff --git a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs index 641271f47c3ac..a76b22ba2240a 100644 --- a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs +++ b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs @@ -1,5 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics @@ -10,13 +14,35 @@ namespace Microsoft.CodeAnalysis.Diagnostics ///
internal sealed class WorkspaceAnalyzerOptions : AnalyzerOptions { - public WorkspaceAnalyzerOptions(AnalyzerOptions options, Workspace workspace) + private readonly Workspace _workspace; + private readonly OptionSet _optionSet; + + public WorkspaceAnalyzerOptions(AnalyzerOptions options, Workspace workspace, OptionSet optionSet) : base(options.AdditionalFiles) { - this.Workspace = workspace; + _workspace = workspace; + _optionSet = optionSet; } - public Workspace Workspace { get; } + public HostWorkspaceServices Services => _workspace.Services; + + public async Task GetDocumentOptionSetAsync(SyntaxTree syntaxTree, CancellationToken cancellationToken) + { + var documentId = _workspace.CurrentSolution.GetDocumentId(syntaxTree); + if (documentId == null) + { + return _optionSet; + } + + var document = _workspace.CurrentSolution.GetDocument(documentId); + if (document == null) + { + return _optionSet; + } + + var documentOptionSet = await document.GetOptionsAsync(_optionSet, cancellationToken).ConfigureAwait(false); + return documentOptionSet ?? _optionSet; + } public override bool Equals(object obj) { @@ -27,13 +53,13 @@ public override bool Equals(object obj) var other = obj as WorkspaceAnalyzerOptions; return other != null && - this.Workspace == other.Workspace && + _workspace == other._workspace && base.Equals(other); } public override int GetHashCode() { - return Hash.Combine(this.Workspace, base.GetHashCode()); + return Hash.Combine(_workspace, base.GetHashCode()); } } } diff --git a/src/Features/Core/Portable/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs b/src/Features/Core/Portable/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs index ed102b125c67a..eb093db70a52d 100644 --- a/src/Features/Core/Portable/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs @@ -69,9 +69,8 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) return; } - var workspace = workspaceOptions.Workspace; - var service = workspace.Services.GetLanguageServices(context.SemanticModel.Compilation.Language) - .GetService(); + var service = workspaceOptions.Services.GetLanguageServices(context.SemanticModel.Compilation.Language) + .GetService(); var unnecessaryImports = service.GetUnnecessaryImports(context.SemanticModel, cancellationToken); if (unnecessaryImports.Any()) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index 332c36949261f..27acaaa8ebbe1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -464,13 +464,22 @@ private string GetDebuggerDisplay() /// public Task GetOptionsAsync(CancellationToken cancellationToken = default(CancellationToken)) { + return GetOptionsAsync(Project.Solution.Options, cancellationToken); + } + + internal Task GetOptionsAsync(OptionSet globalOptionSet, CancellationToken cancellationToken = default(CancellationToken)) + { + // TODO: we have this workaround since Solution.Options is not actually snapshot but just return Workspace.Options which violate snapshot model. + // this doesn't validate whether same optionset is given to invalidate the cache or not. this is not new since existing implementation + // also didn't check whether Workspace.Option is same as before or not. all wierd-ness come from the root cause of Solution.Options violating + // snapshot model. once that is fixed, we can remove this workaround if (_cachedOptions == null) { var newAsyncLazy = new AsyncLazy(async c => { var optionsService = Project.Solution.Workspace.Services.GetRequiredService(); - var optionSet = await optionsService.GetUpdatedOptionSetForDocumentAsync(this, Project.Solution.Options, c).ConfigureAwait(false); - return new DocumentOptionSet(optionSet, Project.Language); + var documentOptionSet = await optionsService.GetUpdatedOptionSetForDocumentAsync(this, globalOptionSet, c).ConfigureAwait(false); + return new DocumentOptionSet(documentOptionSet, Project.Language); }, cacheResult: true); Interlocked.CompareExchange(ref _cachedOptions, newAsyncLazy, comparand: null); From fb8b3793eb390982c9508fa142772756e67e7b03 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Thu, 4 May 2017 15:20:23 -0700 Subject: [PATCH 031/284] removed API in service hub that internally play with data (options) in workspace rather than solution snapshot. now, like other features, analyzer uses snapshot of options rather than mutable one from Workspace. eventually we should only use OptionSet snapshot embeded in solution rather than one in workspace. --- .../Services/SolutionServiceTests.cs | 77 ------------------- .../Core/Diagnostics/DiagnosticComputer.cs | 18 ++++- .../Remote/Core/Services/SolutionService.cs | 31 -------- .../CodeAnalysisService_Diagnostics.cs | 7 +- .../Shared/ServiceHubServiceBase.cs | 6 -- 5 files changed, 19 insertions(+), 120 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 69aebcfb19175..3624063c1e2c8 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -93,53 +93,6 @@ public async Task TestStrongNameProviderEmpty() Assert.Equal(((XmlFileResolver)compilationOptions.XmlReferenceResolver).BaseDirectory, null); } - [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] - public async Task TestCreationWithOption() - { - var code = @"class Test { void Method() { } }"; - - using (var workspace = TestWorkspace.CreateCSharp(code)) - { - var options = new TestOptionSet().WithChangedOption(RemoteHostOptions.RemoteHostTest, true); - - var solution = workspace.CurrentSolution; - var service = await GetSolutionServiceAsync(solution); - - var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - var synched = await service.GetSolutionAsync(solutionChecksum, options, CancellationToken.None); - - Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); - Assert.Empty(options.GetChangedOptions(synched.Workspace.Options)); - - Assert.True(options.GetOption(RemoteHostOptions.RemoteHostTest)); - Assert.True(synched.Workspace.Options.GetOption(RemoteHostOptions.RemoteHostTest)); - } - } - - [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] - public async Task TestOptionIsolation() - { - var code = @"class Test { void Method() { } }"; - - using (var workspace = TestWorkspace.CreateCSharp(code)) - { - var solution = workspace.CurrentSolution; - var service = await GetSolutionServiceAsync(solution); - - var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - - var options = new TestOptionSet().WithChangedOption(RemoteHostOptions.RemoteHostTest, true); - var first = await service.GetSolutionAsync(solutionChecksum, options, CancellationToken.None); - var second = await service.GetSolutionAsync(solutionChecksum, options.WithChangedOption(RemoteHostOptions.RemoteHostTest, false), CancellationToken.None); - - Assert.Equal(await first.State.GetChecksumAsync(CancellationToken.None), await second.State.GetChecksumAsync(CancellationToken.None)); - - // option change shouldn't affect other workspace - Assert.True(first.Workspace.Options.GetOption(RemoteHostOptions.RemoteHostTest)); - Assert.False(second.Workspace.Options.GetOption(RemoteHostOptions.RemoteHostTest)); - } - } - [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestCache() { @@ -161,36 +114,6 @@ public async Task TestCache() } } - [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] - public async Task TestCacheWithOption() - { - var code = @"class Test { void Method() { } }"; - - using (var workspace = TestWorkspace.CreateCSharp(code)) - { - var options = new TestOptionSet().WithChangedOption(RemoteHostOptions.RemoteHostTest, true); - - var solution = workspace.CurrentSolution; - var service = await GetSolutionServiceAsync(solution); - - var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - - var first = await service.GetSolutionAsync(solutionChecksum, options, CancellationToken.None); - var second = await service.GetSolutionAsync(solutionChecksum, options, CancellationToken.None); - - // new solutions if option is involved for isolation - Assert.False(object.ReferenceEquals(first, second)); - - // but semantic of both solution should be same - Assert.Equal(await first.State.GetChecksumAsync(CancellationToken.None), await second.State.GetChecksumAsync(CancellationToken.None)); - - // also any sub nodes such as projects should be same - Assert.True(object.ReferenceEquals(first.Projects.First().State, second.Projects.First().State)); - - Assert.True(first.Workspace is TemporaryWorkspace); - } - } - [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestUpdatePrimaryWorkspace() { diff --git a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs index 5fb4c61aa0af1..b2d89dc4522c8 100644 --- a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -27,6 +28,7 @@ public DiagnosticComputer(Project project) public async Task> GetDiagnosticsAsync( IEnumerable hostAnalyzers, + OptionSet options, IEnumerable analyzerIds, bool reportSuppressedDiagnostics, bool logAnalyzerExecutionTime, @@ -43,13 +45,14 @@ public async Task(); using (var cache = cacheService.EnableCaching(_project.Id)) { - return await AnalyzeAsync(analyzerMap, analyzers, reportSuppressedDiagnostics, logAnalyzerExecutionTime, cancellationToken).ConfigureAwait(false); + return await AnalyzeAsync(analyzerMap, analyzers, options, reportSuppressedDiagnostics, logAnalyzerExecutionTime, cancellationToken).ConfigureAwait(false); } } private async Task> AnalyzeAsync( BidirectionalMap analyzerMap, ImmutableArray analyzers, + OptionSet options, bool reportSuppressedDiagnostics, bool logAnalyzerExecutionTime, CancellationToken cancellationToken) @@ -68,7 +71,7 @@ private async Task CreateAnalyzerMap(IEnumerab return new BidirectionalMap( hostAnalyzers.Concat(project.AnalyzerReferences).SelectMany(r => r.GetAnalyzers(project.Language)).Select(a => KeyValuePair.Create(a.GetAnalyzerId(), a))); } + + private OptionSet MergeOptions(OptionSet workspaceOptions, OptionSet userOptions) + { + var newOptions = workspaceOptions; + foreach (var key in userOptions.GetChangedOptions(workspaceOptions)) + { + newOptions = newOptions.WithChangedOption(key, userOptions.GetOption(key)); + } + + return newOptions; + } } } diff --git a/src/Workspaces/Remote/Core/Services/SolutionService.cs b/src/Workspaces/Remote/Core/Services/SolutionService.cs index 06502f83c9095..e6a103347048b 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionService.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionService.cs @@ -53,26 +53,6 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, Cancella } } - public async Task GetSolutionAsync(Checksum solutionChecksum, OptionSet optionSet, CancellationToken cancellationToken) - { - // get solution - var baseSolution = await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); - if (optionSet == null) - { - return baseSolution; - } - - // since options belong to workspace, we can't share solution - // create temporary workspace - var tempWorkspace = new TemporaryWorkspace(baseSolution); - - // set merged options - tempWorkspace.Options = MergeOptions(tempWorkspace.Options, optionSet); - - // return new solution - return tempWorkspace.CurrentSolution; - } - public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { var currentSolution = s_primaryWorkspace.CurrentSolution; @@ -91,17 +71,6 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell } } - private OptionSet MergeOptions(OptionSet workspaceOptions, OptionSet userOptions) - { - var newOptions = workspaceOptions; - foreach (var key in userOptions.GetChangedOptions(workspaceOptions)) - { - newOptions = newOptions.WithChangedOption(key, userOptions.GetOption(key)); - } - - return newOptions; - } - private async Task CreateSolution_NoLockAsync(Checksum solutionChecksum, Solution baseSolution, CancellationToken cancellationToken) { var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs index aaa77f851e236..a2a5f03a88423 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs @@ -27,16 +27,15 @@ public async Task CalculateDiagnosticsAsync(DiagnosticArguments arguments, strin { try { - var optionSet = await RoslynServices.AssetService.GetAssetAsync(arguments.OptionSetChecksum, CancellationToken).ConfigureAwait(false); - // entry point for diagnostic service - var solution = await GetSolutionWithSpecificOptionsAsync(optionSet).ConfigureAwait(false); + var solution = await GetSolutionAsync().ConfigureAwait(false); + var optionSet = await RoslynServices.AssetService.GetAssetAsync(arguments.OptionSetChecksum, CancellationToken).ConfigureAwait(false); var projectId = arguments.ProjectId; var analyzers = RoslynServices.AssetService.GetGlobalAssetsOfType(CancellationToken); var result = await (new DiagnosticComputer(solution.GetProject(projectId))).GetDiagnosticsAsync( - analyzers, arguments.AnalyzerIds, arguments.ReportSuppressedDiagnostics, arguments.LogAnalyzerExecutionTime, CancellationToken).ConfigureAwait(false); + analyzers, optionSet, arguments.AnalyzerIds, arguments.ReportSuppressedDiagnostics, arguments.LogAnalyzerExecutionTime, CancellationToken).ConfigureAwait(false); await SerializeDiagnosticResultAsync(streamName, result).ConfigureAwait(false); } diff --git a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs index 36336263409c7..84549a8cfd7d1 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs @@ -93,12 +93,6 @@ protected Task GetSolutionAsync() return RoslynServices.SolutionService.GetSolutionAsync(_solutionChecksumOpt, CancellationToken); } - protected Task GetSolutionWithSpecificOptionsAsync(OptionSet options) - { - Contract.ThrowIfNull(_solutionChecksumOpt); - return RoslynServices.SolutionService.GetSolutionAsync(_solutionChecksumOpt, options, CancellationToken); - } - protected virtual void Dispose(bool disposing) { // do nothing here From dcd8fcbc3d2c819ff4f8b4c1aa758efd0ce3359b Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 4 May 2017 14:05:17 -0700 Subject: [PATCH 032/284] IObjectCreationExpression API Change There are couple of changes here: 1. API change: `ImmutableArray MemberInitializers` is changed to `ImmutableArray Initializers`. 2. Implementation changes: 1. Instead of returning the member initializers as synthesized ISymbolInitializer nodes, we now return member intializers as IAssignmentExpression nodes. This ensures completeness of IOperation tree. 2. Now we also return the collection intializer expressions within an object creation expression. Fixes #18115 There are 2 bugs still affecting this area: 1. #18781: IOperation API shape for collection initializer expressions 2. #19276: Missing Field/Property reference expression nodes in object creation initializer node --- .../CSharp/Portable/BoundTree/Expression.cs | 38 +-- .../CSharpCompilerSemanticTest.csproj | 1 + ...perationTests_IObjectCreationExpression.cs | 254 +++++++++++++++++ ...tionTests_IParameterReferenceExpression.cs | 44 +-- .../IOperationTests_ISymbolInitializer.cs | 108 ------- .../Generated/Operations.xml.Generated.cs | 6 +- .../Operations/IObjectCreationExpression.cs | 4 +- .../Portable/Operations/OperationWalker.cs | 2 +- .../Portable/BoundTree/Expression.vb | 30 +- .../Semantic/BasicCompilerSemanticTest.vbproj | 1 + ...perationTests_IObjectCreationExpression.vb | 265 ++++++++++++++++++ ...tionTests_IParameterReferenceExpression.vb | 53 ++-- .../IOperationTests_ISymbolInitializer.vb | 94 ------- .../Compilation/OperationTreeVerifier.cs | 2 +- 14 files changed, 590 insertions(+), 312 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs create mode 100644 src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.vb diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 605ada05830fa..4329f117fca05 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -515,43 +515,7 @@ internal partial class BoundObjectCreationExpression : IObjectCreationExpression ImmutableArray IHasArgumentsExpression.ArgumentsInEvaluationOrder => BoundCall.DeriveArguments(this.Arguments, this.ArgumentNamesOpt, this.ArgsToParamsOpt, this.ArgumentRefKindsOpt, this.Constructor.Parameters, this.Syntax); - ImmutableArray IObjectCreationExpression.MemberInitializers - { - get - { - return (ImmutableArray)s_memberInitializersMappings.GetValue(this, - objectCreationExpression => - { - var objectInitializerExpression = this.InitializerExpressionOpt as BoundObjectInitializerExpression; - if (objectInitializerExpression != null) - { - var builder = ArrayBuilder.GetInstance(objectInitializerExpression.Initializers.Length); - foreach (var memberAssignment in objectInitializerExpression.Initializers) - { - var assignment = memberAssignment as BoundAssignmentOperator; - var leftSymbol = (assignment?.Left as BoundObjectInitializerMember)?.MemberSymbol; - - if ((object)leftSymbol == null) - { - continue; - } - - switch (leftSymbol.Kind) - { - case SymbolKind.Field: - builder.Add(new FieldInitializer(assignment.Syntax, (IFieldSymbol)leftSymbol, assignment.Right)); - break; - case SymbolKind.Property: - builder.Add(new PropertyInitializer(assignment.Syntax, (IPropertySymbol)leftSymbol, assignment.Right)); - break; - } - } - return builder.ToImmutableAndFree(); - } - return ImmutableArray.Empty; - }); - } - } + ImmutableArray IObjectCreationExpression.Initializers => GetChildInitializers(this.InitializerExpressionOpt).As(); internal static ImmutableArray GetChildInitializers(BoundExpression objectOrCollectionInitializer) { diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 9858091851294..4c1aefc953288 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -63,6 +63,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs new file mode 100644 index 0000000000000..2a5b571057c7d --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.cs @@ -0,0 +1,254 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public partial class IOperationTests : SemanticModelTestBase + { + [Fact, WorkItem(17588, "https://github.com/dotnet/roslyn/issues/17588")] + public void ObjectCreationWithMemberInitializers() + { + string source = @" +struct B +{ + public bool Field; +} + +class F +{ + public int Field; + public string Property1 { set; get; } + public B Property2 { set; get; } +} + +class C +{ + public void M1() + /**/{ + var x1 = new F(); + var x2 = new F() { Field = 2 }; + var x3 = new F() { Property1 = """" }; + var x4 = new F() { Property1 = """", Field = 2 }; + var x5 = new F() { Property2 = new B { Field = true } }; + + var e1 = new F() { Property2 = 1 }; + var e2 = new F() { """" }; + }/**/ +} +"; + string expectedOperationTree = @" +IBlockStatement (7 statements, 7 locals) (OperationKind.BlockStatement, IsInvalid) (Syntax: '{ ... }') + Locals: Local_1: F x1 + Local_2: F x2 + Local_3: F x3 + Local_4: F x4 + Local_5: F x5 + Local_6: F e1 + Local_7: F e2 + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x1 = new F();') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x1 = new F();') + Variables: Local_1: F x1 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F()') + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x2 = ne ... ield = 2 };') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x2 = ne ... ield = 2 };') + Variables: Local_1: F x2 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { Field = 2 }') + Initializers(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'Field = 2') + Left: IOperation: (OperationKind.None) (Syntax: 'Field') + Right: ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x3 = ne ... ty1 = """" };') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x3 = ne ... ty1 = """" };') + Variables: Local_1: F x3 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { P ... rty1 = """" }') + Initializers(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.String) (Syntax: 'Property1 = """"') + Left: IOperation: (OperationKind.None) (Syntax: 'Property1') + Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""') + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x4 = ne ... ield = 2 };') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x4 = ne ... ield = 2 };') + Variables: Local_1: F x4 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { P ... Field = 2 }') + Initializers(2): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.String) (Syntax: 'Property1 = """"') + Left: IOperation: (OperationKind.None) (Syntax: 'Property1') + Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'Field = 2') + Left: IOperation: (OperationKind.None) (Syntax: 'Field') + Right: ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x5 = ne ... = true } };') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x5 = ne ... = true } };') + Variables: Local_1: F x5 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { P ... = true } }') + Initializers(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: B) (Syntax: 'Property2 = ... ld = true }') + Left: IOperation: (OperationKind.None) (Syntax: 'Property2') + Right: IObjectCreationExpression (Constructor: B..ctor()) (OperationKind.ObjectCreationExpression, Type: B) (Syntax: 'new B { Field = true }') + Initializers(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Boolean) (Syntax: 'Field = true') + Left: IOperation: (OperationKind.None) (Syntax: 'Field') + Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Boolean, Constant: True) (Syntax: 'true') + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var e1 = ne ... rty2 = 1 };') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var e1 = ne ... rty2 = 1 };') + Variables: Local_1: F e1 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... erty2 = 1 }') + Initializers(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: B, IsInvalid) (Syntax: 'Property2 = 1') + Left: IOperation: (OperationKind.None) (Syntax: 'Property2') + Right: IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: B, IsInvalid) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var e2 = new F() { """" };') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var e2 = new F() { """" };') + Variables: Local_1: F e2 + Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { """" }') + Initializers(1): IInvalidExpression (OperationKind.InvalidExpression, Type: ?, IsInvalid) (Syntax: '""""') + Children(1): ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0029: Cannot implicitly convert type 'int' to 'B' + // var e1 = new F() { Property2 = 1 }; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "B").WithLocation(24, 40), + // CS1922: Cannot initialize type 'F' with a collection initializer because it does not implement 'System.Collections.IEnumerable' + // var e2 = new F() { "" }; + Diagnostic(ErrorCode.ERR_CollectionInitRequiresIEnumerable, @"{ """" }").WithArguments("F").WithLocation(25, 26) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(17588, "https://github.com/dotnet/roslyn/issues/17588")] + public void ObjectCreationWithCollectionInitializer() + { + string source = @" +using System.Collections.Generic; + +class C +{ + private readonly int field; + public void M1(int x) + { + int y = 0; + var x1 = /**/new List { x, y, field }/**/; + } +} +"; + string expectedOperationTree = @" +IObjectCreationExpression (Constructor: System.Collections.Generic.List..ctor()) (OperationKind.ObjectCreationExpression, Type: System.Collections.Generic.List) (Syntax: 'new List(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(17588, "https://github.com/dotnet/roslyn/issues/17588")] + public void ObjectCreationWithNestedCollectionInitializer() + { + string source = @" +using System.Collections.Generic; +using System.Linq; + +class C +{ + private readonly int field = 0; + public void M1(int x) + { + int y = 0; + var x1 = /**/new List> { + new[] { x, y }.ToList(), + new List { field } + }/**/; + } +} +"; + string expectedOperationTree = @" +IObjectCreationExpression (Constructor: System.Collections.Generic.List>..ctor()) (OperationKind.ObjectCreationExpression, Type: System.Collections.Generic.List>) (Syntax: 'new List
  • System.Linq.Enumerable.ToList(this System.Collections.Generic.IEnumerable source)) (OperationKind.InvocationExpression, Type: System.Collections.Generic.List) (Syntax: 'new[] { x, y }.ToList()') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: source) (OperationKind.Argument) (Syntax: 'new[] { x, y }') + IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: 'new[] { x, y }') + IArrayCreationExpression (Element Type: System.Int32) (OperationKind.ArrayCreationExpression, Type: System.Int32[]) (Syntax: 'new[] { x, y }') + Dimension Sizes(1): ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: 'new[] { x, y }') + Initializer: IArrayInitializer (2 elements) (OperationKind.ArrayInitializer) (Syntax: '{ x, y }') + Element Values(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') + IOperation: (OperationKind.None) (Syntax: 'new List { field }') + Children(1): IObjectCreationExpression (Constructor: System.Collections.Generic.List..ctor()) (OperationKind.ObjectCreationExpression, Type: System.Collections.Generic.List) (Syntax: 'new List { field }') + Initializers(1): IOperation: (OperationKind.None) (Syntax: 'field') + Children(1): IFieldReferenceExpression: System.Int32 C.field (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'field') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: C) (Syntax: 'field') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(17588, "https://github.com/dotnet/roslyn/issues/17588")] + public void ObjectCreationWithMemberAndCollectionInitializers() + { + string source = @" +using System.Collections.Generic; + +internal class Class +{ + public int X { get; set; } + public List Y { get; set; } + public Dictionary Z { get; set; } + public Class C { get; set; } + + private readonly int field = 0; + + public void M(int x) + { + int y = 0; + var c = /**/new Class() { + X = x, + Y = { x, y, 3 }, + Z = { { x, y } }, + C = { X = field } + }/**/; + } +} +"; + string expectedOperationTree = @" +IObjectCreationExpression (Constructor: Class..ctor()) (OperationKind.ObjectCreationExpression, Type: Class) (Syntax: 'new Class() ... }') + Initializers(4): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = x') + Left: IOperation: (OperationKind.None) (Syntax: 'X') + Right: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Collections.Generic.List) (Syntax: 'Y = { x, y, 3 }') + Left: IOperation: (OperationKind.None) (Syntax: 'Y') + Right: IOperation: (OperationKind.None) (Syntax: '{ x, y, 3 }') + Children(3): IOperation: (OperationKind.None) (Syntax: 'x') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'y') + Children(1): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') + IOperation: (OperationKind.None) (Syntax: '3') + Children(1): ILiteralExpression (Text: 3) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 3) (Syntax: '3') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Collections.Generic.Dictionary) (Syntax: 'Z = { { x, y } }') + Left: IOperation: (OperationKind.None) (Syntax: 'Z') + Right: IOperation: (OperationKind.None) (Syntax: '{ { x, y } }') + Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: Class) (Syntax: 'C = { X = field }') + Left: IOperation: (OperationKind.None) (Syntax: 'C') + Right: IOperation: (OperationKind.None) (Syntax: '{ X = field }') + Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = field') + Left: IOperation: (OperationKind.None) (Syntax: 'X') + Right: IFieldReferenceExpression: System.Int32 Class.field (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'field') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: Class) (Syntax: 'field') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs index 509772a22f199..9f62943ac058a 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -165,26 +165,30 @@ public void M(int x, int y, int z) "; string expectedOperationTree = @" IObjectCreationExpression (Constructor: Class..ctor()) (OperationKind.ObjectCreationExpression, Type: Class) (Syntax: 'new Class() ... { X = z } }') - Member Initializers(4): IPropertyInitializer (Property: System.Int32 Class.X { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'X = x') - IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') - IPropertyInitializer (Property: System.Collections.Generic.List Class.Y { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Y = { x, y, 3 }') - IOperation: (OperationKind.None) (Syntax: '{ x, y, 3 }') - Children(3): IOperation: (OperationKind.None) (Syntax: 'x') - Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') - IOperation: (OperationKind.None) (Syntax: 'y') - Children(1): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') - IOperation: (OperationKind.None) (Syntax: '3') - Children(1): ILiteralExpression (Text: 3) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 3) (Syntax: '3') - IPropertyInitializer (Property: System.Collections.Generic.Dictionary Class.Z { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Z = { { x, y } }') - IOperation: (OperationKind.None) (Syntax: '{ { x, y } }') - Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }') - Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') - IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') - IPropertyInitializer (Property: Class Class.C { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'C = { X = z }') - IOperation: (OperationKind.None) (Syntax: '{ X = z }') - Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = z') - Left: IOperation: (OperationKind.None) (Syntax: 'X') - Right: IParameterReferenceExpression: z (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'z') + Initializers(4): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = x') + Left: IOperation: (OperationKind.None) (Syntax: 'X') + Right: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Collections.Generic.List) (Syntax: 'Y = { x, y, 3 }') + Left: IOperation: (OperationKind.None) (Syntax: 'Y') + Right: IOperation: (OperationKind.None) (Syntax: '{ x, y, 3 }') + Children(3): IOperation: (OperationKind.None) (Syntax: 'x') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'y') + Children(1): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + IOperation: (OperationKind.None) (Syntax: '3') + Children(1): ILiteralExpression (Text: 3) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 3) (Syntax: '3') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Collections.Generic.Dictionary) (Syntax: 'Z = { { x, y } }') + Left: IOperation: (OperationKind.None) (Syntax: 'Z') + Right: IOperation: (OperationKind.None) (Syntax: '{ { x, y } }') + Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + IAssignmentExpression (OperationKind.AssignmentExpression, Type: Class) (Syntax: 'C = { X = z }') + Left: IOperation: (OperationKind.None) (Syntax: 'C') + Right: IOperation: (OperationKind.None) (Syntax: '{ X = z }') + Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = z') + Left: IOperation: (OperationKind.None) (Syntax: 'X') + Right: IParameterReferenceExpression: z (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'z') "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs index dbc5ed2c4157d..7dc638e30f762 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs @@ -332,113 +332,5 @@ class C VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - - [Fact, WorkItem(17595, "https://github.com/dotnet/roslyn/issues/17595")] - public void MemberInitializerCSharp() - { - string source = @" -struct B -{ - public bool Field; -} - -class F -{ - public int Field; - public string Property1 { set; get; } - public B Property2 { set; get; } -} - -class C -{ - public void M1() - /**/{ - var x1 = new F(); - var x2 = new F() { Field = 2 }; - var x3 = new F() { Property1 = """""""" }; - var x4 = new F() { Property1 = """""""", Field = 2 }; - var x5 = new F() { Property2 = new B { Field = true } }; - - var e1 = new F() { Property2 = 1 }; - var e2 = new F() { """""""" }; - }/**/ -} -"; - string expectedOperationTree = @" -IBlockStatement (7 statements, 7 locals) (OperationKind.BlockStatement, IsInvalid) (Syntax: '{ ... }') - Locals: Local_1: F x1 - Local_2: F x2 - Local_3: F x3 - Local_4: F x4 - Local_5: F x5 - Local_6: F e1 - Local_7: F e2 - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x1 = new F();') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x1 = new F();') - Variables: Local_1: F x1 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F()') - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x2 = ne ... ield = 2 };') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x2 = ne ... ield = 2 };') - Variables: Local_1: F x2 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { Field = 2 }') - Member Initializers(1): IFieldInitializer (Field: System.Int32 F.Field) (OperationKind.FieldInitializerInCreation) (Syntax: 'Field = 2') - ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var x3 = ne ... 1 = """""""" };') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var x3 = ne ... 1 = """""""" };') - Variables: Local_1: F x3 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... y1 = """""""" }') - Member Initializers(1): IPropertyInitializer (Property: System.String F.Property1 { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Property1 = """"') - ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""') - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var x4 = ne ... ield = 2 };') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var x4 = ne ... ield = 2 };') - Variables: Local_1: F x4 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... Field = 2 }') - Member Initializers(2): IPropertyInitializer (Property: System.String F.Property1 { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Property1 = """"') - ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""') - IFieldInitializer (Field: System.Int32 F.Field) (OperationKind.FieldInitializerInCreation) (Syntax: 'Field = 2') - ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x5 = ne ... = true } };') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x5 = ne ... = true } };') - Variables: Local_1: F x5 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { P ... = true } }') - Member Initializers(1): IPropertyInitializer (Property: B F.Property2 { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Property2 = ... ld = true }') - IObjectCreationExpression (Constructor: B..ctor()) (OperationKind.ObjectCreationExpression, Type: B) (Syntax: 'new B { Field = true }') - Member Initializers(1): IFieldInitializer (Field: System.Boolean B.Field) (OperationKind.FieldInitializerInCreation) (Syntax: 'Field = true') - ILiteralExpression (OperationKind.LiteralExpression, Type: System.Boolean, Constant: True) (Syntax: 'true') - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var e1 = ne ... rty2 = 1 };') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var e1 = ne ... rty2 = 1 };') - Variables: Local_1: F e1 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... erty2 = 1 }') - Member Initializers(1): IPropertyInitializer (Property: B F.Property2 { get; set; }) (OperationKind.PropertyInitializerInCreation, IsInvalid) (Syntax: 'Property2 = 1') - IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: B, IsInvalid) (Syntax: '1') - ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') - IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var e2 = ne ... ) { """""""" };') - IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var e2 = ne ... ) { """""""" };') - Variables: Local_1: F e2 - Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { """""""" }') -"; - var expectedDiagnostics = new DiagnosticDescription[] { - // CS1003: Syntax error, ',' expected - // var x3 = new F() { Property1 = """" }; - Diagnostic(ErrorCode.ERR_SyntaxError, @"""""").WithArguments(",", "").WithLocation(20, 42), - // CS1003: Syntax error, ',' expected - // var x4 = new F() { Property1 = """", Field = 2 }; - Diagnostic(ErrorCode.ERR_SyntaxError, @"""""").WithArguments(",", "").WithLocation(21, 42), - // CS1003: Syntax error, ',' expected - // var e2 = new F() { """" }; - Diagnostic(ErrorCode.ERR_SyntaxError, @"""""").WithArguments(",", "").WithLocation(25, 30), - // CS0747: Invalid initializer member declarator - // var x3 = new F() { Property1 = """" }; - Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, @"""""").WithLocation(20, 42), - // CS0747: Invalid initializer member declarator - // var x4 = new F() { Property1 = """", Field = 2 }; - Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, @"""""").WithLocation(21, 42), - // CS0029: Cannot implicitly convert type 'int' to 'B' - // var e1 = new F() { Property2 = 1 }; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "B").WithLocation(24, 40) - }; - - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); - } } } \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index 93bd76bd27283..1188abb551c47 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -1522,11 +1522,11 @@ public override TResult Accept(OperationVisitor internal sealed partial class ObjectCreationExpression : Operation, IHasArgumentsExpression, IObjectCreationExpression { - public ObjectCreationExpression(IMethodSymbol constructor, ImmutableArray memberInitializers, bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : + public ObjectCreationExpression(IMethodSymbol constructor, ImmutableArray initializers, bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : base(OperationKind.ObjectCreationExpression, isInvalid, syntax, type, constantValue) { Constructor = constructor; - MemberInitializers = memberInitializers; + Initializers = initializers; } /// /// Constructor to be invoked on the created instance. @@ -1535,7 +1535,7 @@ public ObjectCreationExpression(IMethodSymbol constructor, ImmutableArray /// Explicitly-specified member initializers. /// - public ImmutableArray MemberInitializers { get; } + public ImmutableArray Initializers { get; } public override void Accept(OperationVisitor visitor) { visitor.VisitObjectCreationExpression(this); diff --git a/src/Compilers/Core/Portable/Operations/IObjectCreationExpression.cs b/src/Compilers/Core/Portable/Operations/IObjectCreationExpression.cs index 9a06dedb54c60..dfe2997af5ae3 100644 --- a/src/Compilers/Core/Portable/Operations/IObjectCreationExpression.cs +++ b/src/Compilers/Core/Portable/Operations/IObjectCreationExpression.cs @@ -18,9 +18,9 @@ public interface IObjectCreationExpression : IHasArgumentsExpression /// IMethodSymbol Constructor { get; } /// - /// Explicitly-specified member initializers. + /// List of member or collection initializer expressions in the object initializer, if any. /// - ImmutableArray MemberInitializers { get; } + ImmutableArray Initializers { get; } } } diff --git a/src/Compilers/Core/Portable/Operations/OperationWalker.cs b/src/Compilers/Core/Portable/Operations/OperationWalker.cs index aeed5ce40c37d..f9798628922b0 100644 --- a/src/Compilers/Core/Portable/Operations/OperationWalker.cs +++ b/src/Compilers/Core/Portable/Operations/OperationWalker.cs @@ -345,7 +345,7 @@ public override void VisitAddressOfExpression(IAddressOfExpression operation) public override void VisitObjectCreationExpression(IObjectCreationExpression operation) { VisitArray(operation.ArgumentsInEvaluationOrder); - VisitArray(operation.MemberInitializers); + VisitArray(operation.Initializers); } public override void VisitFieldInitializer(IFieldInitializer operation) diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb index 029f3a6f4c820..73c08fcfe98c0 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb @@ -1155,8 +1155,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend Partial Class BoundObjectCreationExpression Implements IObjectCreationExpression - Private Shared ReadOnly s_memberInitializersMappings As New System.Runtime.CompilerServices.ConditionalWeakTable(Of BoundObjectCreationExpression, Object) - Private ReadOnly Property IObjectCreationExpression_Constructor As IMethodSymbol Implements IObjectCreationExpression.Constructor Get Return Me.ConstructorOpt @@ -1170,31 +1168,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Private ReadOnly Property IObjectCreationExpression_MemberInitializers As ImmutableArray(Of ISymbolInitializer) Implements IObjectCreationExpression.MemberInitializers - Get - Dim initializer = s_memberInitializersMappings.GetValue(Me, Function(objectCreationStatement) - Dim objectInitializerExpression As BoundObjectInitializerExpressionBase = Me.InitializerOpt - If objectInitializerExpression IsNot Nothing Then - Dim builder = ArrayBuilder(Of ISymbolInitializer).GetInstance(objectInitializerExpression.Initializers.Length) - For Each memberAssignment In objectInitializerExpression.Initializers - Dim assignment = TryCast(memberAssignment, BoundAssignmentOperator) - Dim left = assignment?.Left - If left IsNot Nothing Then - Select Case left.Kind - Case BoundKind.FieldAccess - builder.Add(New FieldInitializer(assignment.Syntax, DirectCast(left, BoundFieldAccess).FieldSymbol, assignment.Right)) - Case BoundKind.PropertyAccess - builder.Add(New PropertyInitializer(assignment.Syntax, DirectCast(left, BoundPropertyAccess).PropertySymbol, assignment.Right)) - End Select - End If - Next - Return builder.ToImmutableAndFree() - End If - - Return ImmutableArray(Of ISymbolInitializer).Empty - End Function) - - Return DirectCast(initializer, ImmutableArray(Of ISymbolInitializer)) + Private ReadOnly Property IObjectCreationExpression_Initializers As ImmutableArray(Of IOperation) Implements IObjectCreationExpression.Initializers + Get + Return If(Me.InitializerOpt IsNot Nothing, Me.InitializerOpt.Initializers.As(Of IOperation), ImmutableArray(Of IOperation).Empty) End Get End Property diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index c12ac6254f277..019f9ff3a9530 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -108,6 +108,7 @@ + diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.vb new file mode 100644 index 0000000000000..232e15217842b --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IObjectCreationExpression.vb @@ -0,0 +1,265 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + Public Sub ObjectCreationWithMemberInitializers() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ObjectCreationWithCollectionInitializer() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ObjectCreationWithNestedCollectionInitializer() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ObjectCreationWithMemberAndCollectionInitializers() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb index 65581f7e4b429..e3af89888feb6 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb @@ -226,7 +226,7 @@ IOperation: (OperationKind.None) (Syntax: 'From y In x ... nto Count()') VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub - + Public Sub ParameterReference_ObjectAndCollectionInitializer() Dim source = .Value +End Class]]>.Value Dim expectedOperationTree = .Value Dim expectedDiagnostics = String.Empty diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb index 974d686a259cc..42dcdd99240a6 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb @@ -269,99 +269,5 @@ IFieldInitializer (Field: C.i2 As System.Int32) (OperationKind.FieldInitializerA VerifyOperationTreeAndDiagnosticsForTest(Of EqualsValueSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub - - - Public Sub MemberInitializer() - Dim source = .Value - - Dim expectedOperationTree = .Value - - Dim expectedDiagnostics = .Value - - VerifyOperationTreeAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) - End Sub End Class End Namespace diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 95209b461d02a..4a0e6df980f3c 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -879,7 +879,7 @@ public override void VisitObjectCreationExpression(IObjectCreationExpression ope LogCommonPropertiesAndNewLine(operation); VisitArguments(operation); - VisitArray(operation.MemberInitializers, "Member Initializers", logElementCount: true); + VisitArray(operation.Initializers, "Initializers", logElementCount: true); } public override void VisitFieldInitializer(IFieldInitializer operation) From c0c83bb57608333cb2a0bec23df835df1684c628 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Thu, 4 May 2017 15:23:30 -0700 Subject: [PATCH 033/284] reordered parameters --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 2 +- .../Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs | 2 +- src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 1f1c6b52a9ea4..1d1e41a0ad5f0 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -118,7 +118,7 @@ private CompilationWithAnalyzersOptions GetAnalyzerOptions( // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with syncronous blocking concurrency. return new CompilationWithAnalyzersOptions( - options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Workspace, project.Solution.Options), + options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Options, project.Solution.Workspace), onAnalyzerException: GetOnAnalyzerException(project.Id), analyzerExceptionFilter: GetAnalyzerExceptionFilter(project), concurrentAnalysis: false, diff --git a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs index a76b22ba2240a..6bcc2c4d4e32e 100644 --- a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs +++ b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs @@ -17,7 +17,7 @@ internal sealed class WorkspaceAnalyzerOptions : AnalyzerOptions private readonly Workspace _workspace; private readonly OptionSet _optionSet; - public WorkspaceAnalyzerOptions(AnalyzerOptions options, Workspace workspace, OptionSet optionSet) + public WorkspaceAnalyzerOptions(AnalyzerOptions options, OptionSet optionSet, Workspace workspace) : base(options.AdditionalFiles) { _workspace = workspace; diff --git a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs index b2d89dc4522c8..a9bb623709a98 100644 --- a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs @@ -71,7 +71,7 @@ private async Task Date: Thu, 4 May 2017 15:30:33 -0700 Subject: [PATCH 034/284] removed default value for cancellation token --- src/Workspaces/Core/Portable/Workspace/Solution/Document.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index 27acaaa8ebbe1..9536fd9e46507 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -467,7 +467,7 @@ private string GetDebuggerDisplay() return GetOptionsAsync(Project.Solution.Options, cancellationToken); } - internal Task GetOptionsAsync(OptionSet globalOptionSet, CancellationToken cancellationToken = default(CancellationToken)) + internal Task GetOptionsAsync(OptionSet globalOptionSet, CancellationToken cancellationToken) { // TODO: we have this workaround since Solution.Options is not actually snapshot but just return Workspace.Options which violate snapshot model. // this doesn't validate whether same optionset is given to invalidate the cache or not. this is not new since existing implementation From 6b832a5b245d701d40b09a84b95df3335b70bc2b Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Thu, 4 May 2017 16:22:44 -0700 Subject: [PATCH 035/284] fix build break. --- src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs b/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs index 26704647f2c31..6f7023739c35d 100644 --- a/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs +++ b/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs @@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.Options { /// - /// An that comes from . It behaves just like a normal + /// An that comes from . It behaves just like a normal /// but remembers which language the is, so you don't have to /// pass that information redundantly when calling . /// From c08f63b2704cd44cc58db0f7798d4b911a89c773 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 22:08:03 -0700 Subject: [PATCH 036/284] Move GenerateVariable types. --- .../GenerateVariable/GenerateVariableTests.cs | 5 ++--- .../AbstractCrossLanguageUserDiagnosticTest.vb | 2 +- .../AddImport/AddImportCrossLanguageTests.vb | 4 ++-- .../AddMissingReferenceTests.vb | 4 ++-- .../Diagnostics/AdditionalFileDiagnosticsTests.vb | 4 ++-- .../GenerateEventCrossLanguageTests.vb | 6 ++---- .../GenerateConstructorCrossLanguageTests.vb | 10 +++------- .../GenerateMethodCrossLanguageTests.vb | 10 +++------- .../GenerateVariableCrossLanguageTests.vb | 14 ++++++-------- .../ImplementInterfaceCrossLanguageTests.vb | 6 ++---- .../UseAutoProperty/UseAutoPropertyTests.vb | 8 ++++---- .../GenerateVariable/GenerateVariableTests.vb | 4 ++-- src/Features/CSharp/Portable/CSharpFeatures.csproj | 2 +- .../CSharpGenerateVariableCodeFixProvider.cs} | 4 ++-- .../VisualBasic/Portable/BasicFeatures.vbproj | 2 +- .../VisualBasicGenerateVariableCodeFixProvider.vb} | 4 ++-- 16 files changed, 37 insertions(+), 52 deletions(-) rename src/Features/CSharp/Portable/{CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.cs => GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs} (94%) rename src/Features/VisualBasic/Portable/{CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.vb => GenerateVariable/VisualBasicGenerateVariableCodeFixProvider.vb} (95%) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs index 9172c5add7590..37ebfc1709bb0 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs @@ -6,9 +6,8 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateVariable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.GenerateVariable; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Roslyn.Test.Utilities; @@ -19,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.GenerateVar public class GenerateVariableTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (null, new GenerateVariableCodeFixProvider()); + => (null, new CSharpGenerateVariableCodeFixProvider()); private readonly CodeStyleOption onWithInfo = new CodeStyleOption(true, NotificationOption.Suggestion); diff --git a/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb b/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb index 04fa4f9d90dbd..4ede648ace9cf 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics Partial Public MustInherit Class AbstractCrossLanguageUserDiagnosticTest Protected Const DestinationDocument = "DestinationDocument" - Friend MustOverride Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend MustOverride Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Protected Async Function TestMissing(definition As XElement) As Task Using workspace = TestWorkspace.Create(definition) diff --git a/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb index 2d6ead9f179e6..e490194bcec32 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb @@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddImport Public Class AddImportCrossLanguageTests Inherits AbstractCrossLanguageUserDiagnosticTest - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Dim fixer As CodeFixProvider If language = LanguageNames.CSharp Then fixer = New CSharpAddImportCodeFixProvider() @@ -25,7 +25,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddImport fixer = New VisualBasicAddImportCodeFixProvider() End If - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)(Nothing, fixer) + Return (Nothing, fixer) End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb b/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb index c5359645f58bb..a5b278a589829 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb @@ -23,13 +23,13 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddMissingReferenc s_systemXamlAssembly = Assembly.Load("System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") End Sub - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Dim fixer As CodeFixProvider = CType(If(language = LanguageNames.CSharp, DirectCast(New CSharpAddMissingReferenceCodeFixProvider(), CodeFixProvider), DirectCast(New VisualBasicAddMissingReferenceCodeFixProvider(), CodeFixProvider)), CodeFixProvider) - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)(Nothing, fixer) + Return (Nothing, fixer) End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb b/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb index e7049ffb84628..36694763e8fbd 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb @@ -14,8 +14,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AdditionalFiles Public Class AdditionalFileDiagnosticsTests Inherits AbstractCrossLanguageUserDiagnosticTest - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)(New AdditionalFileAnalyzer(), New AdditionalFileFixer()) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) + Return (New AdditionalFileAnalyzer(), New AdditionalFileFixer()) End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb index a0e2d16a36ec9..91d8cc7c2c881 100644 --- a/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb @@ -8,10 +8,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateEvent Public Class GenerateEventCrossLanguageTests Inherits AbstractCrossLanguageUserDiagnosticTest - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider()) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) + Return (Nothing, New Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider()) End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb index 7604dc58d3e94..c6e0a1afb6cc9 100644 --- a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb @@ -7,15 +7,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateConstructo Partial Public Class GenerateConstructorCrossLanguageTests Inherits AbstractCrossLanguageUserDiagnosticTest - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New CodeAnalysis.CSharp.GenerateConstructor.GenerateConstructorCodeFixProvider()) + Return (Nothing, New CodeAnalysis.CSharp.GenerateConstructor.GenerateConstructorCodeFixProvider()) Else - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New CodeAnalysis.VisualBasic.GenerateConstructor.GenerateConstructorCodeFixProvider()) + Return (Nothing, New CodeAnalysis.VisualBasic.GenerateConstructor.GenerateConstructorCodeFixProvider()) End If End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb index 46bfaf924f1f4..65b36b383a6b1 100644 --- a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb @@ -15,15 +15,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateMethod _outputHelper = outputHelper End Sub - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New CodeAnalysis.CSharp.CodeFixes.GenerateMethod.GenerateMethodCodeFixProvider()) + Return (Nothing, New CodeAnalysis.CSharp.CodeFixes.GenerateMethod.GenerateMethodCodeFixProvider()) Else - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod.GenerateParameterizedMemberCodeFixProvider()) + Return (Nothing, New CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod.GenerateParameterizedMemberCodeFixProvider()) End If End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb index fcdb9bd24b917..3779941678203 100644 --- a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb @@ -3,7 +3,9 @@ Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CSharp.GenerateVariable Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.GenerateVariable Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable @@ -16,15 +18,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable _outputHelper = outputHelper End Sub - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateVariable.GenerateVariableCodeFixProvider()) + Return (Nothing, New CSharpGenerateVariableCodeFixProvider()) Else - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateVariable.GenerateVariableCodeFixProvider()) + Return (Nothing, New VisualBasicGenerateVariableCodeFixProvider()) End If End Function @@ -67,4 +65,4 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb index 3caf64f61512a..c09f3212a5f5a 100644 --- a/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb @@ -10,13 +10,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.ImplementInterface Public Class ImplementInterfaceCrossLanguageTests Inherits AbstractCrossLanguageUserDiagnosticTest - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then Throw New NotSupportedException("Please add C# Implement interface tests to CSharpEditorTestTests.csproj. These tests require DiagnosticAnalyzer based test base and are NYI for AbstractCrossLanguageUserDiagnosticTest test base.") Else - Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - Nothing, - New VisualBasicImplementInterfaceCodeFixProvider()) + Return (Nothing, New VisualBasicImplementInterfaceCodeFixProvider()) End If End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb b/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb index 7532516d2d58a..43784d1a38d3c 100644 --- a/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb @@ -7,11 +7,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.UseAutoProperty Public Class UseAutoPropertyTests Inherits AbstractCrossLanguageUserDiagnosticTest - Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then - Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(New CSharp.UseAutoProperty.UseAutoPropertyAnalyzer(), New CSharp.UseAutoProperty.UseAutoPropertyCodeFixProvider()) - ElseIf language = LanguageNames.VisualBasic - Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(New VisualBasic.UseAutoProperty.UseAutoPropertyAnalyzer(), New VisualBasic.UseAutoProperty.UseAutoPropertyCodeFixProvider()) + Return (New CSharp.UseAutoProperty.UseAutoPropertyAnalyzer(), New CSharp.UseAutoProperty.UseAutoPropertyCodeFixProvider()) + ElseIf language = LanguageNames.VisualBasic Then + Return (New VisualBasic.UseAutoProperty.UseAutoPropertyAnalyzer(), New VisualBasic.UseAutoProperty.UseAutoPropertyCodeFixProvider()) Else Throw New Exception() End If diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb index 60d5195ac1cbc..89c40f3a13061 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb @@ -4,14 +4,14 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateVariable +Imports Microsoft.CodeAnalysis.VisualBasic.GenerateVariable Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.GenerateVariable Public Class GenerateVariableTests Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) - Return (Nothing, New GenerateVariableCodeFixProvider()) + Return (Nothing, New VisualBasicGenerateVariableCodeFixProvider()) End Function Protected Overrides Function MassageActions(actions As ImmutableArray(Of CodeAction)) As ImmutableArray(Of CodeAction) diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index a15b756b7d3c3..276a40dc7c7b0 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -131,7 +131,7 @@ - + diff --git a/src/Features/CSharp/Portable/CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.cs b/src/Features/CSharp/Portable/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs similarity index 94% rename from src/Features/CSharp/Portable/CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.cs rename to src/Features/CSharp/Portable/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs index a497a99f335f3..0f4563a17eb56 100644 --- a/src/Features/CSharp/Portable/CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs @@ -12,11 +12,11 @@ using Microsoft.CodeAnalysis.GenerateMember.GenerateVariable; using Microsoft.CodeAnalysis.Shared.Extensions; -namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateVariable +namespace Microsoft.CodeAnalysis.CSharp.GenerateVariable { [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.GenerateVariable), Shared] [ExtensionOrder(After = PredefinedCodeFixProviderNames.GenerateMethod)] - internal class GenerateVariableCodeFixProvider : AbstractGenerateMemberCodeFixProvider + internal class CSharpGenerateVariableCodeFixProvider : AbstractGenerateMemberCodeFixProvider { private const string CS1061 = nameof(CS1061); // error CS1061: 'C' does not contain a definition for 'Foo' and no extension method 'Foo' accepting a first argument of type 'C' could be found private const string CS0103 = nameof(CS0103); // error CS0103: The name 'Foo' does not exist in the current context diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj index 71f6b35a7f052..95ceadeb0e4dd 100644 --- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj @@ -89,7 +89,7 @@ - + diff --git a/src/Features/VisualBasic/Portable/CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.vb b/src/Features/VisualBasic/Portable/GenerateVariable/VisualBasicGenerateVariableCodeFixProvider.vb similarity index 95% rename from src/Features/VisualBasic/Portable/CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.vb rename to src/Features/VisualBasic/Portable/GenerateVariable/VisualBasicGenerateVariableCodeFixProvider.vb index 08e48d571ea4a..1d9bc52384e29 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/GenerateVariable/GenerateVariableCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/GenerateVariable/VisualBasicGenerateVariableCodeFixProvider.vb @@ -9,10 +9,10 @@ Imports Microsoft.CodeAnalysis.CodeFixes.GenerateMember Imports Microsoft.CodeAnalysis.GenerateMember.GenerateVariable Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateVariable +Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateVariable - Friend Class GenerateVariableCodeFixProvider + Friend Class VisualBasicGenerateVariableCodeFixProvider Inherits AbstractGenerateMemberCodeFixProvider Friend Const BC30456 As String = "BC30456" ' error BC30456: 'Foo' is not a member of 'P'. From ff703cb1b19bd8e313b012377addc4b316416278 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 22:59:22 -0700 Subject: [PATCH 037/284] Add heuristic to prefer generating readonly fields. --- .../GenerateVariable/GenerateVariableTests.cs | 54 ++++++++++ .../AbstractGenerateVariableService.State.cs | 101 ++++++++++++++++++ .../AbstractGenerateVariableService.cs | 40 +++++-- 3 files changed, 188 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs index 37ebfc1709bb0..77b03830cd953 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs @@ -7173,6 +7173,60 @@ void M() { [|nameof|] } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestPreferReadOnlyIfAfterReadOnlyAssignment() + { + await TestInRegularAndScriptAsync( +@"class Class +{ + private readonly int _foo; + + public Class() + { + _foo = 0; + [|_bar|] = 1; + } +}", +@"class Class +{ + private readonly int _foo; + private readonly int _bar; + + public Class() + { + _foo = 0; + _bar = 1; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestPreferReadOnlyIfBeforeReadOnlyAssignment() + { + await TestInRegularAndScriptAsync( +@"class Class +{ + private readonly int _foo; + + public Class() + { + [|_bar|] = 1; + _foo = 0; + } +}", +@"class Class +{ + private readonly int _foo; + private readonly int _bar; + + public Class() + { + _bar = 1; + _foo = 0; + } }"); } } diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 20b8361c56311..1aee2bf794011 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -36,6 +37,9 @@ private partial class State public ITypeSymbol TypeMemberType { get; private set; } public ITypeSymbol LocalType { get; private set; } + public bool ReadOnlyFieldFirst { get; private set; } + public bool ReadOnlyPropertyFirst { get; private set; } + public bool IsWrittenTo { get; private set; } public bool IsOnlyWrittenTo { get; private set; } @@ -252,9 +256,106 @@ private bool TryInitializeSimpleName( this.IsInConstructor = DetermineIsInConstructor(document); this.IsInMemberContext = this.SimpleNameOpt != this.SimpleNameOrMemberAccessExpressionOpt || syntaxFacts.IsObjectInitializerNamedAssignmentIdentifier(this.SimpleNameOrMemberAccessExpressionOpt); + + this.ReadOnlyPropertyFirst = DetermineReadOnlyFirst(document, SymbolKind.Property, cancellationToken); + this.ReadOnlyFieldFirst = DetermineReadOnlyFirst(document, SymbolKind.Field, cancellationToken); + return true; } + private bool DetermineReadOnlyFirst( + SemanticDocument document, SymbolKind symbolKind, CancellationToken cancellationToken) + { + var syntaxFacts = document.Document.GetLanguageService(); + var simpleName = this.SimpleNameOpt; + + if (syntaxFacts.IsLeftSideOfAssignment(simpleName)) + { + var assignment = simpleName.Parent; + if (syntaxFacts.IsSimpleAssignmentStatement(assignment.Parent)) + { + var statement = assignment.Parent; + var block = statement.Parent; + var children = block.ChildNodesAndTokens(); + + var statementindex = GetStatementindex(children, statement); + return IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex - 1, cancellationToken) || + IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex + 1, cancellationToken); + } + } + + return false; + } + + private bool IsReadOnlyMemberAssignment( + SemanticDocument document, SymbolKind symbolKind, + ChildSyntaxList children, int index, + CancellationToken cancellationToken) + { + var syntaxFacts = document.Document.GetLanguageService(); + if (index >= 0 && index < children.Count) + { + var sibling = children[index]; + if (sibling.IsNode) + { + var siblingNode = sibling.AsNode(); + if (syntaxFacts.IsSimpleAssignmentStatement(siblingNode)) + { + syntaxFacts.GetPartsOfAssignmentStatement( + siblingNode, out var left, out var right); + + var symbol = document.SemanticModel.GetSymbolInfo(left, cancellationToken).Symbol; + if (symbol?.Kind == symbolKind && + symbol.ContainingType.Equals(this.ContainingType)) + { + return IsReadOnly(symbol); + } + } + } + } + + return false; + } + + private bool IsReadOnly(ISymbol symbol) + { + switch (symbol) + { + case IFieldSymbol field: return field.IsReadOnly; + case IPropertySymbol property: + if (property.SetMethod == null) + { + return true; + } + + if (property.ContainingType.GetMembers().OfType().Any( + f => property.SetMethod.Equals(f.AssociatedSymbol))) + { + return true; + } + + return false; + } + + throw ExceptionUtilities.Unreachable; + } + + private int GetStatementindex(ChildSyntaxList children, SyntaxNode statement) + { + var index = 0; + foreach (var child in children) + { + if (child == statement) + { + return index; + } + + index++; + } + + throw ExceptionUtilities.Unreachable; + } + private void DetermineFieldType( SemanticDocument document, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs index 740ca7034edb2..7391afaa4d749 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs @@ -108,16 +108,29 @@ private void AddPropertyCodeActions( return; } - result.Add(new GenerateVariableCodeAction( - (TService)this, document, state, generateProperty: true, - isReadonly: false, isConstant: false, returnsByRef: state.IsInRefContext)); + if (!state.ReadOnlyPropertyFirst) + { + GenerateWritableProperty(result, document, state); + } if (state.TypeToGenerateIn.TypeKind == TypeKind.Interface && !state.IsWrittenTo) { result.Add(new GenerateVariableCodeAction( - (TService)this, document, state, generateProperty: true, + (TService)this, document, state, generateProperty: true, isReadonly: true, isConstant: false, returnsByRef: state.IsInRefContext)); } + + if (state.ReadOnlyPropertyFirst) + { + GenerateWritableProperty(result, document, state); + } + } + + private void GenerateWritableProperty(ArrayBuilder result, SemanticDocument document, State state) + { + result.Add(new GenerateVariableCodeAction( + (TService)this, document, state, generateProperty: true, + isReadonly: false, isConstant: false, returnsByRef: state.IsInRefContext)); } private void AddFieldCodeActions(ArrayBuilder result, SemanticDocument document, State state) @@ -132,9 +145,10 @@ private void AddFieldCodeActions(ArrayBuilder result, SemanticDocume } else { - result.Add(new GenerateVariableCodeAction( - (TService)this, document, state, generateProperty: false, - isReadonly: false, isConstant: false, returnsByRef: false)); + if (!state.ReadOnlyFieldFirst) + { + GenerateWriteableField(result, document, state); + } // If we haven't written to the field, or we're in the constructor for the type // we're writing into, then we can generate this field read-only. @@ -144,10 +158,22 @@ private void AddFieldCodeActions(ArrayBuilder result, SemanticDocume (TService)this, document, state, generateProperty: false, isReadonly: true, isConstant: false, returnsByRef: false)); } + + if (state.ReadOnlyFieldFirst) + { + GenerateWriteableField(result, document, state); + } } } } + private void GenerateWriteableField(ArrayBuilder result, SemanticDocument document, State state) + { + result.Add(new GenerateVariableCodeAction( + (TService)this, document, state, generateProperty: false, + isReadonly: false, isConstant: false, returnsByRef: false)); + } + private void AddLocalCodeActions(ArrayBuilder result, Document document, State state) { if (state.CanGenerateLocal()) From 26c708fb2c2463ddbc12fc3c7f67b648a4e65cbf Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 23:14:48 -0700 Subject: [PATCH 038/284] Prefer readonly properties in constructors. --- .../GenerateVariable/GenerateVariableTests.cs | 65 ++++++++++++++++--- .../AbstractGenerateVariableService.State.cs | 2 - .../AbstractGenerateVariableService.cs | 12 +--- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs index 77b03830cd953..3825d90a66a7b 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs @@ -814,7 +814,7 @@ void Method(I i) interface I { object Foo { get; set; } -}"); +}", index: 1); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] @@ -843,8 +843,7 @@ void Method(I i) interface I { object Foo { get; } -}", -index: 1); +}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] @@ -1509,7 +1508,7 @@ bool ITest.SomeProp { } interface ITest { bool SomeProp { get; set; } -}"); +}", index: 1); } [WorkItem(539468, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539468")] @@ -1533,8 +1532,7 @@ bool ITest.SomeProp { } interface ITest { bool SomeProp { get; } -}", -index: 1); +}"); } [WorkItem(539468, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539468")] @@ -7128,7 +7126,7 @@ public void Foo() [WorkItem(17621, "https://github.com/dotnet/roslyn/issues/17621")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] - public async Task TestWithMatchingTypeName() + public async Task TestWithMatchingTypeName1() { await TestInRegularAndScript1Async( @" @@ -7150,10 +7148,38 @@ public Foo(String foo) String = foo; } - public string String { get; private set; } + public string String { get; } }"); } + [WorkItem(17621, "https://github.com/dotnet/roslyn/issues/17621")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestWithMatchingTypeName2() + { + await TestInRegularAndScript1Async( +@" +using System; + +public class Foo +{ + public Foo(String foo) + { + [|String|] = foo; + } +}", +@"using System; + +public class Foo +{ + public Foo(String foo) + { + String = foo; + } + + public string String { get; private set; } +}", index: 1); + } + [WorkItem(18275, "https://github.com/dotnet/roslyn/issues/18275")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task TestContextualKeyword1() @@ -7227,6 +7253,29 @@ public Class() _bar = 1; _foo = 0; } +}"); + } + + [WorkItem(19239, "https://github.com/dotnet/roslyn/issues/19239")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestGenerateReadOnlyPropertyInConstructor() + { + await TestInRegularAndScriptAsync( +@"class Class +{ + public Class() + { + [|Bar|] = 1; + } +}", +@"class Class +{ + public Class() + { + Bar = 1; + } + + public int Bar { get; } }"); } } diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 1aee2bf794011..f5348949eb81e 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -38,7 +38,6 @@ private partial class State public ITypeSymbol LocalType { get; private set; } public bool ReadOnlyFieldFirst { get; private set; } - public bool ReadOnlyPropertyFirst { get; private set; } public bool IsWrittenTo { get; private set; } public bool IsOnlyWrittenTo { get; private set; } @@ -257,7 +256,6 @@ private bool TryInitializeSimpleName( this.IsInMemberContext = this.SimpleNameOpt != this.SimpleNameOrMemberAccessExpressionOpt || syntaxFacts.IsObjectInitializerNamedAssignmentIdentifier(this.SimpleNameOrMemberAccessExpressionOpt); - this.ReadOnlyPropertyFirst = DetermineReadOnlyFirst(document, SymbolKind.Property, cancellationToken); this.ReadOnlyFieldFirst = DetermineReadOnlyFirst(document, SymbolKind.Field, cancellationToken); return true; diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs index 7391afaa4d749..d865a1f288a06 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs @@ -108,22 +108,16 @@ private void AddPropertyCodeActions( return; } - if (!state.ReadOnlyPropertyFirst) - { - GenerateWritableProperty(result, document, state); - } + var isOnlyReadAndIsInInterface = state.TypeToGenerateIn.TypeKind == TypeKind.Interface && !state.IsWrittenTo; - if (state.TypeToGenerateIn.TypeKind == TypeKind.Interface && !state.IsWrittenTo) + if (isOnlyReadAndIsInInterface || state.IsInConstructor) { result.Add(new GenerateVariableCodeAction( (TService)this, document, state, generateProperty: true, isReadonly: true, isConstant: false, returnsByRef: state.IsInRefContext)); } - if (state.ReadOnlyPropertyFirst) - { - GenerateWritableProperty(result, document, state); - } + GenerateWritableProperty(result, document, state); } private void GenerateWritableProperty(ArrayBuilder result, SemanticDocument document, State state) From 7e582ebd0331299c7cccdb2549cc68e46eb032f7 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 23:43:28 -0700 Subject: [PATCH 039/284] VB side. --- .../GenerateVariable/GenerateVariableTests.vb | 93 ++++++++++++++++++- .../AbstractGenerateVariableService.State.cs | 24 +++-- .../AbstractGenerateVariableService.cs | 4 +- 3 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb index 89c40f3a13061..614172cce8c76 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb @@ -106,6 +106,29 @@ End Interface", End Module Interface IFoo ReadOnly Property Blah As String() +End Interface") + End Function + + + + Public Async Function TestGenerateReadWriteProperty() As Task + Await TestInRegularAndScriptAsync( +"Module Program + Sub Main(args As String()) + Dim i As IFoo + Main(i.[|Blah|]) + End Sub +End Module +Interface IFoo +End Interface", +"Module Program + Sub Main(args As String()) + Dim i As IFoo + Main(i.Blah) + End Sub +End Module +Interface IFoo + Property Blah As String() End Interface", index:=1) End Function @@ -168,7 +191,7 @@ index:=1) End Function - Public Async Function TestGeneratePropertyOnInterface() As Threading.Tasks.Task + Public Async Function TestGeneratePropertyOnInterface1() As Threading.Tasks.Task Await TestInRegularAndScriptAsync( "Interface IFoo End Interface @@ -179,7 +202,7 @@ Class C End Sub End Class", "Interface IFoo - Property Bar As Object + ReadOnly Property Bar As Object End Interface Class C Sub Main @@ -189,6 +212,28 @@ Class C End Class") End Function + + Public Async Function TestGeneratePropertyOnInterface2() As Threading.Tasks.Task + Await TestInRegularAndScriptAsync( +"Interface IFoo +End Interface +Class C + Sub Main + Dim foo As IFoo + Dim b = foo.[|Bar|] + End Sub +End Class", +"Interface IFoo + Property Bar As Object +End Interface +Class C + Sub Main + Dim foo As IFoo + Dim b = foo.Bar + End Sub +End Class", index:=1) + End Function + Public Async Function TestGeneratePropertyIntoModule() As Threading.Tasks.Task @@ -2516,5 +2561,49 @@ End Class", End Sub End Class") End Function + + + Public Async Function TestPreferReadOnlyIfAfterReadOnlyAssignment() As Task + await TestInRegularAndScriptAsync( +"class C + private readonly _foo as integer + + public sub new() + _foo = 0 + [|_bar|] = 1 + end sub +end class", +"class C + private readonly _foo as integer + Private ReadOnly _bar As Integer + + public sub new() + _foo = 0 + _bar = 1 + end sub +end class") + End Function + + + Public Async Function TestPreferReadOnlyIfBeforeReadOnlyAssignment() As Task + Await TestInRegularAndScriptAsync( +"class C + private readonly _foo as integer + + public sub new() + [|_bar|] = 1 + _foo = 0 + end sub +end class", +"class C + private readonly _foo as integer + Private ReadOnly _bar As Integer + + public sub new() + _bar = 1 + _foo = 0 + end sub +end class") + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index f5348949eb81e..1c6d4b677cfd6 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -37,7 +37,7 @@ private partial class State public ITypeSymbol TypeMemberType { get; private set; } public ITypeSymbol LocalType { get; private set; } - public bool ReadOnlyFieldFirst { get; private set; } + public bool OfferReadOnlyFieldFirst { get; private set; } public bool IsWrittenTo { get; private set; } public bool IsOnlyWrittenTo { get; private set; } @@ -256,7 +256,7 @@ private bool TryInitializeSimpleName( this.IsInMemberContext = this.SimpleNameOpt != this.SimpleNameOrMemberAccessExpressionOpt || syntaxFacts.IsObjectInitializerNamedAssignmentIdentifier(this.SimpleNameOrMemberAccessExpressionOpt); - this.ReadOnlyFieldFirst = DetermineReadOnlyFirst(document, SymbolKind.Field, cancellationToken); + this.OfferReadOnlyFieldFirst = DetermineReadOnlyFirst(document, SymbolKind.Field, cancellationToken); return true; } @@ -269,16 +269,20 @@ private bool DetermineReadOnlyFirst( if (syntaxFacts.IsLeftSideOfAssignment(simpleName)) { - var assignment = simpleName.Parent; - if (syntaxFacts.IsSimpleAssignmentStatement(assignment.Parent)) + var assignmentStatement = simpleName.Ancestors().FirstOrDefault(syntaxFacts.IsSimpleAssignmentStatement); + if (assignmentStatement != null) { - var statement = assignment.Parent; - var block = statement.Parent; - var children = block.ChildNodesAndTokens(); + syntaxFacts.GetPartsOfAssignmentStatement(assignmentStatement, out var left, out var right); - var statementindex = GetStatementindex(children, statement); - return IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex - 1, cancellationToken) || - IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex + 1, cancellationToken); + if (left == simpleName) + { + var block = assignmentStatement.Parent; + var children = block.ChildNodesAndTokens(); + + var statementindex = GetStatementindex(children, assignmentStatement); + return IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex - 1, cancellationToken) || + IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex + 1, cancellationToken); + } } } diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs index d865a1f288a06..cd25b2caf5e5f 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs @@ -139,7 +139,7 @@ private void AddFieldCodeActions(ArrayBuilder result, SemanticDocume } else { - if (!state.ReadOnlyFieldFirst) + if (!state.OfferReadOnlyFieldFirst) { GenerateWriteableField(result, document, state); } @@ -153,7 +153,7 @@ private void AddFieldCodeActions(ArrayBuilder result, SemanticDocume isReadonly: true, isConstant: false, returnsByRef: false)); } - if (state.ReadOnlyFieldFirst) + if (state.OfferReadOnlyFieldFirst) { GenerateWriteableField(result, document, state); } From 875191bf7cee4f5f2584a19cb3237922dabd9714 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 5 May 2017 05:50:39 -0500 Subject: [PATCH 040/284] Use LookupSymbols and LookupNamespacesAndTypes to improve candidate speed and accuracy --- .../Extensions/ExpressionSyntaxExtensions.cs | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs index 87a897aebe7b6..9e41dab000a75 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; @@ -809,12 +810,23 @@ private static bool TryReduce( return false; } - // Get a syntax node that's not part of the tree to make sure we speculate on just the name. - var speculativeTree = memberAccess.Name.WithoutTrivia(); - var symbolInfo = semanticModel.GetSpeculativeSymbolInfo(memberAccess.SpanStart, speculativeTree, SpeculativeBindingOption.BindAsExpression); - if (!IsReplacementCandidate(actualSymbol, symbolInfo)) + var symbols = semanticModel.LookupSymbols(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText); + if (!IsReplacementCandidate(actualSymbol, symbols, ImmutableArray.Empty)) { - return false; + if (symbols.IsEmpty) + { + // No need to lookup namespaces and types separately, because without a symbol of the sames name + // in a local scope they would have already been found by LookupSymbols. + return false; + } + + // If we found symbols, but not the *right* symbols, also look up namespaces and types explicitly to + // find candidates. + var typeSymbols = semanticModel.LookupNamespacesAndTypes(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText); + if (!IsReplacementCandidate(actualSymbol, symbols, typeSymbols)) + { + return false; + } } } @@ -832,29 +844,25 @@ private static bool TryReduce( } /// - /// Determines if contains a superset of the symbols in - /// . + /// Determines if and + /// together contain a superset of the symbols in . /// - private static bool IsReplacementCandidate(SymbolInfo actualSymbol, SymbolInfo speculativeSymbol) + private static bool IsReplacementCandidate(SymbolInfo actualSymbol, ImmutableArray speculativeSymbols, ImmutableArray speculativeNamespacesAndTypes) { - if (!(speculativeSymbol.Symbol is null)) - { - return Equals(actualSymbol.Symbol, speculativeSymbol.Symbol); - } - - if (speculativeSymbol.CandidateSymbols.IsDefault) + if (speculativeSymbols.IsEmpty && speculativeNamespacesAndTypes.IsEmpty) { return false; } if (!(actualSymbol.Symbol is null)) { - return speculativeSymbol.CandidateSymbols.Contains(actualSymbol.Symbol); + return speculativeSymbols.Contains(actualSymbol.Symbol) + || speculativeNamespacesAndTypes.Contains(actualSymbol.Symbol); } foreach (var symbol in actualSymbol.CandidateSymbols) { - if (!speculativeSymbol.CandidateSymbols.Contains(symbol)) + if (!speculativeSymbols.Contains(symbol) && !speculativeNamespacesAndTypes.Contains(symbol)) { return false; } From 94d573e47da0e8a093970f463da6406be1ae9959 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 5 May 2017 06:16:25 -0500 Subject: [PATCH 041/284] Avoid calling both LookupSymbols and LookupNamespacesAndTypes when we only need the latter --- .../Extensions/ExpressionSyntaxExtensions.cs | 60 +++++++++++++------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs index 9e41dab000a75..0fd7c7f739ce6 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs @@ -805,28 +805,19 @@ private static bool TryReduce( if (!memberAccess.Expression.IsKind(SyntaxKind.ThisExpression, SyntaxKind.BaseExpression)) { var actualSymbol = semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken); - if (actualSymbol.GetAnySymbol() is null) + if (!TryGetReplacementCandidates( + semanticModel, + memberAccess, + actualSymbol, + out var speculativeSymbols, + out var speculativeNamespacesAndTypes)) { return false; } - var symbols = semanticModel.LookupSymbols(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText); - if (!IsReplacementCandidate(actualSymbol, symbols, ImmutableArray.Empty)) + if (!IsReplacementCandidate(actualSymbol, speculativeSymbols, speculativeNamespacesAndTypes)) { - if (symbols.IsEmpty) - { - // No need to lookup namespaces and types separately, because without a symbol of the sames name - // in a local scope they would have already been found by LookupSymbols. - return false; - } - - // If we found symbols, but not the *right* symbols, also look up namespaces and types explicitly to - // find candidates. - var typeSymbols = semanticModel.LookupNamespacesAndTypes(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText); - if (!IsReplacementCandidate(actualSymbol, symbols, typeSymbols)) - { - return false; - } + return false; } } @@ -843,6 +834,41 @@ private static bool TryReduce( return memberAccess.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken); } + private static bool TryGetReplacementCandidates( + SemanticModel semanticModel, + MemberAccessExpressionSyntax memberAccess, + SymbolInfo actualSymbol, + out ImmutableArray speculativeSymbols, + out ImmutableArray speculativeNamespacesAndTypes) + { + bool containsNamespaceOrTypeSymbol; + bool containsOtherSymbol; + if (!(actualSymbol.Symbol is null)) + { + containsNamespaceOrTypeSymbol = actualSymbol.Symbol is INamespaceOrTypeSymbol; + containsOtherSymbol = !containsNamespaceOrTypeSymbol; + } + else if (!actualSymbol.CandidateSymbols.IsDefaultOrEmpty) + { + containsNamespaceOrTypeSymbol = actualSymbol.CandidateSymbols.Any(symbol => symbol is INamespaceOrTypeSymbol); + containsOtherSymbol = actualSymbol.CandidateSymbols.Any(symbol => !(symbol is INamespaceOrTypeSymbol)); + } + else + { + speculativeSymbols = ImmutableArray.Empty; + speculativeNamespacesAndTypes = ImmutableArray.Empty; + return false; + } + + speculativeSymbols = containsOtherSymbol + ? semanticModel.LookupSymbols(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText) + : ImmutableArray.Empty; + speculativeNamespacesAndTypes = containsNamespaceOrTypeSymbol + ? semanticModel.LookupNamespacesAndTypes(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText) + : ImmutableArray.Empty; + return true; + } + /// /// Determines if and /// together contain a superset of the symbols in . From 36d55d1d20da6464f4106572da7223bf8f236cc2 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Fri, 5 May 2017 08:28:29 -0700 Subject: [PATCH 042/284] Revert "Merge pull request #10993 from rchande/quickinfosession" This reverts commit 3ef5891043f89145b1e1c1a7de8c0e02acd15c6e, reversing changes made to 923fcbf40f87c7c50ea09c8375556c293795874a. --- .../IntelliSense/QuickInfo/Controller.cs | 24 +++++-------------- .../IntelliSense/QuickInfo/Model.cs | 2 ++ ...InfoPresenter.QuickInfoPresenterSession.cs | 6 ----- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs index f230eeff3a5d6..d5808c101bfe9 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Controller.cs @@ -77,26 +77,14 @@ internal override void OnModelUpdated(Model modelOpt) } else { + var quickInfoItem = modelOpt.Item; + // We want the span to actually only go up to the caret. So get the expected span // and then update its end point accordingly. - ITrackingSpan trackingSpan; - QuickInfoItem item = null; + var triggerSpan = modelOpt.GetCurrentSpanInSnapshot(quickInfoItem.TextSpan, this.SubjectBuffer.CurrentSnapshot); + var trackingSpan = triggerSpan.CreateTrackingSpan(SpanTrackingMode.EdgeInclusive); - // Whether or not we have an item to show, we need to start the session. - // If the user Edit.QuickInfo's on a squiggle, they want to see the - // error text even if there's no symbol quickinfo. - if (modelOpt.Item != null) - { - item = modelOpt.Item; - var triggerSpan = modelOpt.GetCurrentSpanInSnapshot(item.TextSpan, this.SubjectBuffer.CurrentSnapshot); - trackingSpan = triggerSpan.CreateTrackingSpan(SpanTrackingMode.EdgeInclusive); - } - else - { - var caret = this.TextView.GetCaretPoint(this.SubjectBuffer).Value; - trackingSpan = caret.Snapshot.CreateTrackingSpan(caret.Position, 0, SpanTrackingMode.EdgeInclusive, TrackingFidelityMode.Forward); - } - sessionOpt.PresenterSession.PresentItem(trackingSpan, item, modelOpt.TrackMouse); + sessionOpt.PresenterSession.PresentItem(trackingSpan, quickInfoItem, modelOpt.TrackMouse); } } @@ -170,7 +158,7 @@ private async Task ComputeModelInBackgroundAsync( } } - return new Model(snapshot.Version, null, null, trackMouse); + return null; } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs index d137e532acf78..ba37c6ced2a6c 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Model.cs @@ -20,6 +20,8 @@ public Model( IQuickInfoProvider provider, bool trackMouse) { + Contract.ThrowIfNull(item); + this.TextVersion = textVersion; this.Item = item; this.Provider = provider; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs index d6caa46b94153..f31ab17118f3e 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/Presentation/QuickInfoPresenter.QuickInfoPresenterSession.cs @@ -100,12 +100,6 @@ private void OnEditorSessionDismissed() internal void AugmentQuickInfoSession(IList quickInfoContent, out ITrackingSpan applicableToSpan) { - if (_item == null) - { - applicableToSpan = null; - return; - } - applicableToSpan = _triggerSpan; quickInfoContent.Add(_item.Content.Create()); } From a4d168aef3637768ddf5b463a603ef019a260aa0 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 5 May 2017 10:42:58 -0500 Subject: [PATCH 043/284] Compare candidate symbols by their original definition The bound symbols have instantiated generic types/methods, but the candidates found through a lookup by name do not. We compare their original definitions to treat these uninstantiated generics as true candidates for simplification. --- .../Extensions/ExpressionSyntaxExtensions.cs | 34 +++++++++++++++++-- .../Utilities/ImmutableArrayExtensions.cs | 3 ++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs index 0fd7c7f739ce6..95225e1765ef6 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs @@ -882,13 +882,14 @@ private static bool IsReplacementCandidate(SymbolInfo actualSymbol, ImmutableArr if (!(actualSymbol.Symbol is null)) { - return speculativeSymbols.Contains(actualSymbol.Symbol) - || speculativeNamespacesAndTypes.Contains(actualSymbol.Symbol); + return speculativeSymbols.Contains(actualSymbol.Symbol, CandidateSymbolEqualityComparer.Instance) + || speculativeNamespacesAndTypes.Contains(actualSymbol.Symbol, CandidateSymbolEqualityComparer.Instance); } foreach (var symbol in actualSymbol.CandidateSymbols) { - if (!speculativeSymbols.Contains(symbol) && !speculativeNamespacesAndTypes.Contains(symbol)) + if (!speculativeSymbols.Contains(symbol, CandidateSymbolEqualityComparer.Instance) + && !speculativeNamespacesAndTypes.Contains(symbol, CandidateSymbolEqualityComparer.Instance)) { return false; } @@ -897,6 +898,33 @@ private static bool IsReplacementCandidate(SymbolInfo actualSymbol, ImmutableArr return true; } + /// + /// Compares symbols by their original definition. + /// + private sealed class CandidateSymbolEqualityComparer : IEqualityComparer + { + public static CandidateSymbolEqualityComparer Instance { get; } = new CandidateSymbolEqualityComparer(); + + private CandidateSymbolEqualityComparer() + { + } + + public bool Equals(ISymbol x, ISymbol y) + { + if (x is null || y is null) + { + return x == y; + } + + return x.OriginalDefinition.Equals(y.OriginalDefinition); + } + + public int GetHashCode(ISymbol obj) + { + return obj?.OriginalDefinition.GetHashCode() ?? 0; + } + } + private static SyntaxTriviaList GetLeadingTriviaForSimplifiedMemberAccess(this MemberAccessExpressionSyntax memberAccess) { // We want to include any user-typed trivia that may be present between the 'Expression', 'OperatorToken' and 'Identifier' of the MemberAccessExpression. diff --git a/src/Workspaces/Core/Portable/Utilities/ImmutableArrayExtensions.cs b/src/Workspaces/Core/Portable/Utilities/ImmutableArrayExtensions.cs index 58f3e2d24f359..1485111673fc0 100644 --- a/src/Workspaces/Core/Portable/Utilities/ImmutableArrayExtensions.cs +++ b/src/Workspaces/Core/Portable/Utilities/ImmutableArrayExtensions.cs @@ -8,6 +8,9 @@ namespace Roslyn.Utilities { internal static class ImmutableArrayExtensions { + internal static bool Contains(this ImmutableArray items, T item, IEqualityComparer equalityComparer) + => items.IndexOf(item, 0, equalityComparer) >= 0; + internal static ImmutableArray ToImmutableArrayOrEmpty(this T[] items) { if (items == null) From 1d9b64a214cfaee648e5ab0c02f85cbbded6edad Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Fri, 5 May 2017 09:55:59 -0700 Subject: [PATCH 044/284] Fix one unit test and skip couple of unit tests due to the API changes. I have filed #19300 to track the skipped tests --- .../Diagnostics/OperationAnalyzerTests.cs | 2 +- .../Diagnostics/OperationAnalyzerTests.vb | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs index 68664fd6013fe..794bfc28d8034 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs @@ -835,7 +835,7 @@ public void M3() ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/19300")] public void MemberInitializerCSharp() { const string source = @" diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb index 055d69d51c90e..4fa188e6ddbdd 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb @@ -794,7 +794,7 @@ End Class Diagnostic(NullArgumentTestAnalyzer.NullArgumentsDescriptor.Id, "Nothing").WithLocation(20, 26)) End Sub - + Public Sub MemberInitializerVisualBasic() Dim source = @@ -877,9 +877,17 @@ End Class Dim comp = CompilationUtils.CreateCompilationWithMscorlibAndVBRuntime(source, parseOptions:=TestOptions.RegularWithIOperationFeature) comp.VerifyDiagnostics() comp.VerifyAnalyzerDiagnostics({New AssignmentTestAnalyzer}, Nothing, Nothing, False, - Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, "f1.Field = 0").WithLocation(22, 9), - Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, "f1.Prop1 = Nothing").WithLocation(23, 9), - Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, "f2.Field = True").WithLocation(26, 9)) + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Prop2 = New Bar() With {.Field = True}").WithLocation(21, 32), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Field = 10").WithLocation(14, 34), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Prop1 = Nothing").WithLocation(15, 32), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Field = 10").WithLocation(16, 32), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Prop1 = Nothing").WithLocation(16, 45), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Prop2 = New Bar() With {.Field = True}").WithLocation(17, 32), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Field = True").WithLocation(17, 57), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, ".Field = True").WithLocation(21, 57), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, "f1.Field = 0").WithLocation(22, 9), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, "f1.Prop1 = Nothing").WithLocation(23, 9), + Diagnostic(AssignmentTestAnalyzer.DoNotUseMemberAssignmentDescriptor.Id, "f2.Field = True").WithLocation(26, 9)) End Sub From a124f06493bafaede2440fc6344a5d058c9bbc55 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Fri, 5 May 2017 13:24:41 -0700 Subject: [PATCH 045/284] some loggings --- ...HostClientServiceFactory.SolutionChecksumUpdater.cs | 3 +++ src/Workspaces/Remote/Core/Services/SolutionService.cs | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs index 2a85108a87d22..1142557513758 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Internal.Log; +using System.Diagnostics; namespace Microsoft.VisualStudio.LanguageServices.Remote { @@ -121,6 +122,8 @@ private async Task SynchronizePrimaryWorkspaceAsync(CancellationToken cancellati { var solution = _service.Workspace.CurrentSolution; var checksum = await solution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); + + Debug.WriteLine("**** Update Remote Primary Workspace"); await remoteHostClient.RunOnRemoteHostAsync( WellKnownRemoteHostServices.RemoteHostService, solution, diff --git a/src/Workspaces/Remote/Core/Services/SolutionService.cs b/src/Workspaces/Remote/Core/Services/SolutionService.cs index e6a103347048b..3dfaf89731005 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionService.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionService.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; @@ -33,7 +34,8 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, Cancella { var currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) - { + { + Debug.WriteLine("**** Cache Hit outside of lock " + (currentSolution.Workspace is RemoteWorkspace).ToString()); return currentSolution; } @@ -43,6 +45,7 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, Cancella currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) { + Debug.WriteLine("**** Cache Hit inside of lock " + (currentSolution.Workspace is RemoteWorkspace).ToString()); return currentSolution; } @@ -61,6 +64,7 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell if (primarySolutionChecksum == solutionChecksum) { // nothing changed + Debug.WriteLine("**** Primary workspace up to date"); return; } @@ -78,12 +82,14 @@ private async Task CreateSolution_NoLockAsync(Checksum solutionChecksu if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // solution has updated + Debug.WriteLine("**** Incremental solution update!"); return await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); } // new solution. bulk sync all asset for the solution await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); + Debug.WriteLine("**** Not same as primary workspace! new temporary workspace created!"); var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); return workspace.CurrentSolution; } @@ -95,6 +101,7 @@ private async Task UpdatePrimaryWorkspace_NoLockAsync(Checksum solutio if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // solution has updated + Debug.WriteLine("**** Primary worksace incremental update!"); s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); return s_primaryWorkspace.CurrentSolution; @@ -103,6 +110,7 @@ private async Task UpdatePrimaryWorkspace_NoLockAsync(Checksum solutio // new solution. bulk sync all asset for the solution await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); + Debug.WriteLine("**** New Primary workspace!"); s_primaryWorkspace.ClearSolution(); s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); From 5a6e108c55c1963c2260853069ffcd53b04b0b5a Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Fri, 5 May 2017 13:57:02 -0700 Subject: [PATCH 046/284] Fix analysis of IOperation descendants for BoundDelegateCreationExpression The current IOperation API implementation of BoundDelegateCreationExpression has a bunch of issues and needs to be redesigned. This is tracked by #8897 and will be addressed post 15.3. Meanwhile, to unblock analyzers on code containing delegate creation expressions with lambda arguments, this bound node has been switched to OperationKind.None with override for Children property. Fixes the first repro case provided in #8884 --- .../CSharp/Portable/BoundTree/Expression.cs | 34 ++----- ...tionTests_IParameterReferenceExpression.cs | 90 +++++++++++++++++++ .../Portable/BoundTree/Expression.vb | 37 ++------ ...tionTests_IParameterReferenceExpression.vb | 79 ++++++++++++++++ 4 files changed, 181 insertions(+), 59 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 605ada05830fa..097a7ed78ed93 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -414,44 +414,20 @@ public override TResult Accept(OperationVisitor - (object)this.MethodOpt != null && - (this.MethodOpt.IsVirtual || this.MethodOpt.IsAbstract || this.MethodOpt.IsOverride) && - !this.SuppressVirtualCalls; - - ISymbol IMemberReferenceExpression.Member => this.MethodOpt; - - IMethodSymbol IMethodBindingExpression.Method => this.MethodOpt; - - protected override OperationKind ExpressionKind => OperationKind.MethodBindingExpression; + protected override OperationKind ExpressionKind => OperationKind.None; - // SyntaxNode for MethodBindingExpression is the argument of DelegateCreationExpression - SyntaxNode IOperation.Syntax => this.Argument.Syntax; + protected override ImmutableArray Children => ImmutableArray.Create(this.Argument); public override void Accept(OperationVisitor visitor) { - visitor.VisitMethodBindingExpression(this); + visitor.VisitNoneOperation(this); } public override TResult Accept(OperationVisitor visitor, TArgument argument) { - return visitor.VisitMethodBindingExpression(this, argument); + return visitor.VisitNoneOperation(this, argument); } } diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs index 509772a22f199..ed488198a123e 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -191,6 +191,96 @@ public void M(int x, int y, int z) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DelegateCreationExpressionWithLambdaArgument() + { + string source = @" +using System; + +class Class +{ + // Used parameter methods + public void UsedParameterMethod1(Action a) + { + Action a2 = /**/new Action(() => + { + a(); + })/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'new Action( ... })') + Children(1): ILambdaExpression (Signature: lambda expression) (OperationKind.LambdaExpression, Type: System.Action) (Syntax: '() => ... }') + IBlockStatement (2 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'a();') + IInvocationExpression (virtual void System.Action.Invoke()) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'a()') + Instance Receiver: IParameterReferenceExpression: a (OperationKind.ParameterReferenceExpression, Type: System.Action) (Syntax: 'a') + IReturnStatement (OperationKind.ReturnStatement) (Syntax: '{ ... }') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DelegateCreationExpressionWithMethodArgument() + { + string source = @" +using System; + +class Class +{ + public delegate void Delegate(int x, int y); + + public void Method(Delegate d) + { + var a = /**/new Delegate(Method2)/**/; + } + + public void Method2(int x, int y) + { + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'new Delegate(Method2)') + Children(1): IOperation: (OperationKind.None) (Syntax: 'Method2') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DelegateCreationExpressionWithInvalidArgument() + { + string source = @" +using System; + +class Class +{ + public delegate void Delegate(int x, int y); + + public void Method(int x) + { + var a = /**/new Delegate(x)/**/; + } +} +"; + string expectedOperationTree = @" +IInvalidExpression (OperationKind.InvalidExpression, Type: Class.Delegate, IsInvalid) (Syntax: 'new Delegate(x)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0149: Method name expected + // var a = /**/new Delegate(x)/**/; + Diagnostic(ErrorCode.ERR_MethodNameExpected, "x").WithLocation(10, 40) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] public void ParameterReference_DynamicCollectionInitializer() { diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb index 029f3a6f4c820..92ed00158a1f4 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb @@ -1500,46 +1500,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Class Friend Partial Class BoundDelegateCreationExpression - Implements IMethodBindingExpression - Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance - Get - If Me.Method.IsShared Then - Return Nothing - Else - Return Me.ReceiverOpt - End If - End Get - End Property - - Private ReadOnly Property IMethodBindingExpression_IsVirtual As Boolean Implements IMethodBindingExpression.IsVirtual - Get - Return Me.Method IsNot Nothing AndAlso (Me.Method.IsOverridable OrElse Me.Method.IsOverrides OrElse Me.Method.IsMustOverride) AndAlso Not Me.SuppressVirtualCalls - End Get - End Property - - Private ReadOnly Property IMemberReferenceExpression_Member As ISymbol Implements IMemberReferenceExpression.Member - Get - Return Me.Method - End Get - End Property + Protected Overrides Function ExpressionKind() As OperationKind + Return OperationKind.None + End Function - Private ReadOnly Property IMethodBindingExpression_Method As IMethodSymbol Implements IMethodBindingExpression.Method + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Get - Return Me.Method + Return ImmutableArray.Create(Of IOperation)(Me.ReceiverOpt) End Get End Property - Protected Overrides Function ExpressionKind() As OperationKind - Return OperationKind.MethodBindingExpression - End Function - Public Overrides Sub Accept(visitor As OperationVisitor) - visitor.VisitMethodBindingExpression(Me) + visitor.VisitNoneOperation(Me) End Sub Public Overrides Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult - Return visitor.VisitMethodBindingExpression(Me, argument) + Return visitor.VisitNoneOperation(Me, argument) End Function End Class diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb index 65581f7e4b429..511e6fa863f54 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb @@ -272,6 +272,85 @@ IObjectCreationExpression (Constructor: Sub [Class]..ctor()) (OperationKind.Obje VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + Public Sub ParameterReference_DelegateCreationExpressionWithLambdaArgument() + Dim source = .Value + + Dim expectedOperationTree = As System.Object + IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'x') + IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Object) (Syntax: 'x') + ILabelStatement (Label: exit) (OperationKind.LabelStatement) (Syntax: 'Function() x') + IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'Function() x') + ILocalReferenceExpression: (OperationKind.LocalReferenceExpression, Type: System.Object) (Syntax: 'Function() x') +]]>.Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_DelegateCreationExpressionWithMethodArgument() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_DelegateCreationExpressionWithInvalidArgument() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub ParameterReference_NameOfExpression() Dim source = Date: Fri, 5 May 2017 14:03:47 -0700 Subject: [PATCH 047/284] added link to github issue to make option part of solution --- src/Workspaces/Core/Portable/Workspace/Solution/Document.cs | 2 +- .../Remote/Core/Diagnostics/DiagnosticComputer.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index 9536fd9e46507..58360061cf39c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -472,7 +472,7 @@ internal Task GetOptionsAsync(OptionSet globalOptionSet, Canc // TODO: we have this workaround since Solution.Options is not actually snapshot but just return Workspace.Options which violate snapshot model. // this doesn't validate whether same optionset is given to invalidate the cache or not. this is not new since existing implementation // also didn't check whether Workspace.Option is same as before or not. all wierd-ness come from the root cause of Solution.Options violating - // snapshot model. once that is fixed, we can remove this workaround + // snapshot model. once that is fixed, we can remove this workaround - https://github.com/dotnet/roslyn/issues/19284 if (_cachedOptions == null) { var newAsyncLazy = new AsyncLazy(async c => diff --git a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs index a9bb623709a98..6bc968139429d 100644 --- a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs @@ -68,10 +68,14 @@ private async Task Date: Fri, 5 May 2017 15:23:16 -0700 Subject: [PATCH 048/284] change to translate any exception to cancellation exception if cancellation is raised. --- .../Core/Next/Remote/JsonRpcClient.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/VisualStudio/Core/Next/Remote/JsonRpcClient.cs b/src/VisualStudio/Core/Next/Remote/JsonRpcClient.cs index 42e1d7d427efd..64d5a62b7681a 100644 --- a/src/VisualStudio/Core/Next/Remote/JsonRpcClient.cs +++ b/src/VisualStudio/Core/Next/Remote/JsonRpcClient.cs @@ -42,12 +42,12 @@ public async Task InvokeAsync(string targetName, params object[] arguments) { await _rpc.InvokeAsync(targetName, arguments).ConfigureAwait(false); } - catch (ObjectDisposedException) + catch { - // object disposed exception can be thrown from StreamJsonRpc if JsonRpc is disposed in the middle of read/write. - // the way we added cancellation support to the JsonRpc which doesn't support cancellation natively - // can cause this exception to happen. newer version supports cancellation token natively, but - // we can't use it now, so we will catch object disposed exception and check cancellation token + // any exception can be thrown from StreamJsonRpc if JsonRpc is disposed in the middle of read/write. + // until we move to newly added cancellation support in JsonRpc, we will catch exception and translate to + // cancellation exception here. if any exception is thrown unrelated to cancellation, then we will rethrow + // the exception _cancellationToken.ThrowIfCancellationRequested(); throw; } @@ -61,12 +61,12 @@ public async Task InvokeAsync(string targetName, params object[] arguments { return await _rpc.InvokeAsync(targetName, arguments).ConfigureAwait(false); } - catch (ObjectDisposedException) + catch { - // object disposed exception can be thrown from StreamJsonRpc if JsonRpc is disposed in the middle of read/write. - // the way we added cancellation support to the JsonRpc which doesn't support cancellation natively - // can cause this exception to happen. newer version supports cancellation token natively, but - // we can't use it now, so we will catch object disposed exception and check cancellation token + // any exception can be thrown from StreamJsonRpc if JsonRpc is disposed in the middle of read/write. + // until we move to newly added cancellation support in JsonRpc, we will catch exception and translate to + // cancellation exception here. if any exception is thrown unrelated to cancellation, then we will rethrow + // the exception _cancellationToken.ThrowIfCancellationRequested(); throw; } From 3d11beecb074b6fe3c8f319564a8237d1cb5e5de Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Fri, 5 May 2017 15:24:51 -0700 Subject: [PATCH 049/284] made VS to crash explicitly when there is bad remote session consumer. --- .../Core/Portable/Execution/AssetStorages.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Execution/AssetStorages.cs b/src/Workspaces/Core/Portable/Execution/AssetStorages.cs index c3a8b75e4dfde..82f107f8e1507 100644 --- a/src/Workspaces/Core/Portable/Execution/AssetStorages.cs +++ b/src/Workspaces/Core/Portable/Execution/AssetStorages.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; @@ -156,13 +157,21 @@ public IReadOnlyDictionary GetRemotableData(PinnedRemot public void RegisterSnapshot(PinnedRemotableDataScope snapshot, AssetStorages.Storage storage) { // duplicates are not allowed, there can be multiple snapshots to same solution, so no ref counting. - Contract.ThrowIfFalse(_storages.TryAdd(snapshot, storage)); + if (!_storages.TryAdd(snapshot, storage)) + { + // this should make failure more explicit + FailFast.OnFatalException(new Exception("who is adding same snapshot?")); + } } public void UnregisterSnapshot(PinnedRemotableDataScope snapshot) { // calling it multiple times for same snapshot is not allowed. - Contract.ThrowIfFalse(_storages.TryRemove(snapshot, out var dummy)); + if (!_storages.TryRemove(snapshot, out var dummy)) + { + // this should make failure more explicit + FailFast.OnFatalException(new Exception("who is removing same snapshot?")); + } } private IEnumerable GetStorages(PinnedRemotableDataScope scope) From 5bdf617bb8a11241a37cea7ba3f4102d47c76d14 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Fri, 5 May 2017 17:21:19 -0700 Subject: [PATCH 050/284] send additional info when setting context for service call. now, it includes flag whether given solution is for primary workspace or not. this should benefit perf on initial solution load --- .../Core/Next/Remote/JsonRpcSession.cs | 4 +- .../Services/SolutionServiceTests.cs | 24 ++++++- .../Execution/PinnedRemotableDataScope.cs | 2 + .../Remote/Core/Services/SolutionService.cs | 70 +++++++++++-------- .../ServiceHub/Services/SnapshotService.cs | 4 +- .../Shared/ServiceHubServiceBase.cs | 8 ++- 6 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs index 95318f119232e..7d243c41766c5 100644 --- a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs +++ b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs @@ -89,10 +89,10 @@ private async Task InitializeAsync() // all roslyn remote service must based on ServiceHubServiceBase which implements Initialize method if (_snapshotClientOpt != null) { - await _snapshotClientOpt.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt.SolutionChecksum).ConfigureAwait(false); + await _snapshotClientOpt.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt.Primary, PinnedScopeOpt.SolutionChecksum).ConfigureAwait(false); } - await _serviceClient.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt?.SolutionChecksum).ConfigureAwait(false); + await _serviceClient.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt?.Primary, PinnedScopeOpt?.SolutionChecksum).ConfigureAwait(false); } public override Task InvokeAsync(string targetName, params object[] arguments) diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 3624063c1e2c8..0a59fa4789472 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -7,11 +7,9 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.LanguageServices.Remote; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Roslyn.VisualStudio.Next.UnitTests.Mocks; @@ -38,6 +36,28 @@ public async Task TestCreation() } } + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public async Task TestGetSolutionWithPrimaryFlag() + { + var code = @"class Test { void Method() { } }"; + + using (var workspace = TestWorkspace.CreateCSharp(code)) + { + var solution = workspace.CurrentSolution; + var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); + + var service1 = await GetSolutionServiceAsync(solution); + var synched1 = await service1.GetSolutionAsync(solutionChecksum, CancellationToken.None); + Assert.Equal(solutionChecksum, await synched1.State.GetChecksumAsync(CancellationToken.None)); + Assert.True(synched1.Workspace is TemporaryWorkspace); + + var service2 = await GetSolutionServiceAsync(solution); + var synched2 = await service2.GetSolutionAsync(solutionChecksum, primary: true, cancellationToken: CancellationToken.None); + Assert.Equal(solutionChecksum, await synched2.State.GetChecksumAsync(CancellationToken.None)); + Assert.True(synched2.Workspace is RemoteWorkspace); + } + } + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestStrongNameProvider() { diff --git a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs index be115ef2f7ffb..b318627aacac8 100644 --- a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs +++ b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs @@ -33,6 +33,8 @@ public PinnedRemotableDataScope( _storages.RegisterSnapshot(this, storage); } + public bool Primary => _storage.SolutionState.BranchId == Workspace.PrimaryBranchId; + public Workspace Workspace => _storage.SolutionState.Workspace; /// diff --git a/src/Workspaces/Remote/Core/Services/SolutionService.cs b/src/Workspaces/Remote/Core/Services/SolutionService.cs index 3dfaf89731005..efc54e2731825 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionService.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionService.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote @@ -30,12 +29,24 @@ public SolutionService(AssetService assetService) _assetService = assetService; } - public async Task GetSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken) + public Task GetSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken) + { + return GetSolutionAsync(solutionChecksum, primary: false, cancellationToken: cancellationToken); + } + + /// + /// This should be only consumed by engine, not by user. + /// + public async Task GetSolutionAsync(Checksum solutionChecksum, bool primary, CancellationToken cancellationToken) { var currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) - { - Debug.WriteLine("**** Cache Hit outside of lock " + (currentSolution.Workspace is RemoteWorkspace).ToString()); + { + if (!(currentSolution.Workspace is RemoteWorkspace)) + { + Debug.WriteLine("**** Cache Hit outside of lock false"); + } + return currentSolution; } @@ -45,17 +56,24 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, Cancella currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) { - Debug.WriteLine("**** Cache Hit inside of lock " + (currentSolution.Workspace is RemoteWorkspace).ToString()); + if (!(currentSolution.Workspace is RemoteWorkspace)) + { + Debug.WriteLine("**** Cache Hit outside of lock false"); + } + return currentSolution; } - var solution = await CreateSolution_NoLockAsync(solutionChecksum, s_primaryWorkspace.CurrentSolution, cancellationToken).ConfigureAwait(false); + var solution = await CreateSolution_NoLockAsync(solutionChecksum, primary, s_primaryWorkspace.CurrentSolution, cancellationToken).ConfigureAwait(false); s_lastSolution = Tuple.Create(solutionChecksum, solution); return solution; } } + /// + /// This should be only consumed by engine, not by user. + /// public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { var currentSolution = s_primaryWorkspace.CurrentSolution; @@ -70,18 +88,26 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - var solution = await UpdatePrimaryWorkspace_NoLockAsync(solutionChecksum, currentSolution, cancellationToken).ConfigureAwait(false); + var primary = true; + var solution = await CreateSolution_NoLockAsync(solutionChecksum, primary, currentSolution, cancellationToken).ConfigureAwait(false); s_primarySolution = Tuple.Create(solutionChecksum, solution); } } - private async Task CreateSolution_NoLockAsync(Checksum solutionChecksum, Solution baseSolution, CancellationToken cancellationToken) + private async Task CreateSolution_NoLockAsync(Checksum solutionChecksum, bool primary, Solution baseSolution, CancellationToken cancellationToken) { var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // solution has updated + if (primary) + { + Debug.WriteLine("**** Primary worksace incremental update!"); + s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); + return s_primaryWorkspace.CurrentSolution; + } + Debug.WriteLine("**** Incremental solution update!"); return await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); } @@ -89,32 +115,18 @@ private async Task CreateSolution_NoLockAsync(Checksum solutionChecksu // new solution. bulk sync all asset for the solution await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); - Debug.WriteLine("**** Not same as primary workspace! new temporary workspace created!"); - var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); - return workspace.CurrentSolution; - } - - private async Task UpdatePrimaryWorkspace_NoLockAsync(Checksum solutionChecksum, Solution baseSolution, CancellationToken cancellationToken) - { - var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); - - if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) + if (primary) { - // solution has updated - Debug.WriteLine("**** Primary worksace incremental update!"); - s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); + Debug.WriteLine("**** New Primary workspace!"); + s_primaryWorkspace.ClearSolution(); + s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); return s_primaryWorkspace.CurrentSolution; } - // new solution. bulk sync all asset for the solution - await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); - - Debug.WriteLine("**** New Primary workspace!"); - s_primaryWorkspace.ClearSolution(); - s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); - - return s_primaryWorkspace.CurrentSolution; + Debug.WriteLine("**** Not same as primary workspace! new temporary workspace created!"); + var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); + return workspace.CurrentSolution; } private static Solution GetAvailableSolution(Checksum solutionChecksum) diff --git a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.cs b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.cs index 0312f42226972..698bcb1569d61 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.cs @@ -27,9 +27,9 @@ public SnapshotService(Stream stream, IServiceProvider serviceProvider) : Rpc.StartListening(); } - public override void Initialize(int sessionId, byte[] solutionChecksum) + public override void Initialize(int sessionId, bool primary, Checksum solutionChecksum) { - base.Initialize(sessionId, solutionChecksum); + base.Initialize(sessionId, primary, solutionChecksum); lock (_gate) { diff --git a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs index 84549a8cfd7d1..051a27624a059 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs @@ -28,6 +28,7 @@ internal abstract class ServiceHubServiceBase : IDisposable protected readonly CancellationToken CancellationToken; private int _sessionId; + private bool _primary; private Checksum _solutionChecksumOpt; private RoslynServices _lazyRoslynServices; @@ -90,7 +91,7 @@ protected RoslynServices RoslynServices protected Task GetSolutionAsync() { Contract.ThrowIfNull(_solutionChecksumOpt); - return RoslynServices.SolutionService.GetSolutionAsync(_solutionChecksumOpt, CancellationToken); + return RoslynServices.SolutionService.GetSolutionAsync(_solutionChecksumOpt, _primary, CancellationToken); } protected virtual void Dispose(bool disposing) @@ -103,14 +104,15 @@ protected void LogError(string message) Log(TraceEventType.Error, message); } - public virtual void Initialize(int sessionId, byte[] solutionChecksum) + public virtual void Initialize(int sessionId, bool primary, Checksum solutionChecksum) { // set session related information _sessionId = sessionId; + _primary = primary; if (solutionChecksum != null) { - _solutionChecksumOpt = new Checksum(solutionChecksum); + _solutionChecksumOpt = solutionChecksum; } } From a260bc69f22cb020f463a3ff50bcd37278220e32 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Fri, 5 May 2017 17:50:20 -0700 Subject: [PATCH 051/284] removed loggings --- .../Remote/Core/Services/SolutionService.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Workspaces/Remote/Core/Services/SolutionService.cs b/src/Workspaces/Remote/Core/Services/SolutionService.cs index efc54e2731825..c9dd90db2359d 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionService.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionService.cs @@ -42,11 +42,6 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, bool pri var currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) { - if (!(currentSolution.Workspace is RemoteWorkspace)) - { - Debug.WriteLine("**** Cache Hit outside of lock false"); - } - return currentSolution; } @@ -56,11 +51,6 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, bool pri currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) { - if (!(currentSolution.Workspace is RemoteWorkspace)) - { - Debug.WriteLine("**** Cache Hit outside of lock false"); - } - return currentSolution; } @@ -81,8 +71,6 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell var primarySolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); if (primarySolutionChecksum == solutionChecksum) { - // nothing changed - Debug.WriteLine("**** Primary workspace up to date"); return; } @@ -103,12 +91,10 @@ private async Task CreateSolution_NoLockAsync(Checksum solutionChecksu // solution has updated if (primary) { - Debug.WriteLine("**** Primary worksace incremental update!"); s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); return s_primaryWorkspace.CurrentSolution; } - Debug.WriteLine("**** Incremental solution update!"); return await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); } @@ -117,14 +103,12 @@ private async Task CreateSolution_NoLockAsync(Checksum solutionChecksu if (primary) { - Debug.WriteLine("**** New Primary workspace!"); s_primaryWorkspace.ClearSolution(); s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); return s_primaryWorkspace.CurrentSolution; } - Debug.WriteLine("**** Not same as primary workspace! new temporary workspace created!"); var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); return workspace.CurrentSolution; } From 36c013bca3c99b7b05ad6bf51ccf9c1abd5f4b46 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Fri, 5 May 2017 18:07:41 -0700 Subject: [PATCH 052/284] removed logging --- .../RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs index 1142557513758..3071ba2917d1b 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs @@ -122,8 +122,6 @@ private async Task SynchronizePrimaryWorkspaceAsync(CancellationToken cancellati { var solution = _service.Workspace.CurrentSolution; var checksum = await solution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); - - Debug.WriteLine("**** Update Remote Primary Workspace"); await remoteHostClient.RunOnRemoteHostAsync( WellKnownRemoteHostServices.RemoteHostService, solution, From 763af90ee6088bf4e3529907c694f26aa3b0ce4a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sun, 7 May 2017 14:29:20 -0700 Subject: [PATCH 053/284] Place fields/properties based on surrounding assignments. --- .../GenerateVariable/GenerateVariableTests.cs | 64 ++++++++++++++++- ...tractGenerateVariableService.CodeAction.cs | 70 +++++++++---------- .../AbstractGenerateVariableService.State.cs | 47 ++++++++++--- 3 files changed, 132 insertions(+), 49 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs index 3825d90a66a7b..194f645264609 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs @@ -7245,8 +7245,8 @@ public Class() }", @"class Class { - private readonly int _foo; private readonly int _bar; + private readonly int _foo; public Class() { @@ -7276,6 +7276,68 @@ public Class() } public int Bar { get; } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestPlaceFieldBasedOnSurroundingStatements() + { + await TestInRegularAndScriptAsync( +@"class Class +{ + private int _foo; + private int _quux; + + public Class() + { + _foo = 0; + [|_bar|] = 1; + _quux = 2; + } +}", +@"class Class +{ + private int _foo; + private int _bar; + private int _quux; + + public Class() + { + _foo = 0; + _bar = 1; + _quux = 2; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestPlacePropertyBasedOnSurroundingStatements() + { + await TestInRegularAndScriptAsync( +@"class Class +{ + public int Foo { get; } + public int Quuz { get; } + + public Class() + { + Foo = 0; + [|Bar|] = 1; + Quux = 2; + } +}", +@"class Class +{ + public int Foo { get; } + public int Bar { get; } + public int Quuz { get; } + + public Class() + { + Foo = 0; + Bar = 1; + Quux = 2; + } }"); } } diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.CodeAction.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.CodeAction.cs index 22ddd456bf16a..17530d1326892 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.CodeAction.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.CodeAction.cs @@ -46,12 +46,18 @@ public GenerateVariableCodeAction( _equivalenceKey = Title; } - protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) + protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) { + var solution = _document.Project.Solution; var syntaxTree = _document.SyntaxTree; var generateUnsafe = _state.TypeMemberType.IsUnsafe() && !_state.IsContainedInUnsafeType; + var otions = new CodeGenerationOptions( + afterThisLocation: _state.AfterThisLocation, + beforeThisLocation: _state.BeforeThisLocation, + contextLocation: _state.IdentifierToken.GetLocation()); + if (_generateProperty) { var getAccessor = CreateAccessor(DetermineMaximalAccessibility(_state), cancellationToken); @@ -59,45 +65,35 @@ protected override async Task GetChangedDocumentAsync(CancellationToke ? null : CreateAccessor(DetermineMinimalAccessibility(_state), cancellationToken); - var result = await CodeGenerator.AddPropertyDeclarationAsync( - _document.Project.Solution, - _state.TypeToGenerateIn, - CodeGenerationSymbolFactory.CreatePropertySymbol( - attributes: default(ImmutableArray), - accessibility: DetermineMaximalAccessibility(_state), - modifiers: new DeclarationModifiers(isStatic: _state.IsStatic, isUnsafe: generateUnsafe), - type: _state.TypeMemberType, - returnsByRef: _returnsByRef, - explicitInterfaceSymbol: null, - name: _state.IdentifierToken.ValueText, - isIndexer: _state.IsIndexer, - parameters: _state.Parameters, - getMethod: getAccessor, - setMethod: setAccessor), - new CodeGenerationOptions(contextLocation: _state.IdentifierToken.GetLocation()), - cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return result; + var propertySymbol = CodeGenerationSymbolFactory.CreatePropertySymbol( + attributes: default(ImmutableArray), + accessibility: DetermineMaximalAccessibility(_state), + modifiers: new DeclarationModifiers(isStatic: _state.IsStatic, isUnsafe: generateUnsafe), + type: _state.TypeMemberType, + returnsByRef: _returnsByRef, + explicitInterfaceSymbol: null, + name: _state.IdentifierToken.ValueText, + isIndexer: _state.IsIndexer, + parameters: _state.Parameters, + getMethod: getAccessor, + setMethod: setAccessor); + + return CodeGenerator.AddPropertyDeclarationAsync( + solution, _state.TypeToGenerateIn, propertySymbol, otions, cancellationToken); } else { - var result = await CodeGenerator.AddFieldDeclarationAsync( - _document.Project.Solution, - _state.TypeToGenerateIn, - CodeGenerationSymbolFactory.CreateFieldSymbol( - attributes: default(ImmutableArray), - accessibility: DetermineMinimalAccessibility(_state), - modifiers: _isConstant ? - new DeclarationModifiers(isConst: true, isUnsafe: generateUnsafe) : - new DeclarationModifiers(isStatic: _state.IsStatic, isReadOnly: _isReadonly, isUnsafe: generateUnsafe), - type: _state.TypeMemberType, - name: _state.IdentifierToken.ValueText), - new CodeGenerationOptions(contextLocation: _state.IdentifierToken.GetLocation()), - cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return result; + var fieldSymbol = CodeGenerationSymbolFactory.CreateFieldSymbol( + attributes: default(ImmutableArray), + accessibility: DetermineMinimalAccessibility(_state), + modifiers: _isConstant + ? new DeclarationModifiers(isConst: true, isUnsafe: generateUnsafe) + : new DeclarationModifiers(isStatic: _state.IsStatic, isReadOnly: _isReadonly, isUnsafe: generateUnsafe), + type: _state.TypeMemberType, + name: _state.IdentifierToken.ValueText); + + return CodeGenerator.AddFieldDeclarationAsync( + solution, _state.TypeToGenerateIn, fieldSymbol, otions, cancellationToken); } } diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 1c6d4b677cfd6..563127fbd0a6a 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -50,6 +50,9 @@ private partial class State public bool IsInExecutableBlock { get; private set; } public bool IsInConditionalAccessExpression { get; private set; } + public Location AfterThisLocation { get; private set; } + public Location BeforeThisLocation { get; private set; } + public static async Task GenerateAsync( TService service, SemanticDocument document, @@ -256,14 +259,23 @@ private bool TryInitializeSimpleName( this.IsInMemberContext = this.SimpleNameOpt != this.SimpleNameOrMemberAccessExpressionOpt || syntaxFacts.IsObjectInitializerNamedAssignmentIdentifier(this.SimpleNameOrMemberAccessExpressionOpt); - this.OfferReadOnlyFieldFirst = DetermineReadOnlyFirst(document, SymbolKind.Field, cancellationToken); + CheckSurroundingContext(document, SymbolKind.Field, cancellationToken); + CheckSurroundingContext(document, SymbolKind.Property, cancellationToken); return true; } - private bool DetermineReadOnlyFirst( + private void CheckSurroundingContext( SemanticDocument document, SymbolKind symbolKind, CancellationToken cancellationToken) { + // See if we're being assigned to. If so, look at the before/after statements + // to see if either is an assignment. If so, we can use that to try to determine + // user patterns that can be used when generating the member. For example, + // if the sibling assignment is to a readonly field, then we want to offer to + // generate a readonly field vs a writable field. + // + // Also, because users often like to keep members/assignments in the same order + // we can pick a good place for the new member based on the surrounding assignments. var syntaxFacts = document.Document.GetLanguageService(); var simpleName = this.SimpleNameOpt; @@ -280,22 +292,30 @@ private bool DetermineReadOnlyFirst( var children = block.ChildNodesAndTokens(); var statementindex = GetStatementindex(children, assignmentStatement); - return IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex - 1, cancellationToken) || - IsReadOnlyMemberAssignment(document, symbolKind, children, statementindex + 1, cancellationToken); + + var previousAssignedSymbol = TryGetAssignedSymbol(document, symbolKind, children, statementindex - 1, cancellationToken); + var nextAssignedSymbol = TryGetAssignedSymbol(document, symbolKind, children, statementindex + 1, cancellationToken); + + if (symbolKind == SymbolKind.Field) + { + this.OfferReadOnlyFieldFirst = IsReadOnly(previousAssignedSymbol) || + IsReadOnly(nextAssignedSymbol); + } + + this.AfterThisLocation = this.AfterThisLocation ?? previousAssignedSymbol?.Locations.FirstOrDefault(); + this.BeforeThisLocation = this.BeforeThisLocation ?? nextAssignedSymbol?.Locations.FirstOrDefault(); } } } - - return false; } - private bool IsReadOnlyMemberAssignment( - SemanticDocument document, SymbolKind symbolKind, + private ISymbol TryGetAssignedSymbol( + SemanticDocument document, SymbolKind symbolKind, ChildSyntaxList children, int index, CancellationToken cancellationToken) { var syntaxFacts = document.Document.GetLanguageService(); - if (index >= 0 && index < children.Count) + if (index >= 0 && index < children.Count) { var sibling = children[index]; if (sibling.IsNode) @@ -310,17 +330,22 @@ private bool IsReadOnlyMemberAssignment( if (symbol?.Kind == symbolKind && symbol.ContainingType.Equals(this.ContainingType)) { - return IsReadOnly(symbol); + return symbol; } } } } - return false; + return null; } private bool IsReadOnly(ISymbol symbol) { + if (symbol == null) + { + return false; + } + switch (symbol) { case IFieldSymbol field: return field.IsReadOnly; From 4e94fcddd57cdcfb70be8ebbaa004cdce065bac9 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sun, 7 May 2017 14:37:08 -0700 Subject: [PATCH 054/284] Add VB tests. --- .../GenerateVariable/GenerateVariableTests.vb | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb index 614172cce8c76..f7e55ce25e732 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb @@ -2596,13 +2596,65 @@ end class") end sub end class", "class C - private readonly _foo as integer Private ReadOnly _bar As Integer + private readonly _foo as integer public sub new() _bar = 1 _foo = 0 end sub +end class") + End Function + + + Public Async Function TestPlaceFieldBasedOnSurroundingStatements() As Task + await TestInRegularAndScriptAsync( +"class Class + private _foo as integer + private _quux as integer + + public sub new() + _foo = 0 + [|_bar|] = 1 + _quux = 2 + end sub +end class", +"class Class + private _foo as integer + Private _bar As Integer + private _quux as integer + + public sub new() + _foo = 0 + _bar = 1 + _quux = 2 + end sub +end class") + End Function + + + Public Async Function TestPlacePropertyBasedOnSurroundingStatements() As Task + Await TestInRegularAndScriptAsync( +"class Class + public readonly property Foo as integer + public readonly property Quux as integer + + public sub new() + Foo = 0 + [|Bar|] = 1 + Quux = 2 + end sub +end class", +"class Class + public readonly property Foo as integer + Public ReadOnly Property Bar As Integer + public readonly property Quux as integer + + public sub new() + Foo = 0 + Bar = 1 + Quux = 2 + end sub end class") End Function End Class From 72df26012c8c7cd18d142d1450c5aca0e80c9c47 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 8 May 2017 14:29:00 -0700 Subject: [PATCH 055/284] Skip a bunch of failing tests due to the API change and fix an existing test to account for the change --- .../Test/Semantic/Diagnostics/OperationAnalyzerTests.cs | 5 ++--- .../Test/Semantic/Diagnostics/OperationAnalyzerTests.vb | 4 ++-- .../QualifyMemberAccess/QualifyMemberAccessTests.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs index 68664fd6013fe..b4d4881fd1443 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs @@ -1121,7 +1121,7 @@ public override void M1() ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/18839")] public void EventAndMethodReferencesCSharp() { const string source = @" @@ -1440,8 +1440,7 @@ public void OnMumble(System.EventArgs args) Diagnostic(LambdaTestAnalyzer.LambdaExpressionDescriptor.Id, "input => input++").WithLocation(9, 31), Diagnostic(LambdaTestAnalyzer.LambdaExpressionDescriptor.Id, "input => { input++; input++; if (input > 0) return true; return false; }").WithLocation(10, 32), Diagnostic(LambdaTestAnalyzer.TooManyStatementsInLambdaExpressionDescriptor.Id, "input => { input++; input++; if (input > 0) return true; return false; }").WithLocation(10, 32), - // Bug: missing a Lambda expression in delegate creation https://github.com/dotnet/roslyn/issues/8347 - //Diagnostic(LambdaTestAnalyzer.LambdaExpressionDescriptor.Id, "(s, e) => { }").WithLocation(22, 42), + Diagnostic(LambdaTestAnalyzer.LambdaExpressionDescriptor.Id, "(s, e) => { }").WithLocation(22, 42), Diagnostic(LambdaTestAnalyzer.LambdaExpressionDescriptor.Id, "(s, e) => { int i = 0; i++; i++; i++; }").WithLocation(23, 19), Diagnostic(LambdaTestAnalyzer.TooManyStatementsInLambdaExpressionDescriptor.Id, "(s, e) => { int i = 0; i++; i++; i++; }").WithLocation(23, 19)); } diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb index 055d69d51c90e..94a2286990644 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb @@ -1138,7 +1138,7 @@ End Class Diagnostic(ExplicitVsImplicitInstanceAnalyzer.ImplicitInstanceDescriptor.Id, "M2").WithLocation(15, 9)) End Sub - + Public Sub EventAndMethodReferencesVisualBasic() Dim source = @@ -1411,7 +1411,7 @@ End Class End Sub - + Public Sub StaticMemberReferenceVisualBasic() Dim source = diff --git a/src/EditorFeatures/CSharpTest/QualifyMemberAccess/QualifyMemberAccessTests.cs b/src/EditorFeatures/CSharpTest/QualifyMemberAccess/QualifyMemberAccessTests.cs index 8af4b22df59c6..b430e2d6bc90e 100644 --- a/src/EditorFeatures/CSharpTest/QualifyMemberAccess/QualifyMemberAccessTests.cs +++ b/src/EditorFeatures/CSharpTest/QualifyMemberAccess/QualifyMemberAccessTests.cs @@ -978,7 +978,7 @@ void M() } [WorkItem(15325, "https://github.com/dotnet/roslyn/issues/15325")] - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/18839"), Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)] public async Task QualifyInstanceMethodInDelegateCreation() { await TestAsyncWithOption( From 0a6697e319ea07ada7d0addfdd4db05b8707b195 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 17:15:51 -0700 Subject: [PATCH 056/284] Handle this. expressions. --- .../GenerateVariable/GenerateVariableTests.cs | 31 +++++++++++++++++++ .../AbstractGenerateVariableService.State.cs | 5 +-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs index 194f645264609..74f87d68407cf 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateVariable/GenerateVariableTests.cs @@ -7310,6 +7310,37 @@ public Class() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] + public async Task TestPlaceFieldBasedOnSurroundingStatements2() + { + await TestInRegularAndScriptAsync( +@"class Class +{ + private int foo; + private int quux; + + public Class() + { + this.foo = 0; + this.[|bar|] = 1; + this.quux = 2; + } +}", +@"class Class +{ + private int foo; + private int bar; + private int quux; + + public Class() + { + this.foo = 0; + this.bar = 1; + this.quux = 2; + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)] public async Task TestPlacePropertyBasedOnSurroundingStatements() { diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 563127fbd0a6a..0009ba02bf2eb 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -277,14 +277,15 @@ private void CheckSurroundingContext( // Also, because users often like to keep members/assignments in the same order // we can pick a good place for the new member based on the surrounding assignments. var syntaxFacts = document.Document.GetLanguageService(); - var simpleName = this.SimpleNameOpt; + var simpleName = this.SimpleNameOrMemberAccessExpressionOpt; if (syntaxFacts.IsLeftSideOfAssignment(simpleName)) { var assignmentStatement = simpleName.Ancestors().FirstOrDefault(syntaxFacts.IsSimpleAssignmentStatement); if (assignmentStatement != null) { - syntaxFacts.GetPartsOfAssignmentStatement(assignmentStatement, out var left, out var right); + syntaxFacts.GetPartsOfAssignmentStatement( + assignmentStatement, out var left, out var right); if (left == simpleName) { From a015d76f7dc0fb048571da1cc998a5ce7bea42a6 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 17:17:34 -0700 Subject: [PATCH 057/284] Simplify code. --- .../AbstractGenerateVariableService.State.cs | 37 +++---------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 0009ba02bf2eb..9339a8f4b1f30 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -292,15 +292,15 @@ private void CheckSurroundingContext( var block = assignmentStatement.Parent; var children = block.ChildNodesAndTokens(); - var statementindex = GetStatementindex(children, assignmentStatement); + var statementindex = GetStatementIndex(children, assignmentStatement); var previousAssignedSymbol = TryGetAssignedSymbol(document, symbolKind, children, statementindex - 1, cancellationToken); var nextAssignedSymbol = TryGetAssignedSymbol(document, symbolKind, children, statementindex + 1, cancellationToken); if (symbolKind == SymbolKind.Field) { - this.OfferReadOnlyFieldFirst = IsReadOnly(previousAssignedSymbol) || - IsReadOnly(nextAssignedSymbol); + this.OfferReadOnlyFieldFirst = FieldIsReadOnly(previousAssignedSymbol) || + FieldIsReadOnly(nextAssignedSymbol); } this.AfterThisLocation = this.AfterThisLocation ?? previousAssignedSymbol?.Locations.FirstOrDefault(); @@ -340,35 +340,10 @@ private ISymbol TryGetAssignedSymbol( return null; } - private bool IsReadOnly(ISymbol symbol) - { - if (symbol == null) - { - return false; - } - - switch (symbol) - { - case IFieldSymbol field: return field.IsReadOnly; - case IPropertySymbol property: - if (property.SetMethod == null) - { - return true; - } - - if (property.ContainingType.GetMembers().OfType().Any( - f => property.SetMethod.Equals(f.AssociatedSymbol))) - { - return true; - } - - return false; - } - - throw ExceptionUtilities.Unreachable; - } + private bool FieldIsReadOnly(ISymbol symbol) + => symbol is IFieldSymbol field && field.IsReadOnly; - private int GetStatementindex(ChildSyntaxList children, SyntaxNode statement) + private int GetStatementIndex(ChildSyntaxList children, SyntaxNode statement) { var index = 0; foreach (var child in children) From 8aa521fea4de5ed4792854e32b81c9bc1c44de11 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 17:54:23 -0700 Subject: [PATCH 058/284] Do not even attempt to create indices for non-C#/VB projects. --- .../SymbolTreeInfoIncrementalAnalyzerProvider.cs | 8 ++++++-- .../SyntaxTreeInfoIncrementalAnalyzerProvider.cs | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 9f730f3180c94..6b961a156ffdf 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Composition; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,7 +12,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.NavigateTo; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; @@ -181,6 +179,12 @@ public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, private async Task UpdateSymbolTreeInfoAsync(Project project, CancellationToken cancellationToken) { + if (!project.SupportsCompilation) + { + // Not a language we can produce indices for (i.e. TypeScript). Bail immediately. + return; + } + if (project.Solution.Workspace.Kind != "Test" && project.Solution.Workspace.Kind != WorkspaceKind.RemoteWorkspace && project.Solution.Workspace.Options.GetOption(NavigateToOptions.OutOfProcessAllowed)) diff --git a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs index 9f3f4dec0ba5a..d9e183360f303 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs @@ -21,6 +21,12 @@ private class IncrementalAnalyzer : IncrementalAnalyzerBase { public override Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) { + if (!document.SupportsSyntaxTree) + { + // Not a language we can produce indices for (i.e. TypeScript). Bail immediately. + return SpecializedTasks.EmptyTask; + } + if (document.Project.Solution.Workspace.Kind != WorkspaceKind.RemoteWorkspace && document.Project.Solution.Workspace.Options.GetOption(SymbolFinderOptions.OutOfProcessAllowed)) { From 435ee7dc08cbefb1c1b450ec0ba0901cd95b26a8 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 8 May 2017 18:05:28 -0700 Subject: [PATCH 059/284] Refactored IErrorReportingService to extract the info bar display logic into IInfoBarService --- src/EditorFeatures/Core/EditorFeatures.csproj | 1 + .../EditorLayerExtensionManager.cs | 8 +- .../Workspaces/EditorErrorReportingService.cs | 4 +- .../Workspaces/EditorInfoBarService.cs | 23 ++ ...tServiceFactory.RemoteHostClientService.cs | 6 +- .../VirtualMemoryNotificationListener.cs | 4 +- .../VisualStudioErrorReportingService.cs | 186 +--------------- ...isualStudioErrorReportingServiceFactory.cs | 13 +- .../Workspace/VisualStudioInfoBarService.cs | 200 ++++++++++++++++++ .../Core/Def/ServicesVisualStudio.csproj | 1 + .../IErrorReportingService.cs | 36 +--- .../ExtensionManager/IInfoBarService.cs | 52 +++++ .../Core/Portable/Log/FunctionId.cs | 1 + .../Core/Portable/Workspaces.csproj | 1 + 14 files changed, 303 insertions(+), 233 deletions(-) create mode 100644 src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs create mode 100644 src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs create mode 100644 src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 3a5b371a27e54..521cb964a62df 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -168,6 +168,7 @@ + diff --git a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs index 39a94d1e9b8df..019533afd41bf 100644 --- a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs +++ b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs @@ -70,10 +70,10 @@ public override void HandleException(object provider, Exception exception) base.HandleException(provider, exception); _errorReportingService?.ShowErrorInfoInActiveView(String.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, provider.GetType().Name), - new ErrorReportingUI(WorkspacesResources.Show_Stack_Trace, ErrorReportingUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false), - new ErrorReportingUI(WorkspacesResources.Enable, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), - new ErrorReportingUI(WorkspacesResources.Enable_and_ignore_future_errors, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), - new ErrorReportingUI(String.Empty, ErrorReportingUI.UIKind.Close, () => LogLeaveDisabled(provider))); + new InfoBarUI(WorkspacesResources.Show_Stack_Trace, InfoBarUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false), + new InfoBarUI(WorkspacesResources.Enable, InfoBarUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), + new InfoBarUI(WorkspacesResources.Enable_and_ignore_future_errors, InfoBarUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), + new InfoBarUI(String.Empty, InfoBarUI.UIKind.Close, () => LogLeaveDisabled(provider))); } else { diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs index fee9ec725e34a..c460c94a5c42d 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs @@ -13,12 +13,12 @@ public void ShowDetailedErrorInfo(Exception exception) Logger.Log(FunctionId.Extension_Exception, exception.StackTrace); } - public void ShowErrorInfoInActiveView(string message, params ErrorReportingUI[] items) + public void ShowErrorInfoInActiveView(string message, params InfoBarUI[] items) { ShowGlobalErrorInfo(message, items); } - public void ShowGlobalErrorInfo(string message, params ErrorReportingUI[] items) + public void ShowGlobalErrorInfo(string message, params InfoBarUI[] items) { Logger.Log(FunctionId.Extension_Exception, message); } diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs new file mode 100644 index 0000000000000..5c98212530912 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Internal.Log; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces +{ + internal class EditorInfoBarService : IInfoBarService + { + public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items) + { + ShowInfoBarInGlobalView(message, items); + } + + public void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items) + { + Logger.Log(FunctionId.Extension_InfoBar, message); + } + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index be22435e65026..97eb651b02ccb 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -251,10 +251,10 @@ private void OnConnectionChanged(object sender, bool connected) FatalError.ReportWithoutCrash(new Exception("Connection to remote host closed")); // use info bar to show warning to users - var infoBarUIs = new List(); + var infoBarUIs = new List(); infoBarUIs.Add( - new ErrorReportingUI(ServicesVSResources.Learn_more, ErrorReportingUI.UIKind.HyperLink, () => + new InfoBarUI(ServicesVSResources.Learn_more, InfoBarUI.UIKind.HyperLink, () => BrowserHelper.StartBrowser(new Uri(OOPKilledMoreInfoLink)), closeAfterAction: false)); var allowRestarting = _workspace.Options.GetOption(RemoteHostOptions.RestartRemoteHostAllowed); @@ -263,7 +263,7 @@ private void OnConnectionChanged(object sender, bool connected) // this is hidden restart option. by default, user can't restart remote host that got killed // by users infoBarUIs.Add( - new ErrorReportingUI("Restart external process", ErrorReportingUI.UIKind.Button, () => + new InfoBarUI("Restart external process", InfoBarUI.UIKind.Button, () => { // start off new remote host var unused = RequestNewRemoteHostAsync(CancellationToken.None); diff --git a/src/VisualStudio/Core/Def/Implementation/VirtualMemoryNotificationListener.cs b/src/VisualStudio/Core/Def/Implementation/VirtualMemoryNotificationListener.cs index 8c9e6897dd3a9..f188ccac7325f 100644 --- a/src/VisualStudio/Core/Def/Implementation/VirtualMemoryNotificationListener.cs +++ b/src/VisualStudio/Core/Def/Implementation/VirtualMemoryNotificationListener.cs @@ -95,9 +95,9 @@ public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam) _workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown, true); _workspace.Services.GetService().ShowGlobalErrorInfo(ServicesVSResources.Visual_Studio_has_suspended_some_advanced_features_to_improve_performance, - new ErrorReportingUI(ServicesVSResources.Re_enable, ErrorReportingUI.UIKind.Button, () => + new InfoBarUI(ServicesVSResources.Re_enable, InfoBarUI.UIKind.Button, () => _workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true)), - new ErrorReportingUI(ServicesVSResources.Learn_more, ErrorReportingUI.UIKind.HyperLink, () => + new InfoBarUI(ServicesVSResources.Learn_more, InfoBarUI.UIKind.HyperLink, () => BrowserHelper.StartBrowser(new Uri(LowVMMoreInfoLink)), closeAfterAction: false)); } } diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs index ec8ce0d50562b..d443933dc82ba 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs @@ -1,199 +1,27 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.Imaging; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation { internal partial class VisualStudioErrorReportingService : IErrorReportingService { - private readonly static InfoBarButton s_enableItem = new InfoBarButton(ServicesVSResources.Enable); - private readonly static InfoBarButton s_enableAndIgnoreItem = new InfoBarButton(ServicesVSResources.Enable_and_ignore_future_errors); + private readonly IInfoBarService _infoBarService; - private readonly IServiceProvider _serviceProvider; - private readonly IForegroundNotificationService _foregroundNotificationService; - private readonly IAsynchronousOperationListener _listener; - - public VisualStudioErrorReportingService( - SVsServiceProvider serviceProvider, IForegroundNotificationService foregroundNotificationService, IAsynchronousOperationListener listener) - { - _serviceProvider = serviceProvider; - _foregroundNotificationService = foregroundNotificationService; - _listener = listener; - } - - public void ShowErrorInfoInActiveView(string message, params ErrorReportingUI[] items) - { - ShowErrorInfo(activeView: true, message: message, items: items); - } - - public void ShowGlobalErrorInfo(string message, params ErrorReportingUI[] items) - { - ShowErrorInfo(activeView: false, message: message, items: items); - } - - public void ShowErrorInfo(bool activeView, string message, params ErrorReportingUI[] items) - { - // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. - _foregroundNotificationService.RegisterNotification(() => - { - if (TryGetInfoBarData(activeView, out var infoBarHost)) - { - CreateInfoBar(infoBarHost, message, items); - } - }, _listener.BeginAsyncOperation("Show InfoBar")); - } - - private bool TryGetInfoBarData(bool activeView, out IVsInfoBarHost infoBarHost) - { - infoBarHost = null; - - if (activeView) - { - var monitorSelectionService = _serviceProvider.GetService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection; - - // We want to get whichever window is currently in focus (including toolbars) as we could have had an exception thrown from the error list - // or interactive window - if (monitorSelectionService == null || - ErrorHandler.Failed(monitorSelectionService.GetCurrentElementValue((uint)VSConstants.VSSELELEMID.SEID_WindowFrame, out var value))) - { - return false; - } - - var frame = value as IVsWindowFrame; - if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out var activeViewInfoBar))) - { - return false; - } - - infoBarHost = activeViewInfoBar as IVsInfoBarHost; - return infoBarHost != null; - } - - // global error info, show it on main window info bar - var shell = _serviceProvider.GetService(typeof(SVsShell)) as IVsShell; - if (shell == null || - ErrorHandler.Failed(shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var globalInfoBar))) - { - return false; - } - - infoBarHost = globalInfoBar as IVsInfoBarHost; - return infoBarHost != null; - } - - private void CreateInfoBar(IVsInfoBarHost infoBarHost, string message, ErrorReportingUI[] items) + public VisualStudioErrorReportingService(IInfoBarService infoBarService) { - var factory = _serviceProvider.GetService(typeof(SVsInfoBarUIFactory)) as IVsInfoBarUIFactory; - if (factory == null) - { - // no info bar factory, don't do anything - return; - } - - var textSpans = new List() - { - new InfoBarTextSpan(message) - }; - - // create action item list - var actionItems = new List(); - - foreach (var item in items) - { - switch (item.Kind) - { - case ErrorReportingUI.UIKind.Button: - actionItems.Add(new InfoBarButton(item.Title)); - break; - case ErrorReportingUI.UIKind.HyperLink: - actionItems.Add(new InfoBarHyperlink(item.Title)); - break; - case ErrorReportingUI.UIKind.Close: - break; - default: - throw ExceptionUtilities.UnexpectedValue(item.Kind); - } - } - - var infoBarModel = new InfoBarModel( - textSpans, - actionItems.ToArray(), - KnownMonikers.StatusInformation, - isCloseButtonVisible: true); - - if (!TryCreateInfoBarUI(factory, infoBarModel, out var infoBarUI)) - { - return; - } - - uint? infoBarCookie = null; - var eventSink = new InfoBarEvents(items, () => - { - // run given onClose action if there is one. - items.FirstOrDefault(i => i.Kind == ErrorReportingUI.UIKind.Close).Action?.Invoke(); - - if (infoBarCookie.HasValue) - { - infoBarUI.Unadvise(infoBarCookie.Value); - } - }); - - infoBarUI.Advise(eventSink, out var cookie); - infoBarCookie = cookie; - - infoBarHost.AddInfoBar(infoBarUI); + _infoBarService = infoBarService; } - private class InfoBarEvents : IVsInfoBarUIEvents + public void ShowErrorInfoInActiveView(string message, params InfoBarUI[] items) { - private readonly ErrorReportingUI[] _items; - private readonly Action _onClose; - - public InfoBarEvents(ErrorReportingUI[] items, Action onClose) - { - Contract.ThrowIfNull(onClose); - - _items = items; - _onClose = onClose; - } - - public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem) - { - var item = _items.FirstOrDefault(i => i.Title == actionItem.Text); - if (item.IsDefault) - { - return; - } - - item.Action?.Invoke(); - - if (!item.CloseAfterAction) - { - return; - } - - infoBarUIElement.Close(); - } - - public void OnClosed(IVsInfoBarUIElement infoBarUIElement) - { - _onClose(); - } + _infoBarService.ShowInfoBarInActiveView(message, items); } - private static bool TryCreateInfoBarUI(IVsInfoBarUIFactory infoBarUIFactory, IVsInfoBar infoBar, out IVsInfoBarUIElement uiElement) + public void ShowGlobalErrorInfo(string message, params InfoBarUI[] items) { - uiElement = infoBarUIFactory.CreateInfoBar(infoBar); - return uiElement != null; + _infoBarService.ShowInfoBarInGlobalView(message, items); } public void ShowDetailedErrorInfo(Exception exception) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs index af1a7340912d3..4005022fc4ce1 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs @@ -1,15 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; using System.Composition; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.Implementation { @@ -19,12 +13,9 @@ internal sealed class VisualStudioErrorReportingServiceFactory : IWorkspaceServi private readonly IErrorReportingService _singleton; [ImportingConstructor] - public VisualStudioErrorReportingServiceFactory( - SVsServiceProvider serviceProvider, - IForegroundNotificationService foregroundNotificationService, - [ImportMany] IEnumerable> asyncListeners) + public VisualStudioErrorReportingServiceFactory(IInfoBarService infoBarService) { - _singleton = new VisualStudioErrorReportingService(serviceProvider, foregroundNotificationService, new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.InfoBar)); + _singleton = new VisualStudioErrorReportingService(infoBarService); } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs new file mode 100644 index 0000000000000..eac76f3fe4172 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Imaging; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation +{ + [Export(typeof(IInfoBarService))] + internal class VisualStudioInfoBarService : IInfoBarService + { + private readonly IServiceProvider _serviceProvider; + private readonly IForegroundNotificationService _foregroundNotificationService; + private readonly IAsynchronousOperationListener _listener; + + [ImportingConstructor] + public VisualStudioInfoBarService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, + IForegroundNotificationService foregroundNotificationService, + [ImportMany] IEnumerable> asyncListeners) + { + _serviceProvider = serviceProvider; + _foregroundNotificationService = foregroundNotificationService; + _listener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.InfoBar); + } + + public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items) + { + ShowInfoBar(activeView: true, message: message, items: items); + } + + public void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items) + { + ShowInfoBar(activeView: false, message: message, items: items); + } + + private void ShowInfoBar(bool activeView, string message, params InfoBarUI[] items) + { + // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. + _foregroundNotificationService.RegisterNotification(() => + { + if (TryGetInfoBarData(activeView, out var infoBarHost)) + { + CreateInfoBar(infoBarHost, message, items); + } + }, _listener.BeginAsyncOperation("Show InfoBar")); + } + + private bool TryGetInfoBarData(bool activeView, out IVsInfoBarHost infoBarHost) + { + infoBarHost = null; + + if (activeView) + { + var monitorSelectionService = _serviceProvider.GetService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection; + + // We want to get whichever window is currently in focus (including toolbars) as we could have had an exception thrown from the error list + // or interactive window + if (monitorSelectionService == null || + ErrorHandler.Failed(monitorSelectionService.GetCurrentElementValue((uint)VSConstants.VSSELELEMID.SEID_WindowFrame, out var value))) + { + return false; + } + + var frame = value as IVsWindowFrame; + if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out var activeViewInfoBar))) + { + return false; + } + + infoBarHost = activeViewInfoBar as IVsInfoBarHost; + return infoBarHost != null; + } + + // global error info, show it on main window info bar + var shell = _serviceProvider.GetService(typeof(SVsShell)) as IVsShell; + if (shell == null || + ErrorHandler.Failed(shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var globalInfoBar))) + { + return false; + } + + infoBarHost = globalInfoBar as IVsInfoBarHost; + return infoBarHost != null; + } + + private void CreateInfoBar(IVsInfoBarHost infoBarHost, string message, InfoBarUI[] items) + { + var factory = _serviceProvider.GetService(typeof(SVsInfoBarUIFactory)) as IVsInfoBarUIFactory; + if (factory == null) + { + // no info bar factory, don't do anything + return; + } + + var textSpans = new List() + { + new InfoBarTextSpan(message) + }; + + // create action item list + var actionItems = new List(); + + foreach (var item in items) + { + switch (item.Kind) + { + case InfoBarUI.UIKind.Button: + actionItems.Add(new InfoBarButton(item.Title)); + break; + case InfoBarUI.UIKind.HyperLink: + actionItems.Add(new InfoBarHyperlink(item.Title)); + break; + case InfoBarUI.UIKind.Close: + break; + default: + throw ExceptionUtilities.UnexpectedValue(item.Kind); + } + } + + var infoBarModel = new InfoBarModel( + textSpans, + actionItems.ToArray(), + KnownMonikers.StatusInformation, + isCloseButtonVisible: true); + + if (!TryCreateInfoBarUI(factory, infoBarModel, out var infoBarUI)) + { + return; + } + + uint? infoBarCookie = null; + var eventSink = new InfoBarEvents(items, () => + { + // run given onClose action if there is one. + items.FirstOrDefault(i => i.Kind == InfoBarUI.UIKind.Close).Action?.Invoke(); + + if (infoBarCookie.HasValue) + { + infoBarUI.Unadvise(infoBarCookie.Value); + } + }); + + infoBarUI.Advise(eventSink, out var cookie); + infoBarCookie = cookie; + + infoBarHost.AddInfoBar(infoBarUI); + } + + private class InfoBarEvents : IVsInfoBarUIEvents + { + private readonly InfoBarUI[] _items; + private readonly Action _onClose; + + public InfoBarEvents(InfoBarUI[] items, Action onClose) + { + Contract.ThrowIfNull(onClose); + + _items = items; + _onClose = onClose; + } + + public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem) + { + var item = _items.FirstOrDefault(i => i.Title == actionItem.Text); + if (item.IsDefault) + { + return; + } + + item.Action?.Invoke(); + + if (!item.CloseAfterAction) + { + return; + } + + infoBarUIElement.Close(); + } + + public void OnClosed(IVsInfoBarUIElement infoBarUIElement) + { + _onClose(); + } + } + + private static bool TryCreateInfoBarUI(IVsInfoBarUIFactory infoBarUIFactory, IVsInfoBar infoBar, out IVsInfoBarUIElement uiElement) + { + uiElement = infoBarUIFactory.CreateInfoBar(infoBar); + return uiElement != null; + } + } +} diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 1809a21b16135..bad1214edf92c 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -168,6 +168,7 @@ + diff --git a/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs b/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs index dfa4803ce26c2..4d5b1138c2bfa 100644 --- a/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs +++ b/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs @@ -2,7 +2,6 @@ using System; using Microsoft.CodeAnalysis.Host; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Extensions { @@ -10,46 +9,19 @@ internal interface IErrorReportingService : IWorkspaceService { /// /// Show error info in an active view. - /// + /// /// Different host can have different definition on what active view means. /// - void ShowErrorInfoInActiveView(string message, params ErrorReportingUI[] items); + void ShowErrorInfoInActiveView(string message, params InfoBarUI[] items); /// /// Show global error info. - /// + /// /// this kind error info should be something that affects whole roslyn such as /// background compilation is disabled due to memory issue and etc /// - void ShowGlobalErrorInfo(string message, params ErrorReportingUI[] items); + void ShowGlobalErrorInfo(string message, params InfoBarUI[] items); void ShowDetailedErrorInfo(Exception exception); } - - internal struct ErrorReportingUI - { - public readonly string Title; - public readonly UIKind Kind; - public readonly Action Action; - public readonly bool CloseAfterAction; - - public ErrorReportingUI(string title, UIKind kind, Action action, bool closeAfterAction = true) - { - Contract.ThrowIfNull(title); - - Title = title; - Kind = kind; - Action = action; - CloseAfterAction = closeAfterAction; - } - - public bool IsDefault => Title == null; - - internal enum UIKind - { - Button, - HyperLink, - Close - } - } } diff --git a/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs b/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs new file mode 100644 index 0000000000000..789b0a53c87be --- /dev/null +++ b/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.CodeAnalysis.Host; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Extensions +{ + internal interface IInfoBarService : IWorkspaceService + { + /// + /// Show an info bar in the current active view. + /// + /// Different hosts can have different definitions on what active view means. + /// + void ShowInfoBarInActiveView(string message, params InfoBarUI[] items); + + /// + /// Show global info bar + /// + /// + /// + void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items); + } + + internal struct InfoBarUI + { + public readonly string Title; + public readonly UIKind Kind; + public readonly Action Action; + public readonly bool CloseAfterAction; + + public InfoBarUI(string title, UIKind kind, Action action, bool closeAfterAction = true) + { + Contract.ThrowIfNull(title); + + Title = title; + Kind = kind; + Action = action; + CloseAfterAction = closeAfterAction; + } + + public bool IsDefault => Title == null; + + internal enum UIKind + { + Button, + HyperLink, + Close + } + } +} diff --git a/src/Workspaces/Core/Portable/Log/FunctionId.cs b/src/Workspaces/Core/Portable/Log/FunctionId.cs index 608bc297dbe10..fcce6868a6442 100644 --- a/src/Workspaces/Core/Portable/Log/FunctionId.cs +++ b/src/Workspaces/Core/Portable/Log/FunctionId.cs @@ -372,5 +372,6 @@ internal enum FunctionId CodeLens_GetFullyQualifiedName, RemoteHostClientService_Restarted, CodeAnalysisService_GetDesignerAttributesAsync, + Extension_InfoBar } } diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 74ab71906e6bf..6ac048dde5611 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -207,6 +207,7 @@ InternalUtilities\SyntaxTreeExtensions.cs + From 98e2c2331c3745ca76ecbbe327664df441115f1a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 18:07:14 -0700 Subject: [PATCH 060/284] Revert "Merge pull request #18972 from CyrusNajmabadi/docHighlights2" This reverts commit 8262112e63d0c92bfa421bcb49e54b6a791cabbd, reversing changes made to 837ca2141fc63d42ea4f6310c07e58624f0ca48a. --- .../CSharp/CSharpEditorFeatures.csproj | 2 + .../CSharpDocumentHighlightsService.cs | 13 +++ ...ighlightingAdditionalReferenceProvider.cs} | 26 +++-- src/EditorFeatures/Core/EditorFeatures.csproj | 19 ++-- ...viewReferenceHighlightingTaggerProvider.cs | 2 +- .../AbstractDocumentHighlightsService.cs | 61 ++++-------- .../DefinitionHighlightTag.cs | 2 +- .../DefinitionHighlightTagDefinition.cs | 2 +- .../IDocumentHighlightsService.cs | 3 +- ...HighlightingAdditionalReferenceProvider.cs | 14 +++ ...htReferenceCommandHandler.StartComparer.cs | 2 +- ...ivateToHighlightReferenceCommandHandler.cs | 2 +- .../ReferenceHighlightTag.cs | 2 +- ...ReferenceHighlightingViewTaggerProvider.cs | 3 +- .../WrittenReferenceHighlightTag.cs | 2 +- .../WrittenReferenceHighlightTagDefinition.cs | 2 +- .../AbstractReferenceHighlightingTests.vb | 11 +-- .../VisualBasic/BasicEditorFeatures.vbproj | 2 + .../VisualBasicDocumentHighlightsService.vb | 6 +- ...HighlightingAdditionalReferenceProvider.vb | 18 ++++ .../CSharp/Portable/CSharpFeatures.csproj | 1 - ...bstractDocumentHighlightsService_Remote.cs | 30 ------ .../DocumentHighlightingOptions.cs | 15 --- .../IRemoteDocumentHighlights.cs | 94 ------------------- src/Features/Core/Portable/Features.csproj | 5 - .../VisualBasic/Portable/BasicFeatures.vbproj | 1 - ...bstractTableDataSourceFindUsagesContext.cs | 2 +- .../WithReferencesFindUsagesContext.cs | 2 +- .../WithoutReferencesFindUsagesContext.cs | 2 +- .../Entries/DocumentSpanEntry.cs | 4 +- .../CSharp/CSharpReferenceHighlighting.cs | 3 +- .../VisualBasic/BasicReferenceHighlighting.cs | 3 +- .../TestUtilities/WellKnownTagNames.cs | 2 +- .../Remote/ServiceHub/ServiceHub.csproj | 1 - .../CodeAnalysisService_DocumentHighlights.cs | 28 ------ 35 files changed, 117 insertions(+), 270 deletions(-) create mode 100644 src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs rename src/{Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs => EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs} (70%) rename src/{Features/Core/Portable/DocumentHighlighting => EditorFeatures/Core/Implementation/ReferenceHighlighting}/AbstractDocumentHighlightsService.cs (82%) rename src/EditorFeatures/Core/{ReferenceHighlighting/Tags => Implementation/ReferenceHighlighting}/DefinitionHighlightTag.cs (88%) rename src/EditorFeatures/Core/{ReferenceHighlighting/Tags => Implementation/ReferenceHighlighting}/DefinitionHighlightTagDefinition.cs (92%) rename src/{Features/Core/Portable/DocumentHighlighting => EditorFeatures/Core/Implementation/ReferenceHighlighting}/IDocumentHighlightsService.cs (95%) create mode 100644 src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs rename src/EditorFeatures/Core/{ => Implementation}/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs (87%) rename src/EditorFeatures/Core/{ => Implementation}/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs (98%) rename src/EditorFeatures/Core/{ReferenceHighlighting/Tags => Implementation/ReferenceHighlighting}/ReferenceHighlightTag.cs (87%) rename src/EditorFeatures/Core/{ => Implementation}/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs (98%) rename src/EditorFeatures/Core/{ReferenceHighlighting/Tags => Implementation/ReferenceHighlighting}/WrittenReferenceHighlightTag.cs (88%) rename src/EditorFeatures/Core/{ReferenceHighlighting/Tags => Implementation/ReferenceHighlighting}/WrittenReferenceHighlightTagDefinition.cs (91%) rename src/{Features/VisualBasic/Portable/DocumentHighlighting => EditorFeatures/VisualBasic/HighlightReferences}/VisualBasicDocumentHighlightsService.vb (73%) create mode 100644 src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb delete mode 100644 src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs delete mode 100644 src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs delete mode 100644 src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs delete mode 100644 src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs diff --git a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj index 4369095c0de14..508f732f4d6cb 100644 --- a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj +++ b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj @@ -136,12 +136,14 @@ + + diff --git a/src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs b/src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs new file mode 100644 index 0000000000000..c1f3baf669ed0 --- /dev/null +++ b/src/EditorFeatures/CSharp/HighlightReferences/CSharpDocumentHighlightsService.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Composition; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.HighlightReferences +{ + [ExportLanguageService(typeof(IDocumentHighlightsService), LanguageNames.CSharp), Shared] + internal class CSharpDocumentHighlightsService : AbstractDocumentHighlightsService + { + } +} diff --git a/src/Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs b/src/EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs similarity index 70% rename from src/Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs rename to src/EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs index 4788978fd5719..2321c81cc552e 100644 --- a/src/Features/CSharp/Portable/DocumentHighlighting/CSharpDocumentHighlightsService.cs +++ b/src/EditorFeatures/CSharp/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.cs @@ -1,31 +1,32 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Immutable; +using System.Collections.Generic; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; using Microsoft.CodeAnalysis.Host.Mef; +using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.CSharp.DocumentHighlighting +namespace Microsoft.CodeAnalysis.Editor.CSharp.ReferenceHighlighting { - [ExportLanguageService(typeof(IDocumentHighlightsService), LanguageNames.CSharp), Shared] - internal class CSharpDocumentHighlightsService : AbstractDocumentHighlightsService + [ExportLanguageService(typeof(IReferenceHighlightingAdditionalReferenceProvider), LanguageNames.CSharp), Shared] + internal class ReferenceHighlightingAdditionalReferenceProvider : IReferenceHighlightingAdditionalReferenceProvider { - protected override async Task> GetAdditionalReferencesAsync( + public async Task> GetAdditionalReferencesAsync( Document document, ISymbol symbol, CancellationToken cancellationToken) { // The FindRefs engine won't find references through 'var' for performance reasons. // Also, they are not needed for things like rename/sig change, and the normal find refs - // feature. However, we would like the results to be highlighted to get a good experience + // feature. However, we would lke the results to be highlighted to get a good experience // while editing (especially since highlighting may have been invoked off of 'var' in // the first place). // // So we look for the references through 'var' directly in this file and add them to the // results found by the engine. - var results = ArrayBuilder.GetInstance(); + List results = null; if (symbol is INamedTypeSymbol && symbol.Name != "var") { @@ -51,13 +52,18 @@ protected override async Task> GetAdditionalReferencesA if (originalSymbol.Equals(boundSymbol)) { + if (results == null) + { + results = new List(); + } + results.Add(type.GetLocation()); } } } } - return results.ToImmutableAndFree(); + return results ?? SpecializedCollections.EmptyEnumerable(); } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index e7809e7b20f5a..3a5b371a27e54 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -168,14 +168,6 @@ - - - - - - - - @@ -638,6 +630,8 @@ + + @@ -649,6 +643,15 @@ + + + + + + + + + diff --git a/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs index 5c90ff6b8f179..29a2060e254e3 100644 --- a/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Preview/PreviewReferenceHighlightingTaggerProvider.cs @@ -6,7 +6,7 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview { diff --git a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/AbstractDocumentHighlightsService.cs similarity index 82% rename from src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/AbstractDocumentHighlightsService.cs index 91b1933bd7692..8a90b0062620f 100644 --- a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/AbstractDocumentHighlightsService.cs @@ -14,47 +14,12 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.DocumentHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { - internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService + internal abstract class AbstractDocumentHighlightsService : IDocumentHighlightsService { public async Task> GetDocumentHighlightsAsync( Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken) - { - var (succeeded, highlights) = await GetDocumentHighlightsInRemoteProcessAsync( - document, position, documentsToSearch, cancellationToken).ConfigureAwait(false); - - if (succeeded) - { - return highlights; - } - - return await GetDocumentHighlightsInCurrentProcessAsync( - document, position, documentsToSearch, cancellationToken).ConfigureAwait(false); - } - - private async Task<(bool succeeded, ImmutableArray highlights)> GetDocumentHighlightsInRemoteProcessAsync( - Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken) - { - using (var session = await TryGetRemoteSessionAsync( - document.Project.Solution, cancellationToken).ConfigureAwait(false)) - { - if (session == null) - { - return (succeeded: false, ImmutableArray.Empty); - } - - var result = await session.InvokeAsync( - nameof(IRemoteDocumentHighlights.GetDocumentHighlightsAsync), - document.Id, - position, - documentsToSearch.Select(d => d.Id).ToArray()).ConfigureAwait(false); - return (true, SerializableDocumentHighlights.Rehydrate(result, document.Project.Solution)); - } - } - - private async Task> GetDocumentHighlightsInCurrentProcessAsync( - Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken) { // use speculative semantic model to see whether we are on a symbol we can do HR var span = new TextSpan(position, 0); @@ -76,11 +41,11 @@ private async Task> GetDocumentHighlightsInCu // Get unique tags for referenced symbols return await GetTagsForReferencedSymbolAsync( - new SymbolAndProjectId(symbol, document.Project.Id), documentsToSearch, + new SymbolAndProjectId(symbol, document.Project.Id), documentsToSearch, solution, cancellationToken).ConfigureAwait(false); } - private static async Task GetSymbolToSearchAsync(Document document, int position, SemanticModel semanticModel, ISymbol symbol, CancellationToken cancellationToken) + private async Task GetSymbolToSearchAsync(Document document, int position, SemanticModel semanticModel, ISymbol symbol, CancellationToken cancellationToken) { // see whether we can use the symbol as it is var currentSemanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -117,7 +82,7 @@ await SymbolFinder.FindReferencesAsync( return ImmutableArray.Empty; } - private static bool ShouldConsiderSymbol(ISymbol symbol) + private bool ShouldConsiderSymbol(ISymbol symbol) { switch (symbol.Kind) { @@ -167,13 +132,19 @@ private async Task> FilterAndCreateSpansAsync documentsToSearch, cancellationToken).ConfigureAwait(false); } - protected virtual Task> GetAdditionalReferencesAsync( + private Task> GetAdditionalReferencesAsync( Document document, ISymbol symbol, CancellationToken cancellationToken) { - return SpecializedTasks.EmptyImmutableArray(); + var additionalReferenceProvider = document.Project.LanguageServices.GetService(); + if (additionalReferenceProvider != null) + { + return additionalReferenceProvider.GetAdditionalReferencesAsync(document, symbol, cancellationToken); + } + + return Task.FromResult(SpecializedCollections.EmptyEnumerable()); } - private static async Task> CreateSpansAsync( + private async Task> CreateSpansAsync( Solution solution, ISymbol symbol, IEnumerable references, @@ -281,7 +252,7 @@ private static bool ShouldIncludeDefinition(ISymbol symbol) return true; } - private static async Task AddLocationSpan(Location location, Solution solution, HashSet spanSet, MultiDictionary tagList, HighlightSpanKind kind, CancellationToken cancellationToken) + private async Task AddLocationSpan(Location location, Solution solution, HashSet spanSet, MultiDictionary tagList, HighlightSpanKind kind, CancellationToken cancellationToken) { var span = await GetLocationSpanAsync(solution, location, cancellationToken).ConfigureAwait(false); if (span != null && !spanSet.Contains(span.Value)) @@ -291,7 +262,7 @@ private static async Task AddLocationSpan(Location location, Solution solution, } } - private static async Task GetLocationSpanAsync( + private async Task GetLocationSpanAsync( Solution solution, Location location, CancellationToken cancellationToken) { try diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTag.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTag.cs similarity index 88% rename from src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTag.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTag.cs index 0b974c620bfa2..2f5873a759429 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTag.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTag.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { internal class DefinitionHighlightTag : NavigableHighlightTag { diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTagDefinition.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTagDefinition.cs similarity index 92% rename from src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTagDefinition.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTagDefinition.cs index 5580291757cf8..b5a8023f8297e 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/DefinitionHighlightTagDefinition.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/DefinitionHighlightTagDefinition.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { [Export(typeof(EditorFormatDefinition))] [Name(DefinitionHighlightTag.TagId)] diff --git a/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs similarity index 95% rename from src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs index 0e08d4418c4f5..f86be931963c1 100644 --- a/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs @@ -1,12 +1,13 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.DocumentHighlighting +namespace Microsoft.CodeAnalysis.Editor { internal enum HighlightSpanKind { diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs new file mode 100644 index 0000000000000..694a5b6e505a2 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IReferenceHighlightingAdditionalReferenceProvider.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +{ + internal interface IReferenceHighlightingAdditionalReferenceProvider : ILanguageService + { + Task> GetAdditionalReferencesAsync(Document document, ISymbol symbol, CancellationToken cancellationToken); + } +} diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs similarity index 87% rename from src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs index 125416fd75162..76b72d5268f3b 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.StartComparer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Microsoft.VisualStudio.Text; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { internal partial class NavigateToHighlightReferenceCommandHandler { diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs similarity index 98% rename from src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs index 07a6b67793a85..a0997e4009c8a 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/NagivateToHighlightReferenceCommandHandler.cs @@ -14,7 +14,7 @@ using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { [ExportCommandHandler(PredefinedCommandHandlerNames.NavigateToHighlightedReference, ContentTypeNames.RoslynContentType)] diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/ReferenceHighlightTag.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightTag.cs similarity index 87% rename from src/EditorFeatures/Core/ReferenceHighlighting/Tags/ReferenceHighlightTag.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightTag.cs index 299c701a98780..6c7d2cddc337b 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/ReferenceHighlightTag.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightTag.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { internal class ReferenceHighlightTag : NavigableHighlightTag { diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs similarity index 98% rename from src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs index e2367ad5f5d3d..cbad756b7bd6a 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs @@ -6,7 +6,6 @@ using System.ComponentModel.Composition; using System.Linq; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Tagging; @@ -22,7 +21,7 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { [Export(typeof(IViewTaggerProvider))] [ContentType(ContentTypeNames.RoslynContentType)] diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTag.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTag.cs similarity index 88% rename from src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTag.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTag.cs index d6f8df3342be5..b4d35d64ae6b1 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTag.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTag.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { internal class WrittenReferenceHighlightTag : NavigableHighlightTag { diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTagDefinition.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTagDefinition.cs similarity index 91% rename from src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTagDefinition.cs rename to src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTagDefinition.cs index 74323824a5056..8db19cc5cb2b4 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/Tags/WrittenReferenceHighlightTagDefinition.cs +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/WrittenReferenceHighlightTagDefinition.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting { [Export(typeof(EditorFormatDefinition))] [Name(WrittenReferenceHighlightTag.TagId)] diff --git a/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb b/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb index 7c9591274611f..ab5ab6a8ab80b 100644 --- a/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb +++ b/src/EditorFeatures/Test2/ReferenceHighlighting/AbstractReferenceHighlightingTests.vb @@ -1,8 +1,7 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading -Imports Microsoft.CodeAnalysis.DocumentHighlighting -Imports Microsoft.CodeAnalysis.Editor.ReferenceHighlighting +Imports Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions Imports Microsoft.CodeAnalysis.Editor.Shared.Options Imports Microsoft.CodeAnalysis.Editor.Shared.Tagging @@ -17,17 +16,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting Public MustInherit Class AbstractReferenceHighlightingTests Protected Async Function VerifyHighlightsAsync(test As XElement, Optional optionIsEnabled As Boolean = True) As Tasks.Task - Await VerifyHighlightsAsync(test, optionIsEnabled, outOfProcess:=False) - Await VerifyHighlightsAsync(test, optionIsEnabled, outOfProcess:=True) - End Function - - Private Async Function VerifyHighlightsAsync(test As XElement, optionIsEnabled As Boolean, outOfProcess As Boolean) As Tasks.Task Using workspace = TestWorkspace.Create(test) WpfTestCase.RequireWpfFact($"{NameOf(AbstractReferenceHighlightingTests)}.VerifyHighlightsAsync creates asynchronous taggers") - workspace.Options = workspace.Options.WithChangedOption( - DocumentHighlightingOptions.OutOfProcessAllowed, outOfProcess) - Dim tagProducer = New ReferenceHighlightingViewTaggerProvider( workspace.GetService(Of IForegroundNotificationService), workspace.GetService(Of ISemanticChangeNotificationService), diff --git a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj index 79ee5ff8d6c4c..531d79a604a6f 100644 --- a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj +++ b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj @@ -129,6 +129,7 @@ + @@ -154,6 +155,7 @@ + diff --git a/src/Features/VisualBasic/Portable/DocumentHighlighting/VisualBasicDocumentHighlightsService.vb b/src/EditorFeatures/VisualBasic/HighlightReferences/VisualBasicDocumentHighlightsService.vb similarity index 73% rename from src/Features/VisualBasic/Portable/DocumentHighlighting/VisualBasicDocumentHighlightsService.vb rename to src/EditorFeatures/VisualBasic/HighlightReferences/VisualBasicDocumentHighlightsService.vb index 2f925d3749ed3..b0693439b5de7 100644 --- a/src/Features/VisualBasic/Portable/DocumentHighlighting/VisualBasicDocumentHighlightsService.vb +++ b/src/EditorFeatures/VisualBasic/HighlightReferences/VisualBasicDocumentHighlightsService.vb @@ -1,13 +1,13 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Composition -Imports Microsoft.CodeAnalysis.DocumentHighlighting +Imports Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting Imports Microsoft.CodeAnalysis.Host.Mef -Namespace Microsoft.CodeAnalysis.VisualBasic.DocumentHighlighting +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.HighlightReferences Friend Class VisualBasicDocumentHighlightsService Inherits AbstractDocumentHighlightsService End Class -End Namespace \ No newline at end of file +End Namespace diff --git a/src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb b/src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb new file mode 100644 index 0000000000000..2c95b09d3a2fb --- /dev/null +++ b/src/EditorFeatures/VisualBasic/ReferenceHighlighting/ReferenceHighlightingAdditionalReferenceProvider.vb @@ -0,0 +1,18 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Composition +Imports System.Threading +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting +Imports Microsoft.CodeAnalysis.Host.Mef + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.ReferenceHighlighting + + Friend Class ReferenceHighlightingAdditionalReferenceProvider + Implements IReferenceHighlightingAdditionalReferenceProvider + + Public Function GetAdditionalReferencesAsync(document As Document, symbol As ISymbol, cancellationToken As CancellationToken) As Task(Of IEnumerable(Of Location)) Implements IReferenceHighlightingAdditionalReferenceProvider.GetAdditionalReferencesAsync + Return SpecializedTasks.EmptyEnumerable(Of Location)() + End Function + End Class +End Namespace diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 0e91914a13210..07d23798af9ff 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -81,7 +81,6 @@ - diff --git a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs deleted file mode 100644 index 7efad4a9663df..0000000000000 --- a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService_Remote.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; - -namespace Microsoft.CodeAnalysis.DocumentHighlighting -{ - internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService - { - private static async Task TryGetRemoteSessionAsync( - Solution solution, CancellationToken cancellationToken) - { - var outOfProcessAllowed = solution.Workspace.Options.GetOption(DocumentHighlightingOptions.OutOfProcessAllowed); - if (!outOfProcessAllowed) - { - return null; - } - - var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); - if (client == null) - { - return null; - } - - return await client.TryCreateCodeAnalysisServiceSessionAsync( - solution, cancellationToken).ConfigureAwait(false); - } - } -} \ No newline at end of file diff --git a/src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs b/src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs deleted file mode 100644 index 0363999301f11..0000000000000 --- a/src/Features/Core/Portable/DocumentHighlighting/DocumentHighlightingOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.DocumentHighlighting -{ - internal static class DocumentHighlightingOptions - { - private const string LocalRegistryPath = @"Roslyn\Features\DocumentHighlighting\"; - - public static readonly Option OutOfProcessAllowed = new Option( - nameof(DocumentHighlightingOptions), nameof(OutOfProcessAllowed), defaultValue: false, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OutOfProcessAllowed))); - } -} \ No newline at end of file diff --git a/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs b/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs deleted file mode 100644 index b58c6f35a18fd..0000000000000 --- a/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Immutable; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.DocumentHighlighting -{ - internal interface IRemoteDocumentHighlights - { - Task GetDocumentHighlightsAsync( - DocumentId documentId, int position, DocumentId[] documentIdsToSearch); - } - - internal struct SerializableDocumentHighlights - { - public DocumentId DocumentId; - public SerializableHighlightSpan[] HighlightSpans; - - public static ImmutableArray Rehydrate(SerializableDocumentHighlights[] array, Solution solution) - { - var result = ArrayBuilder.GetInstance(array.Length); - foreach (var dehydrated in array) - { - result.Push(dehydrated.Rehydrate(solution)); - } - - return result.ToImmutableAndFree(); - } - - private DocumentHighlights Rehydrate(Solution solution) - => new DocumentHighlights(solution.GetDocument(DocumentId), SerializableHighlightSpan.Rehydrate(HighlightSpans)); - - public static SerializableDocumentHighlights[] Dehydrate(ImmutableArray array) - { - var result = new SerializableDocumentHighlights[array.Length]; - var index = 0; - foreach (var highlights in array) - { - result[index] = Dehydrate(highlights); - index++; - } - - return result; - } - - private static SerializableDocumentHighlights Dehydrate(DocumentHighlights highlights) - => new SerializableDocumentHighlights - { - DocumentId = highlights.Document.Id, - HighlightSpans = SerializableHighlightSpan.Dehydrate(highlights.HighlightSpans) - }; - } - - internal struct SerializableHighlightSpan - { - public TextSpan TextSpan; - public HighlightSpanKind Kind; - - internal static SerializableHighlightSpan[] Dehydrate(ImmutableArray array) - { - var result = new SerializableHighlightSpan[array.Length]; - var index = 0; - foreach (var span in array) - { - result[index] = Dehydrate(span); - index++; - } - - return result; - } - - private static SerializableHighlightSpan Dehydrate(HighlightSpan span) - => new SerializableHighlightSpan - { - Kind = span.Kind, - TextSpan = span.TextSpan - }; - - internal static ImmutableArray Rehydrate(SerializableHighlightSpan[] array) - { - var result = ArrayBuilder.GetInstance(array.Length); - foreach (var dehydrated in array) - { - result.Push(dehydrated.Rehydrate()); - } - - return result.ToImmutableAndFree(); - } - - private HighlightSpan Rehydrate() - => new HighlightSpan(TextSpan, Kind); - } -} \ No newline at end of file diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 25a06421f4352..cff5327a8c52d 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -132,7 +132,6 @@ - @@ -158,10 +157,6 @@ - - - - diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj index 884a29b8778a4..7369bfd880ca4 100644 --- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj @@ -66,7 +66,6 @@ InternalUtilities\LambdaUtilities.vb - diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index c8cf14161ae4b..e593f2f5ab596 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.FindUsages; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Text; diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs index 765612f4c86b2..9973893c2464d 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithReferencesFindUsagesContext.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.VisualStudio.Shell.FindAllReferences; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs index 2456a495cfef6..5a5f6a2b02bf8 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs @@ -3,7 +3,7 @@ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.VisualStudio.Shell.FindAllReferences; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs index f32bade84b9c2..68c45919b30eb 100644 --- a/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Next/FindReferences/Entries/DocumentSpanEntry.cs @@ -7,11 +7,10 @@ using System.Windows.Media; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.FindUsages; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Preview; using Microsoft.CodeAnalysis.FindUsages; @@ -19,6 +18,7 @@ using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs index 8f527cbb55af5..627ac96efc079 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs @@ -3,10 +3,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs index 86a0a05fec482..873349a13f999 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs @@ -3,10 +3,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs b/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs index 98dd6cba799a0..d765685a409f3 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownTagNames.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; +using Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting; namespace Microsoft.VisualStudio.IntegrationTest.Utilities { diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj index 68fe27e68b2f4..1d5b8a990240a 100644 --- a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -73,7 +73,6 @@ - diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs deleted file mode 100644 index b98c8e58a9146..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DocumentHighlights.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Immutable; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.DocumentHighlighting; -using Microsoft.CodeAnalysis.Shared.Extensions; - -namespace Microsoft.CodeAnalysis.Remote -{ - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteDocumentHighlights - { - public async Task GetDocumentHighlightsAsync( - DocumentId documentId, int position, DocumentId[] documentIdsToSearch) - { - var solution = await GetSolutionAsync().ConfigureAwait(false); - var document = solution.GetDocument(documentId); - var documentsToSearch = ImmutableHashSet.CreateRange(documentIdsToSearch.Select(solution.GetDocument)); - - var service = document.GetLanguageService(); - var result = await service.GetDocumentHighlightsAsync( - document, position, documentsToSearch, CancellationToken).ConfigureAwait(false); - - return SerializableDocumentHighlights.Dehydrate(result); - } - } -} \ No newline at end of file From ba30bca7de123c048731e9296d05b9b735a4c379 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 8 May 2017 18:51:35 -0700 Subject: [PATCH 061/284] Tuple name completion should not include colon (#19335) --- .../CSharpCompletionCommandHandlerTests.vb | 132 ++++++++++++++++++ .../TupleNameCompletionProvider.cs | 31 +++- 2 files changed, 156 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 5bda149147ed8..709175329f8a5 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -576,6 +576,138 @@ class C End Using End Function + + + Public Async Function ColonInTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("fi") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTypeChars(":") + Assert.Contains("(first:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function ColonInExactTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("first") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTypeChars(":") + Assert.Contains("(first:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function ColonInTupleNameInTupleLiteralAfterComma() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("se") + Await state.AssertSelectedCompletionItem(displayText:="second:", isHardSelected:=True) + Assert.Equal("second", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTypeChars(":") + Assert.Contains("(0, second:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TabInTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("fi") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTab() + state.SendTypeChars(":") + state.SendTypeChars("0") + Assert.Contains("(first:0", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TabInExactTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("first") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTab() + state.SendTypeChars(":") + state.SendTypeChars("0") + Assert.Contains("(first:0", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TabInTupleNameInTupleLiteralAfterComma() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("se") + Await state.AssertSelectedCompletionItem(displayText:="second:", isHardSelected:=True) + Assert.Equal("second", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTab() + state.SendTypeChars(":") + state.SendTypeChars("1") + Assert.Contains("(0, second:1", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + Public Async Function TestKeywordInTupleLiteral() As Task diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs index cdc59e16a51a9..5f6e28fb619cb 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs @@ -2,20 +2,22 @@ using System.Collections.Immutable; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers { internal class TupleNameCompletionProvider : CommonCompletionProvider { - private static readonly CompletionItemRules _cachedRules = CompletionItemRules.Default - .WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, ':')); + private const string ColonString = ":"; public override async Task ProvideCompletionsAsync(CompletionContext completionContext) { @@ -40,7 +42,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC .Cast() .ToImmutableArray(); - AddItems(inferredTypes, index.Value, completionContext); + AddItems(inferredTypes, index.Value, completionContext, context.TargetToken.Parent.SpanStart); } private int? GetElementIndex(CSharpSyntaxContext context) @@ -65,7 +67,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC return null; } - private void AddItems(ImmutableArray inferredTypes, int index, CompletionContext context) + private void AddItems(ImmutableArray inferredTypes, int index, CompletionContext context, int spanStart) { foreach (var type in inferredTypes) { @@ -74,11 +76,26 @@ private void AddItems(ImmutableArray inferredTypes, int index, return; } + // Note: the filter text does not include the ':'. We want to ensure that if + // the user types the name exactly (up to the colon) that it is selected as an + // exact match. + var field = type.TupleElements[index]; - var item = CommonCompletionItem.Create( - field.Name + ":", _cachedRules, Glyph.FieldPublic); - context.AddItem(item); + + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( + displayText: field.Name + ColonString, + symbols: ImmutableArray.Create(field), + rules: CompletionItemRules.Default, + contextPosition: spanStart, + filterText: field.Name)); } } + + protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) + { + return Task.FromResult(new TextChange( + selectedItem.Span, + selectedItem.DisplayText.Substring(0, selectedItem.DisplayText.Length - ColonString.Length))); + } } } \ No newline at end of file From d6d1efa4eeebef75a0443917581548036ff99607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Mon, 8 May 2017 21:22:57 -0700 Subject: [PATCH 062/284] Update to latest PDB reader/writer dependencies (#19302) * Update versions * Improve diagnostics --- NuGet.Config | 1 + build/Targets/Packages.props | 6 +-- .../CSharp/Test/Emit/PDB/PDBTests.cs | 12 ++++-- .../CodeAnalysisResources.Designer.cs | 12 +++--- .../Core/Portable/CodeAnalysisResources.resx | 6 +-- .../Portable/NativePdbWriter/PdbWriter.cs | 40 +++++++++++-------- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index 43d2539f28212..0ea90a3391db9 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -19,6 +19,7 @@ + diff --git a/build/Targets/Packages.props b/build/Targets/Packages.props index d359d97267662..638af15a836ae 100644 --- a/build/Targets/Packages.props +++ b/build/Targets/Packages.props @@ -36,9 +36,9 @@ 0.8.31-beta 1.0.35 1.1.0 - 1.0.0-beta1-61618-01 - 1.5.0 - 1.2.0 + 1.0.0-beta1-61708-01 + 1.6.0-beta2-25304 + 1.3.0-beta1-61619-01 4.7.1-alpha-00001 3.13.8 15.0.26201-alpha diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index 80adf674c9e72..fd713ce68d083 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -180,9 +180,11 @@ public void SymWriterErrors2() embeddedTexts: null, testData: new CompilationTestData() { SymWriterFactory = () => new object() }); + var libName = $"Microsoft.DiaSymReader.Native.{(IntPtr.Size == 4 ? "x86" : "amd64")}.dll"; + result.Diagnostics.Verify( - // error CS0041: Unexpected error writing debug information -- 'Windows PDB writer is not available -- could not find Microsoft.DiaSymReader.Native.{0}.dll' - Diagnostic(ErrorCode.FTL_DebugEmitFailure).WithArguments(string.Format(CodeAnalysisResources.SymWriterNotAvailable, (IntPtr.Size == 4) ? "x86" : "amd64"))); + // error CS0041: Unexpected error writing debug information -- 'The version of Windows PDB writer is older than required: 'Microsoft.DiaSymReader.Native.x86.dll'' + Diagnostic(ErrorCode.FTL_DebugEmitFailure).WithArguments(string.Format(CodeAnalysisResources.SymWriterOlderVersionThanRequired, libName))); Assert.False(result.Success); } @@ -213,9 +215,11 @@ public void SymWriterErrors3() embeddedTexts: null, testData: new CompilationTestData() { SymWriterFactory = () => new MockSymUnmanagedWriter() }); + var libName = $"Microsoft.DiaSymReader.Native.{(IntPtr.Size == 4 ? "x86" : "amd64")}.dll"; + result.Diagnostics.Verify( - // error CS0041: Unexpected error writing debug information -- 'Windows PDB writer doesn't support deterministic compilation -- could not find Microsoft.DiaSymReader.Native.{0}.dll' - Diagnostic(ErrorCode.FTL_DebugEmitFailure).WithArguments(string.Format(CodeAnalysisResources.SymWriterNotDeterministic, (IntPtr.Size == 4) ? "x86" : "amd64"))); + // error CS0041: Unexpected error writing debug information -- 'Windows PDB writer doesn't support deterministic compilation -- could not find {0}' + Diagnostic(ErrorCode.FTL_DebugEmitFailure).WithArguments(string.Format(CodeAnalysisResources.SymWriterNotDeterministic, libName))); Assert.False(result.Success); } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index f7d3bbd3e231d..478cae1ae4eae 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -1289,20 +1289,20 @@ internal static string Struct1 { } /// - /// Looks up a localized string similar to Windows PDB writer is not available -- could not find Microsoft.DiaSymReader.Native.{0}.dll. + /// Looks up a localized string similar to Windows PDB writer doesn't support deterministic compilation: '{0}'. /// - internal static string SymWriterNotAvailable { + internal static string SymWriterNotDeterministic { get { - return ResourceManager.GetString("SymWriterNotAvailable", resourceCulture); + return ResourceManager.GetString("SymWriterNotDeterministic", resourceCulture); } } /// - /// Looks up a localized string similar to Windows PDB writer doesn't support deterministic compilation -- could not find Microsoft.DiaSymReader.Native.{0}.dll. + /// Looks up a localized string similar to The version of Windows PDB writer is older than required: '{0}'. /// - internal static string SymWriterNotDeterministic { + internal static string SymWriterOlderVersionThanRequired { get { - return ResourceManager.GetString("SymWriterNotDeterministic", resourceCulture); + return ResourceManager.GetString("SymWriterOlderVersionThanRequired", resourceCulture); } } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 3af8d6f2de563..96cd5f06360ee 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -465,10 +465,10 @@ Invalid data at offset {0}: {1}{2}*{3}{4} - Windows PDB writer doesn't support deterministic compilation -- could not find Microsoft.DiaSymReader.Native.{0}.dll + Windows PDB writer doesn't support deterministic compilation: '{0}' - - Windows PDB writer is not available -- could not find Microsoft.DiaSymReader.Native.{0}.dll + + The version of Windows PDB writer is older than required: '{0}' The attribute {0} has an invalid value of {1}. diff --git a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs index e4b5a60e629ec..bcbb76264c431 100644 --- a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs +++ b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs @@ -736,17 +736,25 @@ private void DefineScopeLocals(LocalScope currentScope, StandaloneSignatureHandl private const string SymWriterClsid = "0AE2DEB0-F901-478b-BB9F-881EE8066788"; - private static bool s_MicrosoftDiaSymReaderNativeLoadFailed; + private const string LegacyDiaSymReaderModuleName = "diasymreader.dll"; + private const string DiaSymReaderModuleName32 = "Microsoft.DiaSymReader.Native.x86.dll"; + private const string DiaSymReaderModuleName64 = "Microsoft.DiaSymReader.Native.amd64.dll"; + + private static string s_MicrosoftDiaSymReaderNativeLoadFailure; [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] - [DllImport("Microsoft.DiaSymReader.Native.x86.dll", EntryPoint = "CreateSymWriter")] + [DllImport(DiaSymReaderModuleName32, EntryPoint = "CreateSymWriter")] private extern static void CreateSymWriter32(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)]out object symWriter); [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] - [DllImport("Microsoft.DiaSymReader.Native.amd64.dll", EntryPoint = "CreateSymWriter")] + [DllImport(DiaSymReaderModuleName64, EntryPoint = "CreateSymWriter")] private extern static void CreateSymWriter64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)]out object symWriter); - private static string PlatformId => (IntPtr.Size == 4) ? "x86" : "amd64"; + private static string DiaSymReaderModuleName + => (IntPtr.Size == 4) ? DiaSymReaderModuleName32 : DiaSymReaderModuleName64; + + private static string LoadedDiaSymReaderModuleName + => s_MicrosoftDiaSymReaderNativeLoadFailure != null ? LegacyDiaSymReaderModuleName : DiaSymReaderModuleName; private static Type GetCorSymWriterSxSType() { @@ -764,7 +772,7 @@ private static object CreateSymWriterWorker() object symWriter = null; // First try to load an implementation from Microsoft.DiaSymReader.Native, which supports determinism. - if (!s_MicrosoftDiaSymReaderNativeLoadFailed) + if (s_MicrosoftDiaSymReaderNativeLoadFailure == null) { try { @@ -778,9 +786,9 @@ private static object CreateSymWriterWorker() CreateSymWriter64(ref guid, out symWriter); } } - catch (Exception) + catch (Exception e) { - s_MicrosoftDiaSymReaderNativeLoadFailed = true; + s_MicrosoftDiaSymReaderNativeLoadFailure = e.Message; symWriter = null; } } @@ -806,25 +814,25 @@ public void SetMetadataEmitter(MetadataWriter metadataWriter) } catch (Exception) { - throw new NotSupportedException(string.Format(CodeAnalysisResources.SymWriterNotAvailable, PlatformId)); + throw new NotSupportedException(string.Format(CodeAnalysisResources.SymWriterOlderVersionThanRequired, LoadedDiaSymReaderModuleName)); } // Correctness: If the stream is not specified or if it is non-empty the SymWriter appends data to it (provided it contains valid PDB) // and the resulting PDB has Age = existing_age + 1. _pdbStream = new ComMemoryStream(); - if (_deterministic) + if (!_deterministic) { - if (!(symWriter is ISymUnmanagedWriter7)) - { - throw new NotSupportedException(string.Format(CodeAnalysisResources.SymWriterNotDeterministic, PlatformId)); - } - - ((ISymUnmanagedWriter7)symWriter).InitializeDeterministic(new PdbMetadataWrapper(metadataWriter), _pdbStream); + symWriter.Initialize(new PdbMetadataWrapper(metadataWriter), _fileName, _pdbStream, fullBuild: true); + } + else if (symWriter is ISymUnmanagedWriter7 symWriter7) + { + symWriter7.InitializeDeterministic(new PdbMetadataWrapper(metadataWriter), _pdbStream); } else { - symWriter.Initialize(new PdbMetadataWrapper(metadataWriter), _fileName, _pdbStream, fullBuild: true); + throw new NotSupportedException(s_MicrosoftDiaSymReaderNativeLoadFailure ?? + string.Format(CodeAnalysisResources.SymWriterNotDeterministic, LoadedDiaSymReaderModuleName)); } _metadataWriter = metadataWriter; From 8af036a83af9cef70a38fc1c59d3ca35388a3972 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 8 May 2017 21:32:58 -0700 Subject: [PATCH 063/284] Refout: disallow NoPia, best effort determinism, assembly redirect for CopyRefAssembly (#19326) * Ref assemblies make a best effort to emit with determinism * Fixing CopyRefAssembly message output * CopyRefAssembly logs when input isn't ref assembly * CopyRefAssembly does assembly redirect. * Compact error codes for C# 7.1 --- docs/features/refout.md | 11 +- .../Portable/CSharpResources.Designer.cs | 9 ++ .../CSharp/Portable/CSharpResources.resx | 3 + .../CommandLine/CSharpCommandLineParser.cs | 5 + .../Emitter/Model/NamedTypeSymbolAdapter.cs | 1 - .../CSharp/Portable/Errors/ErrorCode.cs | 11 +- .../Test/CommandLine/CommandLineTests.cs | 44 ++++++- .../Test/Emit/Emit/CompilationEmitTests.cs | 107 +++++++++++++++--- .../Semantics/TargetTypedDefaultTests.cs | 10 +- .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 13 ++- .../Core/MSBuildTask/CopyRefAssembly.cs | 30 +++-- .../Core/MSBuildTask/ErrorString.Designer.cs | 9 ++ .../Core/MSBuildTask/ErrorString.resx | 3 + .../CodeAnalysisResources.Designer.cs | 9 ++ .../Core/Portable/CodeAnalysisResources.resx | 3 + .../Core/Portable/Compilation/Compilation.cs | 12 +- .../Portable/Emit/CommonPEModuleBuilder.cs | 2 +- .../VisualBasicCommandLineParser.vb | 4 + .../VisualBasic/Portable/Errors/Errors.vb | 1 + .../Portable/VBResources.Designer.vb | 9 ++ .../VisualBasic/Portable/VBResources.resx | 3 + .../Test/CommandLine/CommandLineTests.vb | 36 +++++- .../Test/Emit/Emit/CompilationEmitTests.vb | 18 +++ 23 files changed, 301 insertions(+), 52 deletions(-) diff --git a/docs/features/refout.md b/docs/features/refout.md index 9ab450afc9ffc..81253cb00f310 100644 --- a/docs/features/refout.md +++ b/docs/features/refout.md @@ -37,11 +37,10 @@ The `/refout` parameter specifies a file path where the ref assembly should be o The `/refonly` parameter is a flag that indicates that a ref assembly should be output instead of an implementation assembly. The `/refonly` parameter is not allowed together with the `/refout` parameter, as it doesn't make sense to have both the primary and secondary outputs be ref assemblies. Also, the `/refonly` parameter silently disables outputting PDBs, as ref assemblies cannot be executed. The `/refonly` parameter translates to `EmitMetadataOnly` being `true`, and `IncludePrivateMembers` being `false` in the `Emit` API (see details below). -Neither `/refonly` nor `/refout` are permitted with `/target:module` or `/addmodule` options. - -When the compiler produces documentation, the contents produced will match the APIs that go into the primary output. In other words, the documentation will be filtered down when using the `/refonly` parameter. +Neither `/refonly` nor `/refout` are permitted with `/target:module`, `/addmodule`, or `/link:...` options. The compilation from the command-line will either produce both assemblies (implementation and ref) or neither. There is no "partial success" scenario. +When the compiler produces documentation, it is un-affected by either the `/refonly` or `/refout` parameters. ### CscTask/CoreCompile The `CoreCompile` target will support a new output, called `IntermediateRefAssembly`, which parallels the existing `IntermediateAssembly`. @@ -65,8 +64,10 @@ Going back to the 4 driving scenarios: ## Future As mentioned above, there may be further refinements after C# 7.1: -- controlling internals (producing public ref assemblies) -- produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set) +- Controlling internals (producing public ref assemblies) +- Produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set) +- When the compiler produces documentation, the contents produced could be filtered down to match the APIs that go into the primary output. In other words, the documentation could be filtered down when using the `/refonly` parameter. + ## Open questions - should explicit method implementations be included in ref assemblies? diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 24a49f2d8bd9f..5f2881190b3a9 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -6730,6 +6730,15 @@ internal static string ERR_NoDynamicPhantomOnBaseIndexer { } } + /// + /// Looks up a localized string similar to Cannot embed types when using /refout or /refonly.. + /// + internal static string ERR_NoEmbeddedTypeWhenRefOutOrRefOnly { + get { + return ResourceManager.GetString("ERR_NoEmbeddedTypeWhenRefOutOrRefOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to Program does not contain a static 'Main' method suitable for an entry point. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 6aedd38c798ca..9a5efb8522168 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2300,6 +2300,9 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot compile net modules when using /refout or /refonly. + + Cannot embed types when using /refout or /refonly. + Overloadable operator expected diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index c28578c8eb9b5..cbde4e6815d12 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -1178,6 +1178,11 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly); } + if ((refOnly || outputRefFilePath != null) && metadataReferences.Any(r => r.Properties.EmbedInteropTypes)) + { + AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly); + } + if (outputKind == OutputKind.NetModule && (refOnly || outputRefFilePath != null)) { AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index cfbad7e754c4b..da2b554d0c6bd 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -325,7 +325,6 @@ internal virtual IEnumerable GetEventsToEmit() foreach (var member in this.GetMembers()) { - // PROTOTYPE(refout) Do something here? if (member.Kind == SymbolKind.Method) { var method = (MethodSymbol)member; diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index e582e372c0c04..68c162402c5ba 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1476,11 +1476,12 @@ internal enum ErrorCode WRN_Experimental = 8305, ERR_TupleInferredNamesNotAvailable = 8306, - ERR_NoRefOutWhenRefOnly = 8355, - ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8356, + ERR_NoRefOutWhenRefOnly = 8307, + ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8308, + ERR_NoEmbeddedTypeWhenRefOutOrRefOnly = 8309, - ERR_BadDynamicMethodArgDefaultLiteral = 9000, - ERR_DefaultLiteralNotValid = 9001, - WRN_DefaultInSwitch = 9002, + ERR_BadDynamicMethodArgDefaultLiteral = 8310, + ERR_DefaultLiteralNotValid = 8311, + WRN_DefaultInSwitch = 8312, } } diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index a7c09bb93d260..616e19367c623 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -2886,6 +2886,18 @@ public void ParseOut() // error CS8301: Do not use refout when using refonly. Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)); + parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/link:b", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8357: Cannot embed types when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + + parsedArgs = DefaultParse(new[] { "/refonly", "/link:b", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8357: Cannot embed types when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + parsedArgs = DefaultParse(new[] { "/refonly:incorrect", "a.cs" }, baseDirectory); parsedArgs.Errors.Verify( // error CS2007: Unrecognized option: '/refonly:incorrect' @@ -9067,6 +9079,11 @@ public static void Main() { System.Console.Write(""Hello""); } + /// Private method + private static void PrivateMethod() + { + System.Console.Write(""Private""); + } }"); var outWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -9081,7 +9098,7 @@ public static void Main() MetadataReaderUtils.VerifyPEMetadata(exe, new[] { "TypeDefinition:", "TypeDefinition:C" }, - new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, + new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void PrivateMethod()", "MethodDefinition:Void .ctor()" }, new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } ); @@ -9099,6 +9116,9 @@ public static void Main() Main method + + Private method + "; Assert.Equal(expectedDoc, content.Trim()); @@ -9170,6 +9190,16 @@ private event Action E1 remove { } } private event Action E2; + + /// Private Class Field + private int field; + + /// Private Struct + private struct S + { + /// Private Struct Field + private int field; + } }"); var outWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -9185,7 +9215,7 @@ private event Action E1 // The types and members that are included needs further refinement. // See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - new[] { "TypeDefinition:", "TypeDefinition:C" }, + new[] { "TypeDefinition:", "TypeDefinition:C", "TypeDefinition:S" }, new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" }, new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" } ); @@ -9207,10 +9237,20 @@ private event Action E1 Main method + + Private Class Field + + + Private Struct + + + Private Struct Field + "; Assert.Equal(expectedDoc, content.Trim()); + // Clean up temp files CleanupAllGeneratedFiles(dir.Path); } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 77ed494cf7dc7..dfe2050db72b5 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -261,11 +261,11 @@ internal static void Main() emitResult.Diagnostics.Verify(); VerifyEntryPoint(output, expectZero: false); - VerifyMethods(output, new[] { "void C.Main()", "C..ctor()" }); + VerifyMethods(output, "C", new[] { "void C.Main()", "C..ctor()" }); VerifyMvid(output, hasMvidSection: false); VerifyEntryPoint(metadataOutput, expectZero: true); - VerifyMethods(metadataOutput, new[] { "C..ctor()" }); + VerifyMethods(metadataOutput, "C", new[] { "C..ctor()" }); VerifyMvid(metadataOutput, hasMvidSection: true); } @@ -399,9 +399,9 @@ public class C Assert.True(emitResult.Success); emitResult.Diagnostics.Verify(); - VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateSetter.get", "void C.PrivateSetter.set", + VerifyMethods(output, "C", new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateSetter.get", "void C.PrivateSetter.set", "C..ctor()", "System.Int32 C.PrivateSetter { get; private set; }" }); - VerifyMethods(metadataOutput, new[] { "System.Int32 C.PrivateSetter.get", "C..ctor()", "System.Int32 C.PrivateSetter { get; }" }); + VerifyMethods(metadataOutput, "C", new[] { "System.Int32 C.PrivateSetter.get", "C..ctor()", "System.Int32 C.PrivateSetter { get; }" }); VerifyMvid(output, hasMvidSection: false); VerifyMvid(metadataOutput, hasMvidSection: true); } @@ -425,9 +425,9 @@ public class C Assert.True(emitResult.Success); emitResult.Diagnostics.Verify(); - VerifyMethods(output, new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateGetter.get", "void C.PrivateGetter.set", + VerifyMethods(output, "C", new[] { "System.Int32 C.k__BackingField", "System.Int32 C.PrivateGetter.get", "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { private get; set; }" }); - VerifyMethods(metadataOutput, new[] { "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { set; }" }); + VerifyMethods(metadataOutput, "C", new[] { "void C.PrivateGetter.set", "C..ctor()", "System.Int32 C.PrivateGetter { set; }" }); } } @@ -449,9 +449,9 @@ public class C Assert.True(emitResult.Success); emitResult.Diagnostics.Verify(); - VerifyMethods(output, new[] { "System.Int32 C.this[System.Int32 i].get", "void C.this[System.Int32 i].set", + VerifyMethods(output, "C", new[] { "System.Int32 C.this[System.Int32 i].get", "void C.this[System.Int32 i].set", "C..ctor()", "System.Int32 C.this[System.Int32 i] { private get; set; }" }); - VerifyMethods(metadataOutput, new[] { "void C.this[System.Int32 i].set", "C..ctor()", + VerifyMethods(metadataOutput, "C", new[] { "void C.this[System.Int32 i].set", "C..ctor()", "System.Int32 C.this[System.Int32 i] { set; }" }); } } @@ -478,9 +478,9 @@ public sealed override int Property { set { } } emitResult.Diagnostics.Verify(); Assert.True(emitResult.Success); - VerifyMethods(output, new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); + VerifyMethods(output, "C", new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); // A getter is synthesized on C.Property so that it can be marked as sealed. It is emitted despite being internal because it is virtual. - VerifyMethods(metadataOutput, new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); + VerifyMethods(metadataOutput, "C", new[] { "void C.Property.set", "C..ctor()", "System.Int32 C.Property.get", "System.Int32 C.Property { internal get; set; }" }); } } @@ -504,7 +504,7 @@ public event System.Action PrivateRemover { add { } private remove { } } ); } - private static void VerifyMethods(MemoryStream stream, string[] expectedMethods) + private static void VerifyMethods(MemoryStream stream, string containingType, string[] expectedMethods) { stream.Position = 0; var metadataRef = AssemblyMetadata.CreateFromImage(stream.ToArray()).GetReference(); @@ -514,7 +514,7 @@ private static void VerifyMethods(MemoryStream stream, string[] expectedMethods) AssertEx.Equal( expectedMethods, - compWithMetadata.GetMember("C").GetMembers().Select(m => m.ToTestDisplayString())); + compWithMetadata.GetMember(containingType).GetMembers().Select(m => m.ToTestDisplayString())); } [Fact] @@ -824,6 +824,10 @@ string M() )); } + /// + /// The client compilation should not be affected (except for some diagnostic differences) + /// by the library assembly only having metadata, or not including private members. + /// private void VerifyRefAssemblyClient(string lib_cs, string client_cs, Action validator, int debugFlag = -1) { // Whether the library is compiled in full, as metadata-only, or as a ref assembly should be transparent @@ -859,6 +863,55 @@ private static void VerifyRefAssemblyClient(string lib_cs, string source, Action validator(comp); } + [Theory] + [InlineData("")] + [InlineData(@"[assembly: System.Reflection.AssemblyVersion(""1"")]")] + [InlineData(@"[assembly: System.Reflection.AssemblyVersion(""1.0.*"")]")] + public void RefAssembly_EmitAsDeterministic(string source) + { + var comp = CreateStandardCompilation(source, options: TestOptions.DebugDll.WithDeterministic(false)); + + var (image1, refImage1) = emitRefOut(); + + // The resolution of the PE header time date stamp is seconds, and we want to make sure that has an opportunity to change + // between calls to Emit. + Thread.Sleep(TimeSpan.FromSeconds(1)); + var (image2, refImage2) = emitRefOut(); + var refImage3 = emitRefOnly(); + + AssertEx.Equal(refImage1, refImage2); + AssertEx.Equal(refImage1, refImage3); + + var comp2 = CreateStandardCompilation(source, options: TestOptions.DebugDll.WithDeterministic(false)); + var refImage4 = emitRefOnly(); + AssertEx.Equal(refImage1, refImage4); + + (ImmutableArray image, ImmutableArray refImage) emitRefOut() + { + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + var options = EmitOptions.Default.WithIncludePrivateMembers(false); + comp.VerifyEmitDiagnostics(); + var result = comp.Emit(output, metadataPEStream: metadataOutput, + options: options); + return (output.ToImmutable(), metadataOutput.ToImmutable()); + } + } + + ImmutableArray emitRefOnly() + { + using (var output = new MemoryStream()) + { + var options = EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false); + comp.VerifyEmitDiagnostics(); + var result = comp.Emit(output, + options: options); + return output.ToImmutable(); + } + } + } + [Theory] [InlineData("public int M() { error(); }", true)] [InlineData("public int M() { error() }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 @@ -1057,7 +1110,7 @@ public void EmitMetadataOnly_DisallowOutputtingNetModule() } [Fact] - public void EmitMetadata_AllowEmbedding() + public void RefAssembly_AllowEmbeddingPdb() { CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, options: TestOptions.DebugDll); @@ -1069,7 +1122,7 @@ public void EmitMetadata_AllowEmbedding() options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded).WithIncludePrivateMembers(false)); VerifyEmbeddedDebugInfo(output, new[] { DebugDirectoryEntryType.CodeView, DebugDirectoryEntryType.EmbeddedPortablePdb }); - VerifyEmbeddedDebugInfo(metadataOutput, new DebugDirectoryEntryType[] { }); + VerifyEmbeddedDebugInfo(metadataOutput, new DebugDirectoryEntryType[] { DebugDirectoryEntryType.Reproducible }); } void VerifyEmbeddedDebugInfo(MemoryStream stream, DebugDirectoryEntryType[] expected) @@ -1083,7 +1136,7 @@ void VerifyEmbeddedDebugInfo(MemoryStream stream, DebugDirectoryEntryType[] expe } [Fact] - public void EmitMetadataOnly_DisallowEmbedding() + public void EmitMetadataOnly_DisallowEmbeddingPdb() { CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, options: TestOptions.DebugDll); @@ -1096,6 +1149,30 @@ public void EmitMetadataOnly_DisallowEmbedding() } } + [Fact] + public void RefAssembly_DisallowEmbeddingTypes() + { + CSharpCompilation comp = CreateCompilation("", options: TestOptions.DebugDll, + references: new MetadataReference[] + { + TestReferences.SymbolsTests.NoPia.GeneralPia.WithEmbedInteropTypes(true) + }); + + using (var output = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + options: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false))); + } + + using (var output = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + metadataPEStream: metadataOutput, + options: EmitOptions.Default.WithIncludePrivateMembers(false))); + } + } + [Fact] public void EmitMetadata() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index f59dff07b54fc..b73c38922472a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -1434,7 +1434,7 @@ static void M1() var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); comp.VerifyDiagnostics( - // (7,14): error CS9000: Cannot use a default literal as an argument to a dynamically dispatched operation. + // (7,14): error CS8310: Cannot use a default literal as an argument to a dynamically dispatched operation. // d.M2(default); Diagnostic(ErrorCode.ERR_BadDynamicMethodArgDefaultLiteral, "default").WithLocation(7, 14) ); @@ -1524,7 +1524,7 @@ static void Main() var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (6,27): error CS9001: Use of default literal is not valid in this context + // (6,27): error CS8311: Use of default literal is not valid in this context // foreach (int x in default) { } Diagnostic(ErrorCode.ERR_DefaultLiteralNotValid, "default").WithLocation(6, 27), // (7,27): error CS0186: Use of null is not valid in this context @@ -1549,7 +1549,7 @@ static void Main() "; var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular7_1, references: new[] { SystemCoreRef }); compilation.VerifyDiagnostics( - // (6,35): error CS9001: Use of default literal is not valid in this context + // (6,35): error CS8311: Use of default literal is not valid in this context // var q = from x in default select x; Diagnostic(ErrorCode.ERR_DefaultLiteralNotValid, "select x").WithLocation(6, 35), // (7,43): error CS1942: The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'. @@ -1898,7 +1898,7 @@ static void M(int x) var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (12,18): warning CS9002: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + // (12,18): warning CS8312: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. // case default: Diagnostic(ErrorCode.WRN_DefaultInSwitch, "default").WithLocation(12, 18) ); @@ -1931,7 +1931,7 @@ static void M(object x) var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (12,18): warning CS9002: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + // (12,18): warning CS8312: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. // case default: Diagnostic(ErrorCode.WRN_DefaultInSwitch, "default").WithLocation(12, 18) ); diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index b2fc07f7f8952..d55296b59c2cc 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -183,9 +183,8 @@ static void Main() [Fact] public void WarningLevel_1() { - for (int i = 0; i < 10000; i++) + foreach (ErrorCode errorCode in Enum.GetValues(typeof(ErrorCode))) { - ErrorCode errorCode = (ErrorCode)i; string errorCodeName = errorCode.ToString(); if (errorCodeName.StartsWith("WRN", StringComparison.Ordinal)) { @@ -215,12 +214,15 @@ public void WarningLevel_2() Assert.Equal(3, ErrorFacts.GetWarningLevel(ErrorCode.WRN_IsDynamicIsConfusing)); Assert.Equal(2, ErrorFacts.GetWarningLevel(ErrorCode.WRN_NoSources)); - // There is space in the range of error codes from 7000-8999 that we might add for new errors in post-Dev10. // If a new warning is added, this test will fail and adding the new case with the expected error level will be required. - for (int i = 7000; i < 9000; i++) + foreach (ErrorCode errorCode in Enum.GetValues(typeof(ErrorCode))) { - ErrorCode errorCode = (ErrorCode)i; + if ((int)errorCode < 7000) + { + continue; + } + string errorCodeName = errorCode.ToString(); if (errorCodeName.StartsWith("WRN", StringComparison.Ordinal)) { @@ -244,6 +246,7 @@ public void WarningLevel_2() case ErrorCode.WRN_AlignmentMagnitude: case ErrorCode.WRN_TupleLiteralNameMismatch: case ErrorCode.WRN_Experimental: + case ErrorCode.WRN_DefaultInSwitch: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_MainIgnored: diff --git a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs index 5b12dc838c517..03b27b3c2904c 100644 --- a/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs +++ b/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs @@ -20,6 +20,11 @@ public sealed class CopyRefAssembly : Task [Required] public string DestinationPath { get; set; } + static CopyRefAssembly() + { + AssemblyResolution.Install(); + } + public CopyRefAssembly() { TaskResources = ErrorString.ResourceManager; @@ -45,20 +50,27 @@ public override bool Execute() Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_BadSource3", SourcePath, e.Message, e.StackTrace); } - try + if (source.Equals(Guid.Empty)) + { + Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_SourceNotRef1", SourcePath); + } + else { - Guid destination = ExtractMvid(DestinationPath); + try + { + Guid destination = ExtractMvid(DestinationPath); - if (source.Equals(destination)) + if (!source.Equals(Guid.Empty) && source.Equals(destination)) + { + Log.LogMessageFromResources(MessageImportance.Low, "CopyRefAssembly_SkippingCopy1", DestinationPath); + return true; + } + } + catch (Exception) { - Log.LogMessageFromResources(MessageImportance.Low, "CopyRefAssembly_SkippingCopy1", DestinationPath); - return true; + Log.LogMessageFromResources(MessageImportance.High, "CopyRefAssembly_BadDestination1", DestinationPath); } } - catch (Exception) - { - Log.LogMessage(MessageImportance.High, "CopyRefAssembly_BadDestination1", DestinationPath); - } } return Copy(); diff --git a/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs b/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs index 560a55decf577..d4db8b30d05b1 100644 --- a/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs +++ b/src/Compilers/Core/MSBuildTask/ErrorString.Designer.cs @@ -98,6 +98,15 @@ internal static string CopyRefAssembly_SkippingCopy1 { } } + /// + /// Looks up a localized string similar to Could not extract the MVID from "{0}". Are you sure it is a reference assembly?. + /// + internal static string CopyRefAssembly_SourceNotRef1 { + get { + return ResourceManager.GetString("CopyRefAssembly_SourceNotRef1", resourceCulture); + } + } + /// /// Looks up a localized string similar to MSB3053: The assembly alias "{1}" on reference "{0}" contains illegal characters.. /// diff --git a/src/Compilers/Core/MSBuildTask/ErrorString.resx b/src/Compilers/Core/MSBuildTask/ErrorString.resx index 9f6962ded7d75..ac0d63fc64e5e 100644 --- a/src/Compilers/Core/MSBuildTask/ErrorString.resx +++ b/src/Compilers/Core/MSBuildTask/ErrorString.resx @@ -154,6 +154,9 @@ Reference assembly "{0}" already has latest information. Leaving it untouched. + + Could not extract the MVID from "{0}". Are you sure it is a reference assembly? + Failed to check the content hash of the source ref assembly '{0}': {1} {2} diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 9a38358b5b06a..0a1d2c3f38c6a 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -477,6 +477,15 @@ internal static string EmbeddingPdbUnexpectedWhenEmittingMetadata { } } + /// + /// Looks up a localized string similar to Embedded interop type references should not be given when emitting a ref assembly.. + /// + internal static string EmbedInteropTypesUnexpectedWhenEmittingRefAssembly { + get { + return ResourceManager.GetString("EmbedInteropTypesUnexpectedWhenEmittingRefAssembly", resourceCulture); + } + } + /// /// Looks up a localized string similar to A key in the pathMap is empty.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 5843e7ac09b72..b0d49fe7d3b07 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -333,6 +333,9 @@ PDB stream should not be given when emitting metadata only. + + Embedded interop type references should not be given when emitting a ref assembly. + Metadata PE stream should not be given when emitting metadata only. diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index d1d232aa1fafc..be14eaa324e02 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -2087,6 +2087,13 @@ public EmitResult Emit( throw new ArgumentException(CodeAnalysisResources.IncludingPrivateMembersUnexpectedWhenEmittingToMetadataPeStream, nameof(metadataPEStream)); } + if ((metadataPEStream != null || options?.EmitMetadataOnly == true) && + options?.IncludePrivateMembers == false && + References.Any(r => r.Properties.EmbedInteropTypes)) + { + throw new ArgumentException(CodeAnalysisResources.EmbedInteropTypesUnexpectedWhenEmittingRefAssembly, nameof(metadataPEStream)); + } + if (options?.DebugInformationFormat == DebugInformationFormat.Embedded && options?.EmitMetadataOnly == true) { @@ -2636,6 +2643,7 @@ internal static bool SerializePeToStream( bool emitSecondaryAssembly = getMetadataPeStreamOpt != null; bool includePrivateMembersOnPrimaryOutput = metadataOnly ? includePrivateMembers : true; + bool deterministicPrimaryOutput = (metadataOnly && !includePrivateMembers) || isDeterministic; if (!Cci.PeWriter.WritePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics, metadataOnly, includePrivateMembersOnPrimaryOutput), messageProvider, @@ -2644,7 +2652,7 @@ internal static bool SerializePeToStream( nativePdbWriterOpt, pdbPathOpt, metadataOnly, - isDeterministic, + deterministicPrimaryOutput, emitTestCoverageData, cancellationToken)) { @@ -2665,7 +2673,7 @@ internal static bool SerializePeToStream( nativePdbWriterOpt: null, pdbPathOpt: null, metadataOnly: true, - isDeterministic: isDeterministic, + isDeterministic: true, emitTestCoverageData: false, cancellationToken: cancellationToken)) { diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index bdf7c957645d5..5cf8461f793e8 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -247,7 +247,7 @@ public uint GetFakeSymbolTokenForIL(Cci.IReference symbol, SyntaxNode syntaxNode uint token = _referencesInILMap.GetOrAddTokenFor(symbol, out added); if (added) { - ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true)); // PROTOTYPE(refout) Not sure about this + ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true)); } return token; } diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 5f58fd2094c52..029a9e8b343a9 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -1203,6 +1203,10 @@ lVbRuntimePlus: AddDiagnostic(diagnostics, ERRID.ERR_NoRefOutWhenRefOnly) End If + If (refOnly OrElse outputRefFileName IsNot Nothing) AndAlso metadataReferences.Any(Function(r) r.Properties.EmbedInteropTypes) Then + AddDiagnostic(diagnostics, ERRID.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly) + End If + If outputKind = OutputKind.NetModule AndAlso (refOnly OrElse outputRefFileName IsNot Nothing) Then AddDiagnostic(diagnostics, ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly) End If diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 5303649b024cf..e1354d47b40bd 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1733,6 +1733,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_NoRefOutWhenRefOnly = 37300 ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 37301 + ERR_NoEmbeddedTypeWhenRefOutOrRefOnly = 37302 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index 50815e2e104e2..19140e975e4dd 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -8070,6 +8070,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Cannot embed types when using /refout or /refonly.. + ''' + Friend ReadOnly Property ERR_NoEmbeddedTypeWhenRefOutOrRefOnly() As String + Get + Return ResourceManager.GetString("ERR_NoEmbeddedTypeWhenRefOutOrRefOnly", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Array bounds cannot appear in type specifiers.. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index d7239d6c732ba..aa0c8d2866e81 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5471,6 +5471,9 @@ Do not use refout when using refonly. + + Cannot embed types when using /refout or /refonly. + Cannot compile net modules when using /refout or /refonly. diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index c0aa1e3150f7d..0e3f43a3cdf81 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -2923,6 +2923,14 @@ print Goodbye, World" parsedArgs.Errors.Verify( Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/refout:ref.dll", "/link:b", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refonly", "/link:b", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/refonly", "/target:module", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify( Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) @@ -8188,6 +8196,10 @@ Public Class C Public Shared Sub Main() System.Console.Write(""Hello"") End Sub + ''' Private method + Private Shared Sub PrivateMethod() + System.Console.Write(""Private"") + End Sub End Class") Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) @@ -8202,7 +8214,7 @@ End Class") MetadataReaderUtils.VerifyPEMetadata(exe, {"TypeDefinition:", "TypeDefinition:C"}, - {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, + {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()", "MethodDefinition:Void PrivateMethod()"}, {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} ) @@ -8222,6 +8234,9 @@ a Main method + + Private method + " Assert.Equal(expectedDoc, content.Trim()) @@ -8295,6 +8310,14 @@ outWriter.ToString().Trim()) Public Shared Sub Main() Bad() End Sub + ''' Field + Private Dim field As Integer + + ''' Field + Private Structure S + ''' Struct Field + Private Dim field As Integer + End Structure End Class") Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) @@ -8310,7 +8333,7 @@ End Class") ' The types and members that are included needs further refinement. ' See issue https://github.com/dotnet/roslyn/issues/17612 MetadataReaderUtils.VerifyPEMetadata(refDll, - {"TypeDefinition:", "TypeDefinition:C"}, + {"TypeDefinition:", "TypeDefinition:C", "TypeDefinition:S"}, {"MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()"}, {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute", "ReferenceAssemblyAttribute"} ) @@ -8334,6 +8357,15 @@ a Main method + + Field + + + Field + + + Struct Field + " Assert.Equal(expectedDoc, content.Trim()) diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index f79bc3827226a..a2939a18115fa 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -886,6 +886,24 @@ End Class" End Using End Sub + + Public Sub RefAssembly_DisallowEmbeddingTypes() + Dim comp = CreateCompilation("", references:={MscorlibRef.WithEmbedInteropTypes(True)}, + options:=TestOptions.DebugDll) + + Using output As New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, + options:=EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False))) + End Using + + Using output As New MemoryStream() + Using metadataOutput = New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, metadataPEStream:=metadataOutput, + options:=EmitOptions.Default.WithIncludePrivateMembers(False))) + End Using + End Using + End Sub + Public Sub EmitMetadataOnly_DisallowMetadataPeStream() Dim comp = CreateCompilation("", references:={MscorlibRef}, From a68da9dbf8fb5cebb7461a271cf6c32d28261cec Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 9 May 2017 02:32:45 -0700 Subject: [PATCH 064/284] follow PR feedbacks --- ...cIncrementalAnalyzer.CompilationManager.cs | 2 +- .../Diagnostics/WorkspaceAnalyzerOptions.cs | 21 ++++++++++--------- .../Portable/Workspace/Solution/Document.cs | 4 ++-- .../Core/Diagnostics/DiagnosticComputer.cs | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 1d1e41a0ad5f0..a446155b3d447 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -118,7 +118,7 @@ private CompilationWithAnalyzersOptions GetAnalyzerOptions( // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with syncronous blocking concurrency. return new CompilationWithAnalyzersOptions( - options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Options, project.Solution.Workspace), + options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Options, project.Solution), onAnalyzerException: GetOnAnalyzerException(project.Id), analyzerExceptionFilter: GetAnalyzerExceptionFilter(project), concurrentAnalysis: false, diff --git a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs index 6bcc2c4d4e32e..483d78af05671 100644 --- a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs +++ b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs @@ -14,34 +14,33 @@ namespace Microsoft.CodeAnalysis.Diagnostics /// internal sealed class WorkspaceAnalyzerOptions : AnalyzerOptions { - private readonly Workspace _workspace; + private readonly Solution _solution; private readonly OptionSet _optionSet; - public WorkspaceAnalyzerOptions(AnalyzerOptions options, OptionSet optionSet, Workspace workspace) + public WorkspaceAnalyzerOptions(AnalyzerOptions options, OptionSet optionSet, Solution solution) : base(options.AdditionalFiles) { - _workspace = workspace; + _solution = solution; _optionSet = optionSet; } - public HostWorkspaceServices Services => _workspace.Services; + public HostWorkspaceServices Services => _solution.Workspace.Services; public async Task GetDocumentOptionSetAsync(SyntaxTree syntaxTree, CancellationToken cancellationToken) { - var documentId = _workspace.CurrentSolution.GetDocumentId(syntaxTree); + var documentId = _solution.GetDocumentId(syntaxTree); if (documentId == null) { return _optionSet; } - var document = _workspace.CurrentSolution.GetDocument(documentId); + var document = _solution.GetDocument(documentId); if (document == null) { return _optionSet; } - var documentOptionSet = await document.GetOptionsAsync(_optionSet, cancellationToken).ConfigureAwait(false); - return documentOptionSet ?? _optionSet; + return await document.GetOptionsAsync(_optionSet, cancellationToken).ConfigureAwait(false); } public override bool Equals(object obj) @@ -53,13 +52,15 @@ public override bool Equals(object obj) var other = obj as WorkspaceAnalyzerOptions; return other != null && - _workspace == other._workspace && + _solution.WorkspaceVersion == other._solution.WorkspaceVersion && + _solution.Workspace == other._solution.Workspace && base.Equals(other); } public override int GetHashCode() { - return Hash.Combine(_workspace, base.GetHashCode()); + return Hash.Combine(_solution.Workspace, + Hash.Combine(_solution.WorkspaceVersion, base.GetHashCode())); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index 58360061cf39c..ce633d9b5b565 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -467,7 +467,7 @@ private string GetDebuggerDisplay() return GetOptionsAsync(Project.Solution.Options, cancellationToken); } - internal Task GetOptionsAsync(OptionSet globalOptionSet, CancellationToken cancellationToken) + internal Task GetOptionsAsync(OptionSet solutionOptions, CancellationToken cancellationToken) { // TODO: we have this workaround since Solution.Options is not actually snapshot but just return Workspace.Options which violate snapshot model. // this doesn't validate whether same optionset is given to invalidate the cache or not. this is not new since existing implementation @@ -478,7 +478,7 @@ internal Task GetOptionsAsync(OptionSet globalOptionSet, Canc var newAsyncLazy = new AsyncLazy(async c => { var optionsService = Project.Solution.Workspace.Services.GetRequiredService(); - var documentOptionSet = await optionsService.GetUpdatedOptionSetForDocumentAsync(this, globalOptionSet, c).ConfigureAwait(false); + var documentOptionSet = await optionsService.GetUpdatedOptionSetForDocumentAsync(this, solutionOptions, c).ConfigureAwait(false); return new DocumentOptionSet(documentOptionSet, Project.Language); }, cacheResult: true); diff --git a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs index 6bc968139429d..9d53a7a5e49ec 100644 --- a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs @@ -75,7 +75,7 @@ private async Task Date: Tue, 9 May 2017 02:36:32 -0700 Subject: [PATCH 065/284] remove unused usings. --- .../RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs index 3071ba2917d1b..2a85108a87d22 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Internal.Log; -using System.Diagnostics; namespace Microsoft.VisualStudio.LanguageServices.Remote { From e4b4ea4ccdd93e7348b1803d5c60109854858d5f Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 9 May 2017 05:15:05 -0700 Subject: [PATCH 066/284] made document checksum not to use persistant service. we found 2 issues. 1. hard to validate persisted data and 2. recalculating checksum is faster than reading from storage. --- .../Test.Next/Services/AssetServiceTests.cs | 1 + .../Services/ServiceHubServicesTests.cs | 1 + .../Services/SolutionServiceTests.cs | 1 + .../Test.Next/VisualStudioTest.Next.csproj | 1 - .../Core/Portable/Log/FunctionId.cs | 71 ++++++---- .../Solution/TextDocumentState_Checksum.cs | 85 +----------- .../Remote/Core/RemoteWorkspaces.csproj | 1 + .../Remote/Core/Services/SolutionCreator.cs | 68 +++++++++- src/Workspaces/Remote/Core/TestUtils.cs | 125 ++++++++++++++++++ 9 files changed, 238 insertions(+), 116 deletions(-) create mode 100644 src/Workspaces/Remote/Core/TestUtils.cs diff --git a/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs index 85dec43674b29..e9e949f0946b6 100644 --- a/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Remote.DebugUtil; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Roslyn.VisualStudio.Next.UnitTests.Mocks; diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index ca558d84ed835..94df904ea4f90 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Remote.DebugUtil; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.TodoComments; using Roslyn.Test.Utilities; diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 69aebcfb19175..5c667179a4681 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Remote.DebugUtil; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.Remote; diff --git a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj index 6cb0c1dfabd0c..b076f92a0ab82 100644 --- a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj +++ b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj @@ -263,7 +263,6 @@ - diff --git a/src/Workspaces/Core/Portable/Log/FunctionId.cs b/src/Workspaces/Core/Portable/Log/FunctionId.cs index 608bc297dbe10..2ea627eba7b32 100644 --- a/src/Workspaces/Core/Portable/Log/FunctionId.cs +++ b/src/Workspaces/Core/Portable/Log/FunctionId.cs @@ -327,50 +327,69 @@ internal enum FunctionId BKTree_ExceptionInCacheRead, IntellisenseBuild_Failed, + FileTextLoader_FileLengthThresholdExceeded, + // Generic performance measurement action IDs MeasurePerformance_StartAction, MeasurePerformance_StopAction, - RemoteHostClientService_AddGlobalAssetsAsync, - RemoteHostClientService_RemoveGlobalAssets, - RemoteHostClientService_Enabled, - ServiceHubRemoteHostClient_CreateAsync, - SolutionSynchronizationServiceFactory_CreatePinnedRemotableDataScopeAsync, - PinnedRemotableDataScope_GetRemotableData, Serializer_CreateChecksum, Serializer_Serialize, Serializer_Deserialize, + CodeAnalysisService_CalculateDiagnosticsAsync, CodeAnalysisService_SerializeDiagnosticResultAsync, - AssetStorage_CleanAssets, - AssetService_GetAssetAsync, - SnapshotService_RequestAssetAsync, - CompilationService_GetCompilationAsync, - RemoteHostService_SynchronizePrimaryWorkspaceAsync, - RemoteHostService_SynchronizeGlobalAssetsAsync, - AssetStorage_TryGetAsset, - AssetService_SynchronizeAssetsAsync, - AssetService_SynchronizeSolutionAssetsAsync, CodeAnalysisService_GetReferenceCountAsync, CodeAnalysisService_FindReferenceLocationsAsync, CodeAnalysisService_FindReferenceMethodsAsync, CodeAnalysisService_GetFullyQualifiedName, - SolutionChecksumUpdater_SynchronizePrimaryWorkspace, - SolutionState_ComputeChecksumsAsync, - ProjectState_ComputeChecksumsAsync, - DocumentState_ComputeChecksumsAsync, - JsonRpcSession_RequestAssetAsync, - SolutionSynchronizationService_GetRemotableData, - AssetService_SynchronizeProjectAssetsAsync, - FileTextLoader_FileLengthThresholdExceeded, + CodeAnalysisService_GetTodoCommentsAsync, + CodeAnalysisService_GetDesignerAttributesAsync, + + ServiceHubRemoteHostClient_CreateAsync, + PinnedRemotableDataScope_GetRemotableData, + RemoteHost_Connect, RemoteHost_Disconnect, - CodeAnalysisService_GetTodoCommentsAsync, + + RemoteHostClientService_AddGlobalAssetsAsync, + RemoteHostClientService_RemoveGlobalAssets, + RemoteHostClientService_Enabled, + RemoteHostClientService_Restarted, + + RemoteHostService_SynchronizePrimaryWorkspaceAsync, + RemoteHostService_SynchronizeGlobalAssetsAsync, + + AssetStorage_CleanAssets, + AssetStorage_TryGetAsset, + + AssetService_GetAssetAsync, + AssetService_SynchronizeAssetsAsync, + AssetService_SynchronizeSolutionAssetsAsync, + AssetService_SynchronizeProjectAssetsAsync, + CodeLens_GetReferenceCountAsync, CodeLens_FindReferenceLocationsAsync, CodeLens_FindReferenceMethodsAsync, CodeLens_GetFullyQualifiedName, - RemoteHostClientService_Restarted, - CodeAnalysisService_GetDesignerAttributesAsync, + + SolutionState_ComputeChecksumsAsync, + ProjectState_ComputeChecksumsAsync, + DocumentState_ComputeChecksumsAsync, + + SolutionSynchronizationService_GetRemotableData, + SolutionSynchronizationServiceFactory_CreatePinnedRemotableDataScopeAsync, + + SolutionChecksumUpdater_SynchronizePrimaryWorkspace, + + JsonRpcSession_RequestAssetAsync, + + SolutionService_GetSolutionAsync, + SolutionService_UpdatePrimaryWorkspaceAsync, + + SnapshotService_RequestAssetAsync, + + CompilationService_GetCompilationAsync, + SolutionCreator_AssetDifferences, } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs index 99f6b6e840625..7caf67306c022 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs @@ -1,22 +1,14 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Serialization; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Versions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { internal partial class TextDocumentState { - private const string TextChecksum = nameof(TextChecksum); - private const string SerializationFormat = "1"; - public bool TryGetStateChecksums(out DocumentStateChecksums stateChecksums) { return _lazyChecksums.TryGetValue(out stateChecksums); @@ -42,85 +34,10 @@ private async Task ComputeChecksumsAsync(CancellationTok var serializer = new Serializer(solutionServices.Workspace); var infoChecksum = serializer.CreateChecksum(Info.Attributes, cancellationToken); - var textChecksum = await GetTextChecksumAsync(serializer, await textAndVersionTask.ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var textChecksum = serializer.CreateChecksum((await textAndVersionTask.ConfigureAwait(false)).Text, cancellationToken); return new DocumentStateChecksums(infoChecksum, textChecksum); } } - - private async Task GetTextChecksumAsync(Serializer serializer, TextAndVersion textAndVersion, CancellationToken cancellationToken) - { - // calculating checksum for source text is one of most expansive checksum calculation we need to do. - // this should let us get around it if possible - var solution = solutionServices.Workspace.CurrentSolution; - - var document = solution.GetDocument(Id); - if (document == null) - { - // can't use persistent service since it is based on solution objects - return serializer.CreateChecksum(textAndVersion.Text, cancellationToken); - } - - var storage = solution.Workspace.Services.GetService()?.GetStorage(solution); - if (storage == null) - { - // persistent service not available - return serializer.CreateChecksum(textAndVersion.Text, cancellationToken); - } - - try - { - using (var stream = await storage.ReadStreamAsync(document, TextChecksum, cancellationToken).ConfigureAwait(false)) - using (var reader = ObjectReader.TryGetReader(stream)) - { - if (TryReadVersion(reader, out var persistedVersion) && - document.CanReusePersistedTextVersion(textAndVersion.Version, persistedVersion)) - { - return Checksum.ReadFrom(reader); - } - } - - // either checksum doesn't exist or can't reuse. re-calculate the checksum - var checksum = serializer.CreateChecksum(textAndVersion.Text, cancellationToken); - - // save newly calculated checksum - using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken)) - { - WriteVersionsTo(writer, textAndVersion.Version); - checksum.WriteTo(writer); - - stream.Position = 0; - await storage.WriteStreamAsync(document, TextChecksum, stream, cancellationToken).ConfigureAwait(false); - } - - return checksum; - } - catch (Exception e) when (IOUtilities.IsNormalIOException(e)) - { - // Storage APIs can throw arbitrary exceptions. - } - - // go simple route if persistent thing didn't work out - return serializer.CreateChecksum(textAndVersion.Text, cancellationToken); - } - - private void WriteVersionsTo(ObjectWriter writer, VersionStamp version) - { - writer.WriteString(SerializationFormat); - version.WriteTo(writer); ; - } - - private static bool TryReadVersion(ObjectReader reader, out VersionStamp persistedVersion) - { - persistedVersion = VersionStamp.Default; - if (reader?.ReadString() != SerializationFormat) - { - return false; - } - - persistedVersion = VersionStamp.ReadFrom(reader); - return true; - } } } diff --git a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj index 4f6971b6f9ae3..3790331862d61 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj +++ b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj @@ -78,6 +78,7 @@ + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/Services/SolutionCreator.cs b/src/Workspaces/Remote/Core/Services/SolutionCreator.cs index ab44bd54b1567..a785663ffafeb 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionCreator.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionCreator.cs @@ -1,15 +1,16 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Remote.DebugUtil; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote @@ -79,10 +80,8 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum) solution = await UpdateProjectsAsync(solution, oldSolutionChecksums.Projects, newSolutionChecksums.Projects).ConfigureAwait(false); } -#if DEBUG // make sure created solution has same checksum as given one - Contract.Requires(newSolutionChecksum == await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false)); -#endif + await ValidateChecksumAsync(newSolutionChecksum, solution).ConfigureAwait(false); return solution; } @@ -613,5 +612,64 @@ private ImmutableArray GetStrongNameKeyPaths(ProjectInfo.ProjectAttribut return builder.ToImmutableAndFree(); } + + private async Task ValidateChecksumAsync(Checksum givenSolutionChecksum, Solution solution) + { + // have this to avoid error on async + await SpecializedTasks.EmptyTask; + +#if DEBUG + var currentSolutionChecksum = await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false); + + if (givenSolutionChecksum == currentSolutionChecksum) + { + return; + } + + Contract.Requires(false, "checksum not same"); + + var map = solution.GetAssetMap(); + await RemoveDuplicateChecksumsAsync(givenSolutionChecksum, map).ConfigureAwait(false); + + foreach (var kv in map.Where(kv => kv.Value is ChecksumWithChildren).ToList()) + { + map.Remove(kv.Key); + } + + var sb = new StringBuilder(); + foreach (var kv in map) + { + sb.AppendLine($"{kv.Key.ToString()}, {kv.Value.ToString()}"); + } + + Logger.Log(FunctionId.SolutionCreator_AssetDifferences, sb.ToString()); +#endif + + return; + } + + private async Task RemoveDuplicateChecksumsAsync(Checksum givenSolutionChecksum, Dictionary map) + { + var solutionChecksums = await _assetService.GetAssetAsync(givenSolutionChecksum, _cancellationToken).ConfigureAwait(false); + map.RemoveChecksums(solutionChecksums); + + foreach (var projectChecksum in solutionChecksums.Projects) + { + var projectChecksums = await _assetService.GetAssetAsync(projectChecksum, _cancellationToken).ConfigureAwait(false); + map.RemoveChecksums(projectChecksums); + + foreach (var documentChecksum in projectChecksums.Documents) + { + var documentChecksums = await _assetService.GetAssetAsync(documentChecksum, _cancellationToken).ConfigureAwait(false); + map.RemoveChecksums(documentChecksums); + } + + foreach (var documentChecksum in projectChecksums.AdditionalDocuments) + { + var documentChecksums = await _assetService.GetAssetAsync(documentChecksum, _cancellationToken).ConfigureAwait(false); + map.RemoveChecksums(documentChecksums); + } + } + } } } diff --git a/src/Workspaces/Remote/Core/TestUtils.cs b/src/Workspaces/Remote/Core/TestUtils.cs new file mode 100644 index 0000000000000..8010164270c98 --- /dev/null +++ b/src/Workspaces/Remote/Core/TestUtils.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using Microsoft.CodeAnalysis.Serialization; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Remote.DebugUtil +{ + internal static class TestUtils + { + public static void RemoveChecksums(this Dictionary map, ChecksumWithChildren checksums) + { + map.Remove(checksums.Checksum); + + foreach (var child in checksums.Children) + { + var checksum = child as Checksum; + if (checksum != null) + { + map.Remove(checksum); + } + + var collection = child as ChecksumCollection; + if (collection != null) + { + foreach (var item in collection) + { + map.Remove(item); + } + } + } + } + + public static Dictionary GetAssetMap(this Solution solution) + { + var map = new Dictionary(); + + AppendAssetMap(solution, map); + + return map; + } + + public static Dictionary GetAssetMap(this Project project) + { + var map = new Dictionary(); + + AppendAssetMap(project, map); + + return map; + } + + public static void AppendAssetMap(this Solution solution, Dictionary map) + { + SolutionStateChecksums solutionChecksums; + Contract.Requires(solution.State.TryGetStateChecksums(out solutionChecksums)); + + solutionChecksums.Find(solution.State, Flatten(solutionChecksums), map, CancellationToken.None); + + foreach (var project in solution.Projects) + { + AppendAssetMap(project, map); + } + } + + private static void AppendAssetMap(Project project, Dictionary map) + { + ProjectStateChecksums projectChecksums; + if (!project.State.TryGetStateChecksums(out projectChecksums)) + { + Contract.Requires(!RemoteSupportedLanguages.IsSupported(project.Language)); + return; + } + + projectChecksums.Find(project.State, Flatten(projectChecksums), map, CancellationToken.None); + + foreach (var document in project.Documents) + { + AppendAssetMap(document, map); + } + + foreach (var document in project.AdditionalDocuments) + { + AppendAssetMap(document, map); + } + } + + private static void AppendAssetMap(TextDocument document, Dictionary map) + { + DocumentStateChecksums documentChecksums; + Contract.Requires(document.State.TryGetStateChecksums(out documentChecksums)); + + documentChecksums.Find(document.State, Flatten(documentChecksums), map, CancellationToken.None); + + // fix up due to source text can't be obtained synchronously in product code + map[documentChecksums.Text] = document.State.GetTextSynchronously(CancellationToken.None); + } + + private static HashSet Flatten(ChecksumWithChildren checksums) + { + var set = new HashSet(); + set.Add(checksums.Checksum); + + foreach (var child in checksums.Children) + { + var checksum = child as Checksum; + if (checksum != null) + { + set.Add(checksum); + } + + var collection = child as ChecksumCollection; + if (collection != null) + { + foreach (var item in collection) + { + set.Add(item); + } + } + } + + return set; + } + } +} From d0490d7c86c43fa72afad2bfd476d6a1e30530f9 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 9 May 2017 06:15:03 -0700 Subject: [PATCH 067/284] fix test failures --- .../Remote/InProcRemostHostClient.cs | 2 +- .../Services/SolutionServiceTests.cs | 28 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs b/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs index c185f1bfa2788..3d20c8ed0c12f 100644 --- a/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs @@ -65,7 +65,7 @@ protected override async Task TryCreateServiceSessionAsync(string servi // this is the back channel the system uses to move data between VS and remote host var snapshotStream = getSnapshotAsync.Value == null ? null : await _inprocServices.RequestServiceAsync(WellKnownServiceHubServices.SnapshotService, cancellationToken).ConfigureAwait(false); - // get stream from service hub to communicate service specific information + // get stream from service hub to communicate service specific information // this is what consumer actually use to communicate information var serviceStream = await _inprocServices.RequestServiceAsync(serviceName, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 66c465789996c..0d5aaf3a39ad7 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -40,22 +40,30 @@ public async Task TestCreation() [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestGetSolutionWithPrimaryFlag() { - var code = @"class Test { void Method() { } }"; + var code1 = @"class Test1 { void Method() { } }"; - using (var workspace = TestWorkspace.CreateCSharp(code)) + using (var workspace = TestWorkspace.CreateCSharp(code1)) { var solution = workspace.CurrentSolution; var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - var service1 = await GetSolutionServiceAsync(solution); - var synched1 = await service1.GetSolutionAsync(solutionChecksum, CancellationToken.None); - Assert.Equal(solutionChecksum, await synched1.State.GetChecksumAsync(CancellationToken.None)); - Assert.True(synched1.Workspace is TemporaryWorkspace); + var service = await GetSolutionServiceAsync(solution); + var synched = await service.GetSolutionAsync(solutionChecksum, CancellationToken.None); + Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); + Assert.True(synched.Workspace is TemporaryWorkspace); + } + + var code2 = @"class Test2 { void Method() { } }"; - var service2 = await GetSolutionServiceAsync(solution); - var synched2 = await service2.GetSolutionAsync(solutionChecksum, primary: true, cancellationToken: CancellationToken.None); - Assert.Equal(solutionChecksum, await synched2.State.GetChecksumAsync(CancellationToken.None)); - Assert.True(synched2.Workspace is RemoteWorkspace); + using (var workspace = TestWorkspace.CreateCSharp(code2)) + { + var solution = workspace.CurrentSolution; + var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); + + var service = await GetSolutionServiceAsync(solution); + var synched = await service.GetSolutionAsync(solutionChecksum, primary: true, cancellationToken: CancellationToken.None); + Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); + Assert.True(synched.Workspace is RemoteWorkspace); } } From ddd68b6e04dfd1d936b4b01aafac700041f3cea8 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 9 May 2017 10:04:15 -0700 Subject: [PATCH 068/284] fix test failure --- src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs | 2 +- .../Remote/ServiceHub/Shared/RoslynJsonConverter.cs | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs index 1144f558da455..5ce8f5f5472f7 100644 --- a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs +++ b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs @@ -92,7 +92,7 @@ private async Task InitializeAsync() await _snapshotClientOpt.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt.Primary, PinnedScopeOpt.SolutionChecksum).ConfigureAwait(false); } - await _serviceClient.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt?.Primary, PinnedScopeOpt?.SolutionChecksum).ConfigureAwait(false); + await _serviceClient.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt?.Primary ?? false, PinnedScopeOpt?.SolutionChecksum).ConfigureAwait(false); } public override Task InvokeAsync(string targetName, params object[] arguments) diff --git a/src/Workspaces/Remote/ServiceHub/Shared/RoslynJsonConverter.cs b/src/Workspaces/Remote/ServiceHub/Shared/RoslynJsonConverter.cs index 372cc037f2766..a0478e654bbb7 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/RoslynJsonConverter.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/RoslynJsonConverter.cs @@ -127,11 +127,16 @@ private class ChecksumJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => typeof(Checksum) == objectType; - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => - new Checksum(Convert.FromBase64String((string)reader.Value)); + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var value = (string)reader.Value; + return value == null ? null : new Checksum(Convert.FromBase64String(value)); + } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => - writer.WriteValue(value.ToString()); + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value?.ToString()); + } } } } From 3986cdb6c061ecc79680967e1a7757e64b6888f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 9 May 2017 10:39:27 -0700 Subject: [PATCH 069/284] Implement SourceLink for Windows PDBs (#18539) --- docs/compilers/CSharp/CommandLine.md | 6 +- docs/compilers/Visual Basic/CommandLine.md | 7 +- .../Portable/CSharpResources.Designer.cs | 6 +- .../CSharp/Portable/CSharpResources.resx | 6 +- .../CommandLine/CSharpCommandLineParser.cs | 7 +- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../Test/CommandLine/CommandLineTests.cs | 46 ++++-- .../Test/Emit/CSharpCompilerEmitTest.csproj | 1 + .../Test/Emit/PDB/PDBSourceLinkTests.cs | 153 ++++++++++++++++++ .../Symbol/Compilation/CompilationAPITests.cs | 12 -- .../StreamExtensionsTests.cs | 43 ++++- .../CodeAnalysisResources.Designer.cs | 18 +-- .../Core/Portable/CodeAnalysisResources.resx | 6 +- .../Core/Portable/Compilation/Compilation.cs | 16 +- .../InternalUtilities/StreamExtensions.cs | 25 +++ .../NativePdbWriter/ISymUnmanagedWriter.cs | 9 +- .../Portable/NativePdbWriter/PdbWriter.cs | 37 ++++- .../PEWriter/MetadataWriter.PortablePdb.cs | 8 +- .../Core/Portable/PEWriter/PeWriter.cs | 5 + .../VisualBasicCommandLineParser.vb | 6 +- .../VisualBasic/Portable/Errors/Errors.vb | 2 +- .../Portable/VBResources.Designer.vb | 6 +- .../VisualBasic/Portable/VBResources.resx | 6 +- .../Test/CommandLine/CommandLineTests.vb | 18 ++- .../Compilation/CompilationAPITests.vb | 12 -- src/Test/PdbUtilities/Pdb/SymReaderFactory.cs | 20 +-- .../Utilities/Portable/Pdb/PdbValidation.cs | 29 +++- 27 files changed, 393 insertions(+), 119 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs diff --git a/docs/compilers/CSharp/CommandLine.md b/docs/compilers/CSharp/CommandLine.md index 8244ce7bfc71e..6af4c2f7ea19d 100644 --- a/docs/compilers/CSharp/CommandLine.md +++ b/docs/compilers/CSharp/CommandLine.md @@ -29,7 +29,11 @@ | `/linkresource`:*resinfo* | Link the specified resource to this assembly (Short form: `/linkres`) Where the *resinfo* format is *file*{`,`*string name*{`,``public``|``private`}} | **CODE GENERATION** | `/debug`{`+`|`-`} | Emit (or do not Emit) debugging information -| `/debug`:{`full`|`pdbonly`|`portable`} | Specify debugging type (`full` is default, and enables attaching a debugger to a running program. `portable` is a cross-platform format) +| `/debug`:`full` | Emit debugging information to .pdb file using default format for the current platform: _Windows PDB_ on Windows, _Portable PDB_ on other systems +| `/debug`:`pdbonly` | Same as `/debug:full`. For backward compatibility. +| `/debug`:`portable` | Emit debugging information to to .pdb file using cross-platform [Portable PDB format](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/portable_pdb.md) +| `/debug`:`embedded` | Emit debugging information into the .dll/.exe itself (.pdb file is not produced) using [Portable PDB format](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/portable_pdb.md). +| `/sourcelink`:*file* | [Source link](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/source_link.md) info to embed into PDB. | `/optimize`{`+`|`-`} | Enable optimizations (Short form: `/o`) | `/deterministic` | Produce a deterministic assembly (including module version GUID and timestamp) | **ERRORS AND WARNINGS** diff --git a/docs/compilers/Visual Basic/CommandLine.md b/docs/compilers/Visual Basic/CommandLine.md index 0a7afcefd67da..4c52e339fce47 100644 --- a/docs/compilers/Visual Basic/CommandLine.md +++ b/docs/compilers/Visual Basic/CommandLine.md @@ -27,9 +27,14 @@ | `/win32manifest:`*file* | The provided file is embedded in the manifest section of the output PE. | `/win32resource:`*file* | Specifies a Win32 resource file (.res). | **CODE GENERATION** +| `/debug`{`+`|`-`} | Emit debugging information. +| `/debug`:`full` | Emit debugging information to .pdb file using default format for the current platform: _Windows PDB_ on Windows, _Portable PDB_ on other systems +| `/debug`:`pdbonly` | Same as `/debug:full`. For backward compatibility. +| `/debug`:`portable` | Emit debugging information to to .pdb file using cross-platform [Portable PDB format](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/portable_pdb.md) +| `/debug`:`embedded` | Emit debugging information into the .dll/.exe itself (.pdb file is not produced) using [Portable PDB format](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/portable_pdb.md). +| `/sourcelink`:*file* | [Source link](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/source_link.md) info to embed into PDB. | `/optimize`{`+`|`-`} | Enable optimizations. | `/removeintchecks`{`+`|`-`} | Remove integer checks. Default off. -| `/debug`{`+`|`-`} | Emit debugging information. | `/debug:full` | Emit full debugging information (default). | `/debug:portable` | Emit debugging information in the portable format. | `/debug:pdbonly` | Emit PDB file only. diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 65d87f9cc8232..7a7b71a958a5d 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -8675,11 +8675,11 @@ internal static string ERR_SourceFileReferencesNotSupported { } /// - /// Looks up a localized string similar to /sourcelink switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded must be specified).. + /// Looks up a localized string similar to /sourcelink switch is only supported when emitting PDB.. /// - internal static string ERR_SourceLinkRequiresPortablePdb { + internal static string ERR_SourceLinkRequiresPdb { get { - return ResourceManager.GetString("ERR_SourceLinkRequiresPortablePdb", resourceCulture); + return ResourceManager.GetString("ERR_SourceLinkRequiresPdb", resourceCulture); } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 13a64ea0367ed..9009bd391d8fe 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4450,7 +4450,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ (including module version GUID and timestamp) /instrument:TestCoverage Produce an assembly instrumented to collect coverage information - /sourcelink:<file> Source link info to embed into Portable PDB. + /sourcelink:<file> Source link info to embed into PDB. - ERRORS AND WARNINGS - /warnaserror[+|-] Report all warnings as errors @@ -4971,8 +4971,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ An expression tree may not contain a tuple conversion. - - /sourcelink switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded must be specified). + + /sourcelink switch is only supported when emitting PDB. /embed switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded). diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index c14b627d769c1..deb01386ff63e 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -1193,12 +1193,9 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar keyFileSetting = ParseGenericPathToFile(keyFileSetting, diagnostics, baseDirectory); } - if (sourceLink != null) + if (sourceLink != null && !emitPdb) { - if (!emitPdb || !debugInformationFormat.IsPortable()) - { - AddDiagnostic(diagnostics, ErrorCode.ERR_SourceLinkRequiresPortablePdb); - } + AddDiagnostic(diagnostics, ErrorCode.ERR_SourceLinkRequiresPdb); } if (embedAllSourceFiles) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 6c6cbeb3ee268..3ed35efce1427 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1060,7 +1060,7 @@ internal enum ErrorCode ERR_InvalidOutputName = 2041, ERR_InvalidDebugInformationFormat = 2042, ERR_LegacyObjectIdSyntax = 2043, - ERR_SourceLinkRequiresPortablePdb = 2044, + ERR_SourceLinkRequiresPdb = 2044, ERR_CannotEmbedWithoutPdb = 2045, // unused 2046-2999 WRN_CLS_NoVarArgs = 3000, diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index b63e0120146af..9f9ed6865138b 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -25,9 +25,8 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; - -using static Roslyn.Test.Utilities.SharedResourceHelpers; using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers; +using static Roslyn.Test.Utilities.SharedResourceHelpers; namespace Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests { @@ -1725,19 +1724,19 @@ public void SourceLink() Assert.Equal(Path.Combine(_baseDirectory, "s l.json"), parsedArgs.SourceLink); parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug:full", "a.cs" }, _baseDirectory); - parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPortablePdb)); + parsedArgs.Errors.Verify(); parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug:pdbonly", "a.cs" }, _baseDirectory); - parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPortablePdb)); + parsedArgs.Errors.Verify(); parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug-", "a.cs" }, _baseDirectory); - parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPortablePdb)); + parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPdb)); parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug+", "a.cs" }, _baseDirectory); - parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPortablePdb)); + parsedArgs.Errors.Verify(); parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "a.cs" }, _baseDirectory); - parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPortablePdb)); + parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPdb)); } [Fact] @@ -1800,6 +1799,31 @@ public void SourceLink_EndToEnd_Portable() CleanupAllGeneratedFiles(src.Path); } + [Fact] + public void SourceLink_EndToEnd_Windows() + { + var dir = Temp.CreateDirectory(); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@"class C { public static void Main() {} }"); + + var sl = dir.CreateFile("sl.json"); + byte[] slContent = Encoding.UTF8.GetBytes(@"{ ""documents"" : {} }"); + sl.WriteAllBytes(slContent); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, new[] { "/nologo", "/debug:full", "/sourcelink:sl.json", "a.cs" }); + int exitCode = csc.Run(outWriter); + Assert.Equal(0, exitCode); + + var pdbStream = File.OpenRead(Path.Combine(dir.Path, "a.pdb")); + var actualData = PdbValidation.GetSourceLinkData(pdbStream); + AssertEx.Equal(slContent, actualData); + + // Clean up temp files + CleanupAllGeneratedFiles(src.Path); + } + [Fact] public void Embed() { @@ -7275,12 +7299,14 @@ public void IOFailure_DisposeXmlFile() Assert.Equal($"error CS0016: Could not write to output file '{xmlPath}' -- 'Fake IOException'{Environment.NewLine}", outWriter.ToString()); } - [Fact] - public void IOFailure_DisposeSourceLinkFile() + [Theory] + [InlineData("portable")] + [InlineData("full")] + public void IOFailure_DisposeSourceLinkFile(string format) { var srcPath = MakeTrivialExe(Temp.CreateDirectory().Path); var sourceLinkPath = Path.Combine(Path.GetDirectoryName(srcPath), "test.json"); - var csc = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/debug:portable", $"/sourcelink:{sourceLinkPath}", srcPath }); + var csc = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/debug:" + format, $"/sourcelink:{sourceLinkPath}", srcPath }); csc.FileOpen = (file, mode, access, share) => { if (file == sourceLinkPath) diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index 0d8951495e9e2..2a8c915886eb3 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -159,6 +159,7 @@ + diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs new file mode 100644 index 0000000000000..0afa594315b6b --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Debugging; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.PDB +{ + public class PDBSourceLinkTests : CSharpPDBTestBase + { + [Theory] + [InlineData(DebugInformationFormat.Pdb)] + [InlineData(DebugInformationFormat.PortablePdb)] + public void SourceLink(DebugInformationFormat format) + { + string source = @" +using System; + +class C +{ + public static void Main() + { + Console.WriteLine(); + } +} +"; + var sourceLinkBlob = Encoding.UTF8.GetBytes(@" +{ + ""documents"": { + ""f:/build/*"" : ""https://raw.githubusercontent.com/my-org/my-project/1111111111111111111111111111111111111111/*"" + } +} +"); + + var c = CreateStandardCompilation(Parse(source, "f:/build/foo.cs"), options: TestOptions.DebugDll); + + var pdbStream = new MemoryStream(); + c.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(format), pdbStream: pdbStream, sourceLinkStream: new MemoryStream(sourceLinkBlob)); + + var actualData = PdbValidation.GetSourceLinkData(pdbStream); + AssertEx.Equal(sourceLinkBlob, actualData); + } + + [Fact] + public void SourceLink_Embedded() + { + string source = @" +using System; + +class C +{ + public static void Main() + { + Console.WriteLine(); + } +} +"; + var sourceLinkBlob = Encoding.UTF8.GetBytes(@" +{ + ""documents"": { + ""f:/build/*"" : ""https://raw.githubusercontent.com/my-org/my-project/1111111111111111111111111111111111111111/*"" + } +} +"); + var c = CreateStandardCompilation(Parse(source, "f:/build/foo.cs"), options: TestOptions.DebugDll); + + var peBlob = c.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded), sourceLinkStream: new MemoryStream(sourceLinkBlob)); + + using (var peReader = new PEReader(peBlob)) + { + var embeddedEntry = peReader.ReadDebugDirectory().Single(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); + + using (var embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntry)) + { + var pdbReader = embeddedMetadataProvider.GetMetadataReader(); + + var actualBlob = + (from cdiHandle in pdbReader.GetCustomDebugInformation(EntityHandle.ModuleDefinition) + let cdi = pdbReader.GetCustomDebugInformation(cdiHandle) + where pdbReader.GetGuid(cdi.Kind) == PortableCustomDebugInfoKinds.SourceLink + select pdbReader.GetBlobBytes(cdi.Value)).Single(); + + AssertEx.Equal(sourceLinkBlob, actualBlob); + } + } + } + + [Theory] + [InlineData(DebugInformationFormat.Pdb)] + [InlineData(DebugInformationFormat.Embedded)] + [InlineData(DebugInformationFormat.PortablePdb)] + public void SourceLink_Errors(DebugInformationFormat format) + { + string source = @" +using System; + +class C +{ + public static void Main() + { + Console.WriteLine(); + } +} +"; + var sourceLinkStream = new TestStream(canRead: true, readFunc: (_, __, ___) => { throw new Exception("Error!"); }); + + var c = CreateStandardCompilation(Parse(source, "f:/build/foo.cs"), options: TestOptions.DebugDll); + var pdbStream = format != DebugInformationFormat.Embedded ? new MemoryStream() : null; + var result = c.Emit(new MemoryStream(), pdbStream, options: EmitOptions.Default.WithDebugInformationFormat(format), sourceLinkStream: sourceLinkStream); + result.Diagnostics.Verify( + // error CS0041: Unexpected error writing debug information -- 'Error!' + Diagnostic(ErrorCode.FTL_DebugEmitFailure).WithArguments("Error!").WithLocation(1, 1)); + } + + [Theory] + [InlineData(DebugInformationFormat.Pdb)] + [InlineData(DebugInformationFormat.PortablePdb)] + public void SourceLink_Empty(DebugInformationFormat format) + { + string source = @" +using System; + +class C +{ + public static void Main() + { + Console.WriteLine(); + } +} +"; + var sourceLinkBlob = new byte[0]; + + var c = CreateStandardCompilation(Parse(source, "f:/build/foo.cs"), options: TestOptions.DebugDll); + + var pdbStream = new MemoryStream(); + c.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(format), pdbStream: pdbStream, sourceLinkStream: new MemoryStream(sourceLinkBlob)); + pdbStream.Position = 0; + var bs = Roslyn.Utilities.StreamExtensions.ReadAllBytes(pdbStream); + var actualData = PdbValidation.GetSourceLinkData(pdbStream); + AssertEx.Equal(sourceLinkBlob, actualData); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs index de4c796ed83eb..51a99debe4b3a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs @@ -210,18 +210,6 @@ public void Emit_BadArgs() options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb), sourceLinkStream: new TestStream(canRead: false, canWrite: true, canSeek: true))); - Assert.Throws("sourceLinkStream", () => comp.Emit( - peStream: new MemoryStream(), - pdbStream: new MemoryStream(), - options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Pdb), - sourceLinkStream: new MemoryStream())); - - Assert.Throws("sourceLinkStream", () => comp.Emit( - peStream: new MemoryStream(), - pdbStream: null, - options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb), - sourceLinkStream: new MemoryStream())); - Assert.Throws("embeddedTexts", () => comp.Emit( peStream: new MemoryStream(), pdbStream: new MemoryStream(), diff --git a/src/Compilers/Core/CodeAnalysisTest/InternalUtilities/StreamExtensionsTests.cs b/src/Compilers/Core/CodeAnalysisTest/InternalUtilities/StreamExtensionsTests.cs index 708facb9e0935..cc61e25fdc2da 100644 --- a/src/Compilers/Core/CodeAnalysisTest/InternalUtilities/StreamExtensionsTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/InternalUtilities/StreamExtensionsTests.cs @@ -11,7 +11,7 @@ namespace Roslyn.Utilities.UnitTests.InternalUtilities public class StreamExtensionsTests { [Fact] - public void CallsReadMultipleTimes() + public void TryReadAll_CallsReadMultipleTimes() { var firstRead = true; var sourceArray = new byte[] { 1, 2, 3, 4 }; @@ -37,7 +37,7 @@ public void CallsReadMultipleTimes() } [Fact] - public void ExceptionsPropagate() + public void TryReadAll_ExceptionsPropagate() { var buffer = new byte[10]; @@ -57,7 +57,7 @@ public void ExceptionsPropagate() } [Fact] - public void ExceptionMayChangeOutput() + public void TryReadAll_ExceptionMayChangeOutput() { var firstRead = true; var sourceArray = new byte[] { 1, 2, 3, 4 }; @@ -82,7 +82,7 @@ public void ExceptionMayChangeOutput() } [Fact] - public void ExceptionMayChangePosition() + public void TryReadAll_ExceptionMayChangePosition() { var firstRead = true; var sourceArray = new byte[] { 1, 2, 3, 4 }; @@ -107,7 +107,7 @@ public void ExceptionMayChangePosition() } [Fact] - public void PrematureEndOfStream() + public void TryReadAll_PrematureEndOfStream() { var sourceArray = new byte[] { 1, 2, 3, 4 }; var stream = new MemoryStream(sourceArray); @@ -118,5 +118,38 @@ public void PrematureEndOfStream() var expected = new byte[] { 1, 2, 3, 4, 0, 0 }; Assert.Equal(expected, destArray); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ReadAllBytes(bool canSeek) + { + var sourceArray = new byte[] { 1, 2, 3, 4 }; + var stream = new TestStream(canSeek: canSeek, backingStream: new MemoryStream(sourceArray)); + stream.ReadByte(); + Assert.Equal(new byte[] { 2, 3, 4 }, stream.ReadAllBytes()); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ReadAllBytes_End(bool canSeek) + { + var sourceArray = new byte[] { 1, 2 }; + var stream = new TestStream(canSeek: canSeek, backingStream: new MemoryStream(sourceArray)); + stream.ReadByte(); + stream.ReadByte(); + Assert.Equal(new byte[0], stream.ReadAllBytes()); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ReadAllBytes_Resize(bool canSeek) + { + var sourceArray = new byte[] { 1, 2 }; + var stream = new TestStream(canSeek: canSeek, backingStream: new MemoryStream(sourceArray), length: 3); + Assert.Equal(new byte[] { 1, 2 }, stream.ReadAllBytes()); + } } } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 478cae1ae4eae..1fcb0662afc5e 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -1189,15 +1189,6 @@ internal static string SizeHasToBePositive { } } - /// - /// Looks up a localized string similar to Source Link embedding is only supported when emitting Portable PDB.. - /// - internal static string SourceLinkRequiresPortablePdb { - get { - return ResourceManager.GetString("SourceLinkRequiresPortablePdb", resourceCulture); - } - } - /// /// Looks up a localized string similar to SourceText cannot be embedded. Provide encoding or canBeEmbedded=true at construction.. /// @@ -1291,6 +1282,15 @@ internal static string Struct1 { /// /// Looks up a localized string similar to Windows PDB writer doesn't support deterministic compilation: '{0}'. /// + internal static string SymWriterDoesNotSupportSourceLink { + get { + return ResourceManager.GetString("SymWriterDoesNotSupportSourceLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Windows PDB writer is not available -- could not find Microsoft.DiaSymReader.Native.{0}.dll. + /// internal static string SymWriterNotDeterministic { get { return ResourceManager.GetString("SymWriterNotDeterministic", resourceCulture); diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 96cd5f06360ee..73efdfebe9ba8 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -470,6 +470,9 @@ The version of Windows PDB writer is older than required: '{0}' + + Windows PDB writer doesn't support SourceLink feature: '{0}' + The attribute {0} has an invalid value of {1}. @@ -561,9 +564,6 @@ Feature 'IOperation' is disabled. - - Source Link embedding is only supported when emitting Portable PDB. - Unrecognized resource file format. diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index c270b6573f891..0865c4659a0e8 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1991,7 +1991,6 @@ public EmitResult Emit( /// /// /// Stream containing information linking the compilation to a source control. - /// Only supported when emitting Portable PDBs. /// /// /// Texts to embed in the PDB. @@ -2041,20 +2040,11 @@ public EmitResult Emit( } } - if (sourceLinkStream != null) + if (sourceLinkStream != null && !sourceLinkStream.CanRead) { - if (options == null || - options.DebugInformationFormat == DebugInformationFormat.Pdb || - options.DebugInformationFormat == DebugInformationFormat.PortablePdb && pdbStream == null) - { - throw new ArgumentException(CodeAnalysisResources.SourceLinkRequiresPortablePdb, nameof(sourceLinkStream)); - } - - if (!sourceLinkStream.CanRead) - { - throw new ArgumentException(CodeAnalysisResources.StreamMustSupportRead, nameof(sourceLinkStream)); - } + throw new ArgumentException(CodeAnalysisResources.StreamMustSupportRead, nameof(sourceLinkStream)); } + if (embeddedTexts != null && !embeddedTexts.IsEmpty()) { if (options == null || diff --git a/src/Compilers/Core/Portable/InternalUtilities/StreamExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/StreamExtensions.cs index 1c4fc9fb4b4e2..c1dabc5901254 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/StreamExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/StreamExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Diagnostics; using System.IO; @@ -47,5 +48,29 @@ public static int TryReadAll( } return totalBytesRead; } + + /// + /// Reads all bytes from the current postion of the given stream to its end. + /// + public static byte[] ReadAllBytes(this Stream stream) + { + if (stream.CanSeek) + { + long length = stream.Length - stream.Position; + if (length == 0) + { + return Array.Empty(); + } + + var buffer = new byte[length]; + int actualLength = TryReadAll(stream, buffer, 0, buffer.Length); + Array.Resize(ref buffer, actualLength); + return buffer; + } + + var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + return memoryStream.ToArray(); + } } } diff --git a/src/Compilers/Core/Portable/NativePdbWriter/ISymUnmanagedWriter.cs b/src/Compilers/Core/Portable/NativePdbWriter/ISymUnmanagedWriter.cs index 33ed19958582e..fe57c59b2f9ef 100644 --- a/src/Compilers/Core/Portable/NativePdbWriter/ISymUnmanagedWriter.cs +++ b/src/Compilers/Core/Portable/NativePdbWriter/ISymUnmanagedWriter.cs @@ -109,8 +109,8 @@ void DefineSequencePoints(ISymUnmanagedDocumentWriter document, uint count, /// /// The highest version of the interface available in Microsoft.DiaSymReader.Native. /// - [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("22DAEAF2-70F6-4EF1-B0C3-984F0BF27BFD"), SuppressUnmanagedCodeSecurity] - internal interface ISymUnmanagedWriter7 : ISymUnmanagedWriter5 + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("5ba52f3b-6bf8-40fc-b476-d39c529b331e"), SuppressUnmanagedCodeSecurity] + internal interface ISymUnmanagedWriter8 : ISymUnmanagedWriter5 { // ISymUnmanagedWriter, ISymUnmanagedWriter2, ISymUnmanagedWriter3, ISymUnmanagedWriter4, ISymUnmanagedWriter5 void _VtblGap1_33(); @@ -120,6 +120,11 @@ internal interface ISymUnmanagedWriter7 : ISymUnmanagedWriter5 // ISymUnmanagedWriter7 unsafe void UpdateSignatureByHashingContent([In]byte* buffer, int size); + + // ISymUnmanagedWriter8 + void UpdateSignature(Guid pdbId, uint stamp, int age); + unsafe void SetSourceServerData([In]byte* data, int size); + unsafe void SetSourceLinkData([In]byte* data, int size); } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("98ECEE1E-752D-11d3-8D56-00C04F680B2B"), SuppressUnmanagedCodeSecurity] diff --git a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs index bcbb76264c431..61df6cfcb0a99 100644 --- a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs +++ b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs @@ -120,7 +120,8 @@ internal enum PdbWriterOperation : byte OpenMapTokensToSourceSpans, MapTokenToSourceSpan, CloseMapTokensToSourceSpans, - SetSource + SetSource, + SetSourceLinkData, } public bool LogOperation(PdbWriterOperation op) @@ -825,9 +826,9 @@ public void SetMetadataEmitter(MetadataWriter metadataWriter) { symWriter.Initialize(new PdbMetadataWrapper(metadataWriter), _fileName, _pdbStream, fullBuild: true); } - else if (symWriter is ISymUnmanagedWriter7 symWriter7) + else if (symWriter is ISymUnmanagedWriter8 symWriter8) { - symWriter7.InitializeDeterministic(new PdbMetadataWrapper(metadataWriter), _pdbStream); + symWriter8.InitializeDeterministic(new PdbMetadataWrapper(metadataWriter), _pdbStream); } else { @@ -855,7 +856,7 @@ public unsafe BlobContentId GetContentId() { fixed (byte* hashPtr = &hash[0]) { - ((ISymUnmanagedWriter7)_symWriter).UpdateSignatureByHashingContent(hashPtr, hash.Length); + ((ISymUnmanagedWriter8)_symWriter).UpdateSignatureByHashingContent(hashPtr, hash.Length); } } catch (Exception ex) @@ -1505,6 +1506,34 @@ public void WriteDefinitionLocations(MultiDictionary DebugInformationFormat.PortablePdb AndAlso debugInformationFormat <> DebugInformationFormat.Embedded Then - AddDiagnostic(diagnostics, ERRID.ERR_SourceLinkRequiresPortablePdb) - End If + If sourceLink IsNot Nothing And Not emitPdb Then + AddDiagnostic(diagnostics, ERRID.ERR_SourceLinkRequiresPdb) End If If embedAllSourceFiles Then diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 2b1e55429e4d0..e80c45b05247b 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1696,7 +1696,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_RefReturningCallInExpressionTree = 37263 - ERR_SourceLinkRequiresPortablePdb = 37264 + ERR_SourceLinkRequiresPdb = 37264 ERR_CannotEmbedWithoutPdb = 37265 ERR_InvalidInstrumentationKind = 37266 diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index f3cc8d7983f6d..a827d691c4349 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -10037,11 +10037,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property ''' - ''' Looks up a localized string similar to /sourcelink switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded must be specified).. + ''' Looks up a localized string similar to /sourcelink switch is only supported when emitting PDB.. ''' - Friend ReadOnly Property ERR_SourceLinkRequiresPortablePdb() As String + Friend ReadOnly Property ERR_SourceLinkRequiresPdb() As String Get - Return ResourceManager.GetString("ERR_SourceLinkRequiresPortablePdb", resourceCulture) + Return ResourceManager.GetString("ERR_SourceLinkRequiresPdb", resourceCulture) End Get End Property diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 67dc422629514..7bef84cd0b6db 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5107,7 +5107,7 @@ (including module version GUID and timestamp) /instrument:TestCoverage Produce an assembly instrumented to collect coverage information -/sourcelink:<file> Source link info to embed into Portable PDB. +/sourcelink:<file> Source link info to embed into PDB. - ERRORS AND WARNINGS - /nowarn Disable all warnings. @@ -5418,8 +5418,8 @@ Option '{0}' must be an absolute path. - - /sourcelink switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded must be specified). + + /sourcelink switch is only supported when emitting PDB. Tuple element names must be unique. diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 84f612cb5eb24..fa6de58103a09 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -2657,19 +2657,19 @@ End Module").Path Assert.Equal(Path.Combine(_baseDirectory, "s l.json"), parsedArgs.SourceLink) parsedArgs = DefaultParse({"/sourcelink:sl.json", "/debug:full", "a.vb"}, _baseDirectory) - parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPortablePdb)) + parsedArgs.Errors.Verify() parsedArgs = DefaultParse({"/sourcelink:sl.json", "/debug:pdbonly", "a.vb"}, _baseDirectory) - parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPortablePdb)) + parsedArgs.Errors.Verify() parsedArgs = DefaultParse({"/sourcelink:sl.json", "/debug-", "a.vb"}, _baseDirectory) - parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPortablePdb)) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPdb)) parsedArgs = DefaultParse({"/sourcelink:sl.json", "/debug+", "a.vb"}, _baseDirectory) - parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPortablePdb)) + parsedArgs.Errors.Verify() parsedArgs = DefaultParse({"/sourcelink:sl.json", "a.vb"}, _baseDirectory) - parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPortablePdb)) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_SourceLinkRequiresPdb)) End Sub @@ -8252,11 +8252,13 @@ End Module Assert.Equal($"error BC2012: can't open '{xmlPath}' for writing: Fake IOException{Environment.NewLine}", outWriter.ToString()) End Sub - - Public Sub IOFailure_DisposeSourceLinkFile() + + + + Public Sub IOFailure_DisposeSourceLinkFile(format As String) Dim srcPath = MakeTrivialExe(Temp.CreateDirectory().Path) Dim sourceLinkPath = Path.Combine(Path.GetDirectoryName(srcPath), "test.json") - Dim csc = New MockVisualBasicCompiler(_baseDirectory, {"/nologo", "/preferreduilang:en", "/debug:portable", $"/sourcelink:{sourceLinkPath}", srcPath}) + Dim csc = New MockVisualBasicCompiler(_baseDirectory, {"/nologo", "/preferreduilang:en", "/debug:" & format, $"/sourcelink:{sourceLinkPath}", srcPath}) csc.FileOpen = Function(filePath, mode, access, share) If filePath = sourceLinkPath Then Return New TestStream( diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb index 3674b2d1225c6..5c25c3217506c 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb @@ -285,18 +285,6 @@ End Namespace options:=EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb), sourceLinkStream:=New TestStream(canRead:=False, canWrite:=True, canSeek:=True))) - Assert.Throws(Of ArgumentException)("sourceLinkStream", Sub() comp.Emit( - peStream:=New MemoryStream(), - pdbStream:=New MemoryStream(), - options:=EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Pdb), - sourceLinkStream:=New MemoryStream())) - - Assert.Throws(Of ArgumentException)("sourceLinkStream", Sub() comp.Emit( - peStream:=New MemoryStream(), - pdbStream:=Nothing, - options:=EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb), - sourceLinkStream:=New MemoryStream())) - Assert.Throws(Of ArgumentException)("embeddedTexts", Sub() comp.Emit( peStream:=New MemoryStream(), pdbStream:=New MemoryStream(), diff --git a/src/Test/PdbUtilities/Pdb/SymReaderFactory.cs b/src/Test/PdbUtilities/Pdb/SymReaderFactory.cs index 9538682fb451b..2502719b6dbe4 100644 --- a/src/Test/PdbUtilities/Pdb/SymReaderFactory.cs +++ b/src/Test/PdbUtilities/Pdb/SymReaderFactory.cs @@ -23,7 +23,7 @@ public static class SymReaderFactory [DllImport("Microsoft.DiaSymReader.Native.amd64.dll", EntryPoint = "CreateSymReader")] private extern static void CreateSymReader64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)]out object symReader); - private static ISymUnmanagedReader3 CreateNativeSymReader(Stream pdbStream, object metadataImporter) + private static ISymUnmanagedReader5 CreateNativeSymReader(Stream pdbStream, object metadataImporter) { object symReader = null; @@ -37,42 +37,42 @@ private static ISymUnmanagedReader3 CreateNativeSymReader(Stream pdbStream, obje CreateSymReader64(ref guid, out symReader); } - var reader = (ISymUnmanagedReader3)symReader; + var reader = (ISymUnmanagedReader5)symReader; reader.Initialize(pdbStream, metadataImporter); return reader; } - private static ISymUnmanagedReader3 CreatePortableSymReader(Stream pdbStream, object metadataImporter) + private static ISymUnmanagedReader5 CreatePortableSymReader(Stream pdbStream, object metadataImporter) { - return (ISymUnmanagedReader3)new PortablePdb.SymBinder().GetReaderFromStream(pdbStream, metadataImporter); + return (ISymUnmanagedReader5)new PortablePdb.SymBinder().GetReaderFromStream(pdbStream, metadataImporter); } - public static ISymUnmanagedReader3 CreateReader(byte[] pdbImage, byte[] peImageOpt = null) + public static ISymUnmanagedReader5 CreateReader(byte[] pdbImage, byte[] peImageOpt = null) { return CreateReader(new MemoryStream(pdbImage), (peImageOpt != null) ? new PEReader(new MemoryStream(peImageOpt)) : null); } - public static ISymUnmanagedReader3 CreateReader(ImmutableArray pdbImage, ImmutableArray peImageOpt = default(ImmutableArray)) + public static ISymUnmanagedReader5 CreateReader(ImmutableArray pdbImage, ImmutableArray peImageOpt = default(ImmutableArray)) { return CreateReader(new MemoryStream(pdbImage.ToArray()), (peImageOpt.IsDefault) ? null : new PEReader(peImageOpt)); } - public static ISymUnmanagedReader3 CreateReader(Stream pdbStream, Stream peStreamOpt = null) + public static ISymUnmanagedReader5 CreateReader(Stream pdbStream, Stream peStreamOpt = null) { return CreateReader(pdbStream, (peStreamOpt != null) ? new PEReader(peStreamOpt) : null); } - public static ISymUnmanagedReader3 CreateReader(Stream pdbStream, PEReader peReaderOpt) + public static ISymUnmanagedReader5 CreateReader(Stream pdbStream, PEReader peReaderOpt) { return CreateReader(pdbStream, peReaderOpt?.GetMetadataReader(), peReaderOpt); } - public static ISymUnmanagedReader3 CreateReader(Stream pdbStream, MetadataReader metadataReaderOpt, IDisposable metadataMemoryOwnerOpt) + public static ISymUnmanagedReader5 CreateReader(Stream pdbStream, MetadataReader metadataReaderOpt, IDisposable metadataMemoryOwnerOpt) { return CreateReaderImpl(pdbStream, metadataImporter: new DummyMetadataImport(metadataReaderOpt, metadataMemoryOwnerOpt)); } - public static ISymUnmanagedReader3 CreateReaderImpl(Stream pdbStream, object metadataImporter) + public static ISymUnmanagedReader5 CreateReaderImpl(Stream pdbStream, object metadataImporter) { pdbStream.Position = 0; bool isPortable = pdbStream.ReadByte() == 'B' && pdbStream.ReadByte() == 'S' && pdbStream.ReadByte() == 'J' && pdbStream.ReadByte() == 'B'; diff --git a/src/Test/Utilities/Portable/Pdb/PdbValidation.cs b/src/Test/Utilities/Portable/Pdb/PdbValidation.cs index ef8158465840a..5e424fd6a2142 100644 --- a/src/Test/Utilities/Portable/Pdb/PdbValidation.cs +++ b/src/Test/Utilities/Portable/Pdb/PdbValidation.cs @@ -10,11 +10,13 @@ using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Emit; +using Microsoft.DiaSymReader; using Microsoft.DiaSymReader.Tools; using Microsoft.Metadata.Tools; using Roslyn.Test.MetadataUtilities; @@ -265,11 +267,34 @@ internal static string GetPdbXml( return actual; } + public unsafe static byte[] GetSourceLinkData(Stream pdbStream) + { + pdbStream.Position = 0; + + var symReader = SymReaderFactory.CreateReader(pdbStream); + try + { + Marshal.ThrowExceptionForHR(symReader.GetSourceServerData(out byte* data, out int size)); + if (size == 0) + { + return Array.Empty(); + } + + var result = new byte[size]; + Marshal.Copy((IntPtr)data, result, 0, result.Length); + return result; + } + finally + { + ((ISymUnmanagedDispose)symReader).Destroy(); + } + } + public static void ValidateDebugDirectory(Stream peStream, Stream portablePdbStreamOpt, string pdbPath, bool isDeterministic) { - peStream.Seek(0, SeekOrigin.Begin); - PEReader peReader = new PEReader(peStream); + peStream.Position = 0; + var peReader = new PEReader(peStream); var debugDirectory = peReader.PEHeaders.PEHeader.DebugTableDirectory; int position; From e9053e6b088fe4327ac68006e2763efcef39e70a Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 9 May 2017 13:09:18 -0700 Subject: [PATCH 070/284] Moved EditorInfoBarService to its own folder, export as workspace service --- .../{Workspaces => InfoBar}/EditorInfoBarService.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename src/EditorFeatures/Core/Implementation/{Workspaces => InfoBar}/EditorInfoBarService.cs (68%) diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs b/src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs similarity index 68% rename from src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs rename to src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs index 5c98212530912..bd8a0567fd41b 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorInfoBarService.cs +++ b/src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { + [ExportWorkspaceService(typeof(IInfoBarService))] internal class EditorInfoBarService : IInfoBarService { public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items) From d69c102665ba12b7ba920dfd5e3b4c47d04b6af2 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 9 May 2017 13:15:17 -0700 Subject: [PATCH 071/284] Moved VisualStudioInfoBarService, exported as workspace service --- .../VisualStudioInfoBarService.cs | 10 ++++++++-- .../VisualStudioErrorReportingServiceFactory.cs | 9 ++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) rename src/VisualStudio/Core/Def/Implementation/{Workspace => InfoBar}/VisualStudioInfoBarService.cs (95%) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs similarity index 95% rename from src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs rename to src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index eac76f3fe4172..65c2f2f3d3b0c 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -5,7 +5,9 @@ using System.ComponentModel.Composition; using System.Linq; using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Shell; @@ -14,8 +16,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation { - [Export(typeof(IInfoBarService))] - internal class VisualStudioInfoBarService : IInfoBarService + [ExportWorkspaceService(typeof(IInfoBarService))] + internal class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject, IInfoBarService { private readonly IServiceProvider _serviceProvider; private readonly IForegroundNotificationService _foregroundNotificationService; @@ -33,11 +35,13 @@ public VisualStudioInfoBarService([Import(typeof(SVsServiceProvider))] IServiceP public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items) { + ThisCanBeCalledOnAnyThread(); ShowInfoBar(activeView: true, message: message, items: items); } public void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items) { + ThisCanBeCalledOnAnyThread(); ShowInfoBar(activeView: false, message: message, items: items); } @@ -55,6 +59,8 @@ private void ShowInfoBar(bool activeView, string message, params InfoBarUI[] ite private bool TryGetInfoBarData(bool activeView, out IVsInfoBarHost infoBarHost) { + AssertIsForeground(); + infoBarHost = null; if (activeView) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs index 4005022fc4ce1..4fdc31eaf3991 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs @@ -10,16 +10,19 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation [ExportWorkspaceServiceFactory(typeof(IErrorReportingService), ServiceLayer.Host), Shared] internal sealed class VisualStudioErrorReportingServiceFactory : IWorkspaceServiceFactory { - private readonly IErrorReportingService _singleton; + private IErrorReportingService _singleton; [ImportingConstructor] - public VisualStudioErrorReportingServiceFactory(IInfoBarService infoBarService) + public VisualStudioErrorReportingServiceFactory() { - _singleton = new VisualStudioErrorReportingService(infoBarService); } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) { + if (_singleton == null) + { + _singleton = new VisualStudioErrorReportingService(workspaceServices.GetRequiredService()); + } return _singleton; } } From 4a0eea3bbeef286049c8f9e4ed452e87ce205fcc Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 9 May 2017 13:20:45 -0700 Subject: [PATCH 072/284] Cleaned up documentation --- .../Core/Portable/ExtensionManager/IInfoBarService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs b/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs index 789b0a53c87be..de2e3e8b55e26 100644 --- a/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs +++ b/src/Workspaces/Core/Portable/ExtensionManager/IInfoBarService.cs @@ -18,8 +18,6 @@ internal interface IInfoBarService : IWorkspaceService /// /// Show global info bar /// - /// - /// void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items); } From c6f25f62941c5fbf9c9e024319a305bd3f785482 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 9 May 2017 14:19:14 -0500 Subject: [PATCH 073/284] Disable test for simplifier edge case See dotnet/roslyn#19368 --- .../Test2/Simplification/TypeNameSimplifierTest.vb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb b/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb index 32b2f089b013e..0733460a05bed 100644 --- a/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb +++ b/src/EditorFeatures/Test2/Simplification/TypeNameSimplifierTest.vb @@ -764,7 +764,8 @@ namespace N1 Await TestAsync(input, expected) End Function - + + Public Async Function TestSimplifyNot_Delegate1() As Task Dim input = From b4ae4feee48f030b738224b5f772613bb0876b05 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 9 May 2017 13:27:59 -0700 Subject: [PATCH 074/284] Added missing csproj edits --- src/EditorFeatures/Core/EditorFeatures.csproj | 2 +- src/VisualStudio/Core/Def/ServicesVisualStudio.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 521cb964a62df..c4ca714ba6082 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -168,7 +168,7 @@ - + diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index bad1214edf92c..4c7b48649bd96 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -168,7 +168,7 @@ - + From c1bfe6f3caea206275af989fd095230ed5677650 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 9 May 2017 13:47:09 -0700 Subject: [PATCH 075/284] added boost service in service hub --- .../Remote/ServiceHub/ServiceHub.csproj | 1 + .../Remote/ServiceHub/UserOperationBooster.cs | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/Workspaces/Remote/ServiceHub/UserOperationBooster.cs diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj index 1d5b8a990240a..d6d11f07df18b 100644 --- a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -90,6 +90,7 @@ + diff --git a/src/Workspaces/Remote/ServiceHub/UserOperationBooster.cs b/src/Workspaces/Remote/ServiceHub/UserOperationBooster.cs new file mode 100644 index 0000000000000..cdfd1e14466ad --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/UserOperationBooster.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Threading; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Remote +{ + /// + /// Boost performance of any servicehub service which is invoked by user explicit actions + /// + internal static class UserOperationBooster + { + private static int s_count = 0; + + public static IDisposable Boost() + { + return new Booster(); + } + + private static void Start() + { + var value = Interlocked.Increment(ref s_count); + Contract.Requires(value >= 0); + + if (value == 1) + { + // boost to normal priority + Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal; + } + } + + private static void Done() + { + var value = Interlocked.Decrement(ref s_count); + Contract.Requires(value >= 0); + + if (value == 0) + { + // when boost is done, set process back to below normal priority + Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal; + } + } + + private class Booster : IDisposable + { + public Booster() + { + Start(); + } + + public void Dispose() + { + Done(); + + GC.SuppressFinalize(this); + } + + ~Booster() + { + if (!Environment.HasShutdownStarted) + { + Contract.Fail($@"Should have been disposed!"); + } + } + } + } +} From 7642cb0211cf0461c885ca4bdc03bcd415fdc06a Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 9 May 2017 13:47:20 -0700 Subject: [PATCH 076/284] fixed release mode build break --- src/Workspaces/Remote/Core/TestUtils.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Remote/Core/TestUtils.cs b/src/Workspaces/Remote/Core/TestUtils.cs index 8010164270c98..4085a78ecaf4b 100644 --- a/src/Workspaces/Remote/Core/TestUtils.cs +++ b/src/Workspaces/Remote/Core/TestUtils.cs @@ -53,7 +53,7 @@ public static Dictionary GetAssetMap(this Project project) public static void AppendAssetMap(this Solution solution, Dictionary map) { SolutionStateChecksums solutionChecksums; - Contract.Requires(solution.State.TryGetStateChecksums(out solutionChecksums)); + Contract.ThrowIfFalse(solution.State.TryGetStateChecksums(out solutionChecksums)); solutionChecksums.Find(solution.State, Flatten(solutionChecksums), map, CancellationToken.None); @@ -88,7 +88,7 @@ private static void AppendAssetMap(Project project, Dictionary private static void AppendAssetMap(TextDocument document, Dictionary map) { DocumentStateChecksums documentChecksums; - Contract.Requires(document.State.TryGetStateChecksums(out documentChecksums)); + Contract.ThrowIfFalse(document.State.TryGetStateChecksums(out documentChecksums)); documentChecksums.Find(document.State, Flatten(documentChecksums), map, CancellationToken.None); From 57eca79980cbbfba569e50f72d286d2144cacecb Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:01:49 -0700 Subject: [PATCH 077/284] Simplify serialization. --- .../Core/Portable/Serialization/ObjectWriter.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index 2e4a6b8f8508b..eaf572d8da981 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -728,7 +728,8 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) // don't blow the stack. 'LongRunning' ensures that we get a dedicated thread // to do this work. That way we don't end up blocking the threadpool. var task = Task.Factory.StartNew( - () => WriteObjectWorker(instance, writable), + obj => WriteObjectWorker((IObjectWritable)obj), + writable, _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); @@ -736,7 +737,7 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) } else { - WriteObjectWorker(instance, writable); + WriteObjectWorker(writable); } _recursionDepth--; @@ -744,14 +745,14 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) } } - private void WriteObjectWorker(object instance, IObjectWritable writable) + private void WriteObjectWorker(IObjectWritable writable) { // emit object header up front - this.WriteObjectHeader(instance, 0); + this.WriteObjectHeader(writable); writable.WriteTo(this); } - private void WriteObjectHeader(object instance, uint memberCount) + private void WriteObjectHeader(IObjectWritable instance) { _objectReferenceMap.Add(instance); From 75c9f0d2b68c648a24a77d74393d66741680e9d7 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:03:08 -0700 Subject: [PATCH 078/284] Inline method --- .../Core/Portable/Serialization/ObjectWriter.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index eaf572d8da981..517218dbb3b7b 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -748,16 +748,11 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) private void WriteObjectWorker(IObjectWritable writable) { // emit object header up front - this.WriteObjectHeader(writable); - writable.WriteTo(this); - } - - private void WriteObjectHeader(IObjectWritable instance) - { - _objectReferenceMap.Add(instance); + _objectReferenceMap.Add(writable); _writer.Write((byte)EncodingKind.Object); - this.WriteKnownType(instance.GetType()); + this.WriteKnownType(writable.GetType()); + writable.WriteTo(this); } private static Exception NoSerializationTypeException(string typeName) From 4644b5bfd2280fdaa58aa47bb3b22b234e42d7ba Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:03:56 -0700 Subject: [PATCH 079/284] Inline method --- .../Core/Portable/Serialization/ObjectReader.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index f0d550fadfc50..50757add9b016 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -576,17 +576,12 @@ public Type ReadType() private Type ReadTypeAfterTag() => _binderSnapshot.GetTypeFromId(this.ReadInt32()); - private Func ReadTypeReader() - { - _reader.ReadByte(); - return _binderSnapshot.GetTypeReaderFromId(this.ReadInt32()); - } - private object ReadObject() { var id = _objectReferenceMap.GetNextReferenceId(); - var typeReader = this.ReadTypeReader(); + _reader.ReadByte(); + var typeReader = _binderSnapshot.GetTypeReaderFromId(this.ReadInt32()); // recursive: read and construct instance immediately from member elements encoding next in the stream var instance = typeReader(this); From 6f1baf8b90cf78233b52614761b4c0b59783a601 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:22:39 -0700 Subject: [PATCH 080/284] Write out less data for objects. --- src/Compilers/Core/Portable/Serialization/ObjectReader.cs | 3 +-- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index 50757add9b016..4e150a112bdff 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -32,7 +32,7 @@ internal sealed partial class ObjectReader : IDisposable /// this version, just change VersionByte2. /// internal const byte VersionByte1 = 0b10101010; - internal const byte VersionByte2 = 0b00000111; + internal const byte VersionByte2 = 0b00001000; private readonly BinaryReader _reader; private readonly CancellationToken _cancellationToken; @@ -580,7 +580,6 @@ private object ReadObject() { var id = _objectReferenceMap.GetNextReferenceId(); - _reader.ReadByte(); var typeReader = _binderSnapshot.GetTypeReaderFromId(this.ReadInt32()); // recursive: read and construct instance immediately from member elements encoding next in the stream diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index 517218dbb3b7b..83a011d143005 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -751,7 +751,10 @@ private void WriteObjectWorker(IObjectWritable writable) _objectReferenceMap.Add(writable); _writer.Write((byte)EncodingKind.Object); - this.WriteKnownType(writable.GetType()); + + // Directly write out the type-id for this object. i.e. no need to write out the 'Type' + // tag since we just wrote out the 'Object' tag + this.WriteInt32(_binderSnapshot.GetTypeId(writable.GetType())); writable.WriteTo(this); } From 2d2fed797708cf124efe99349c7850a34794feb8 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:26:17 -0700 Subject: [PATCH 081/284] No need to return values here. They are never used. --- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index 83a011d143005..e10836345435e 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -319,15 +319,12 @@ public void Dispose() } public bool TryGetReferenceId(object value, out int referenceId) - { - return _valueToIdMap.TryGetValue(value, out referenceId); - } + => _valueToIdMap.TryGetValue(value, out referenceId); - public int Add(object value) + public void Add(object value) { var id = _nextId++; _valueToIdMap.Add(value, id); - return id; } } From 20f0557760b07680f94a6a6482a7cbb1b53d34ef Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:32:55 -0700 Subject: [PATCH 082/284] Make our reference maps less overhead. --- .../Portable/Serialization/ObjectReader.cs | 46 +++++++------------ .../Portable/Serialization/ObjectWriter.cs | 6 ++- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index 4e150a112bdff..5d3564f508a02 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -2,10 +2,8 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; -using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -39,9 +37,11 @@ internal sealed partial class ObjectReader : IDisposable /// /// Map of reference id's to deserialized objects. + /// + /// These are not readonly because they're structs and we mutate htemthem. /// - private readonly ReaderReferenceMap _objectReferenceMap; - private readonly ReaderReferenceMap _stringReferenceMap; + private ReaderReferenceMap _objectReferenceMap; + private ReaderReferenceMap _stringReferenceMap; /// /// Copy of the global binder data that maps from Types to the appropriate reading-function @@ -67,8 +67,8 @@ private ObjectReader( Debug.Assert(BitConverter.IsLittleEndian); _reader = new BinaryReader(stream, Encoding.UTF8); - _objectReferenceMap = new ReaderReferenceMap(); - _stringReferenceMap = new ReaderReferenceMap(); + _objectReferenceMap = ReaderReferenceMap.Create(); + _stringReferenceMap = ReaderReferenceMap.Create(); // Capture a copy of the current static binder state. That way we don't have to // access any locks while we're doing our processing. @@ -227,17 +227,18 @@ private object ReadValueWorker() /// /// An reference-id to object map, that can share base data efficiently. /// - private class ReaderReferenceMap where T : class + private struct ReaderReferenceMap where T : class { private readonly List _values; internal static readonly ObjectPool> s_objectListPool = new ObjectPool>(() => new List(20)); - public ReaderReferenceMap() - { - _values = s_objectListPool.Allocate(); - } + private ReaderReferenceMap(List values) + => _values = values; + + public static ReaderReferenceMap Create() + => new ReaderReferenceMap(s_objectListPool.Allocate()); public void Dispose() { @@ -245,21 +246,11 @@ public void Dispose() s_objectListPool.Free(_values); } - public int GetNextReferenceId() - { - _values.Add(null); - return _values.Count - 1; - } - - public void SetValue(int referenceId, T value) - { - _values[referenceId] = value; - } + public void AddValue(T value) + => _values.Add(value); public T GetValue(int referenceId) - { - return _values[referenceId]; - } + => _values[referenceId]; } internal uint ReadCompressedUInt() @@ -321,7 +312,6 @@ private string ReadStringValue(EncodingKind kind) private unsafe string ReadStringLiteral(EncodingKind kind) { - int id = _stringReferenceMap.GetNextReferenceId(); string value; if (kind == EncodingKind.StringUtf8) { @@ -338,7 +328,7 @@ private unsafe string ReadStringLiteral(EncodingKind kind) } } - _stringReferenceMap.SetValue(id, value); + _stringReferenceMap.AddValue(value); return value; } @@ -578,13 +568,11 @@ private Type ReadTypeAfterTag() private object ReadObject() { - var id = _objectReferenceMap.GetNextReferenceId(); - var typeReader = _binderSnapshot.GetTypeReaderFromId(this.ReadInt32()); // recursive: read and construct instance immediately from member elements encoding next in the stream var instance = typeReader(this); - _objectReferenceMap.SetValue(id, instance); + _objectReferenceMap.AddValue(instance); return instance; } diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index e10836345435e..db26312aa2032 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -32,9 +32,11 @@ internal sealed partial class ObjectWriter : IDisposable /// Map of serialized object's reference ids. The object-reference-map uses reference equality /// for performance. While the string-reference-map uses value-equality for greater cache hits /// and reuse. + /// + /// These are not readonly because they're structs and we mutate htemthem. /// - private readonly WriterReferenceMap _objectReferenceMap; - private readonly WriterReferenceMap _stringReferenceMap; + private WriterReferenceMap _objectReferenceMap; + private WriterReferenceMap _stringReferenceMap; /// /// Copy of the global binder data that maps from Types to the appropriate reading-function From ba6b8b105d52abd7bb3126f714f58951de485f68 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:36:34 -0700 Subject: [PATCH 083/284] Fix spelling --- src/Compilers/Core/Portable/Serialization/ObjectReader.cs | 2 +- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index 5d3564f508a02..6183edf5cfefd 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -38,7 +38,7 @@ internal sealed partial class ObjectReader : IDisposable /// /// Map of reference id's to deserialized objects. /// - /// These are not readonly because they're structs and we mutate htemthem. + /// These are not readonly because they're structs and we mutate them. /// private ReaderReferenceMap _objectReferenceMap; private ReaderReferenceMap _stringReferenceMap; diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index db26312aa2032..9150ae232eec2 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -33,7 +33,7 @@ internal sealed partial class ObjectWriter : IDisposable /// for performance. While the string-reference-map uses value-equality for greater cache hits /// and reuse. /// - /// These are not readonly because they're structs and we mutate htemthem. + /// These are not readonly because they're structs and we mutate them. /// private WriterReferenceMap _objectReferenceMap; private WriterReferenceMap _stringReferenceMap; From c920455ca95adf61f7b29d6750d1067cad8a7dea Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:39:19 -0700 Subject: [PATCH 084/284] Actually make into a struct. --- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index 9150ae232eec2..c8d0c4a3b15b6 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -281,7 +281,7 @@ private void WriteEncodedUInt32(uint v) /// /// An object reference to reference-id map, that can share base data efficiently. /// - private class WriterReferenceMap + private struct WriterReferenceMap { private readonly Dictionary _valueToIdMap; private readonly bool _valueEquality; From 230d93532f3f2a54732aa9e25326a46f733ff234 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:39:51 -0700 Subject: [PATCH 085/284] Simplify --- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index c8d0c4a3b15b6..0617573a56b9c 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -296,12 +296,12 @@ private struct WriterReferenceMap public WriterReferenceMap(bool valueEquality) { _valueEquality = valueEquality; - _valueToIdMap = GetDictionaryPool().Allocate(); + _valueToIdMap = GetDictionaryPool(valueEquality).Allocate(); _nextId = 0; } - private ObjectPool> GetDictionaryPool() - => _valueEquality ? s_valueDictionaryPool : s_referenceDictionaryPool; + private static ObjectPool> GetDictionaryPool(bool valueEquality) + => valueEquality ? s_valueDictionaryPool : s_referenceDictionaryPool; public void Dispose() { From 7551ad2b3f3b18a83be7626cc9f3b84af00654b4 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:40:16 -0700 Subject: [PATCH 086/284] Fix code. --- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index 0617573a56b9c..f97466d36672d 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -305,7 +305,7 @@ private static ObjectPool> GetDictionaryPool(bool valueE public void Dispose() { - var pool = GetDictionaryPool(); + var pool = GetDictionaryPool(_valueEquality); // If the map grew too big, don't return it to the pool. // When testing with the Roslyn solution, this dropped only 2.5% of requests. From 38925ac96f71b1d1bcbf9a5b548a80341033cc14 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 15:44:57 -0700 Subject: [PATCH 087/284] Adding comment. --- .../Core/Portable/Serialization/ObjectWriter.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index f97466d36672d..8630d34a541da 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -34,6 +34,21 @@ internal sealed partial class ObjectWriter : IDisposable /// and reuse. /// /// These are not readonly because they're structs and we mutate them. + /// + /// When we write out objects/strings we give each successive, unique, item a monotonically + /// increasing integral ID starting at 0. I.e. the first object gets ID-0, the next gets + /// ID-1 and so on and so forth. We do *not* include these IDs with the object when it is + /// written out. We only include the ID if we hit the object *again* while writing. + /// + /// During reading, the reader knows to give each object it reads the same monotonically + /// increasing integral value. i.e. the first object it reads is put into an array at position + /// 0, the next at position 1, and so on. Then, when the reader reads in an object-reference + /// it can just retrieved it directly from that array. + /// + /// In other words, writing and reading take advantage of the fact that they know they will + /// write and read objects in the exact same order. So they only need the IDs for references + /// and not the objects themselves because the ID is inferred from the order the object is + /// written or read in. /// private WriterReferenceMap _objectReferenceMap; private WriterReferenceMap _stringReferenceMap; From 9b9f11b24274a8a5ba472a100d93dbd7ff3b76b9 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 16:38:18 -0700 Subject: [PATCH 088/284] Use-throw-expression should not be offered when the 'if-block' has additional statements in it. --- .../UseThrowExpressionTests.cs | 42 +++++++++++++++++++ ...actUseThrowExpressionDiagnosticAnalyzer.cs | 18 ++++++++ 2 files changed, 60 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/UseThrowExpression/UseThrowExpressionTests.cs b/src/EditorFeatures/CSharpTest/UseThrowExpression/UseThrowExpressionTests.cs index a393d7e4a5b2c..96b96345e7ed8 100644 --- a/src/EditorFeatures/CSharpTest/UseThrowExpression/UseThrowExpressionTests.cs +++ b/src/EditorFeatures/CSharpTest/UseThrowExpression/UseThrowExpressionTests.cs @@ -386,6 +386,48 @@ public C(int? x) _x = x; } +}"); + } + + [WorkItem(19377, "https://github.com/dotnet/roslyn/issues/19377")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseThrowExpression)] + public async Task TestNotWithMultipleStatementsInIf1() + { + await TestMissingInRegularAndScriptAsync( +@"using System; + +class C +{ + void M(string s) + { + if (s == null) + { + Console.WriteLine(); + [|throw|] new ArgumentNullException(nameof(s)); + } + _s = s; + } +}"); + } + + [WorkItem(19377, "https://github.com/dotnet/roslyn/issues/19377")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseThrowExpression)] + public async Task TestNotWithMultipleStatementsInIf2() + { + await TestMissingInRegularAndScriptAsync( +@"using System; + +class C +{ + void M(string s) + { + if (s == null) + { + [|throw|] new ArgumentNullException(nameof(s)); + Console.WriteLine(); + } + _s = s; + } }"); } } diff --git a/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs b/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs index 8ae3cc4d1674a..a0c92f5d6c605 100644 --- a/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs @@ -116,6 +116,12 @@ private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol return; } + if (!IsOnlyStatementOfIf(ifOperation, throwOperation)) + { + // The if-statement can only have a single throw-statement in it. + return; + } + var containingBlock = GetOperation( semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement; if (containingBlock == null) @@ -189,6 +195,18 @@ private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol } } + private bool IsOnlyStatementOfIf(IIfStatement ifOperation, IThrowStatement throwStatement) + { + if (ifOperation.IfTrueStatement.Syntax == throwStatement.Syntax) + { + return true; + } + + return ifOperation.IfTrueStatement is IBlockStatement block && + block.Statements.Length == 1 && + block.Statements[0].Syntax == throwStatement.Syntax; + } + protected abstract ISyntaxFactsService GetSyntaxFactsService(); protected abstract ISemanticFactsService GetSemanticFactsService(); From b3495e9c7147d28c0f81cfd5fa58b517caaeedf6 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 16:51:57 -0700 Subject: [PATCH 089/284] Restore logic. --- .../Portable/Serialization/ObjectReader.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index 6183edf5cfefd..ea876f3ffbefc 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -246,9 +246,20 @@ public void Dispose() s_objectListPool.Free(_values); } + + public int GetNextObjectId() + { + var id = _values.Count; + _values.Add(null); + return id; + } + public void AddValue(T value) => _values.Add(value); + public void AddValue(int index, T value) + => _values[index] = value; + public T GetValue(int referenceId) => _values[referenceId]; } @@ -568,11 +579,16 @@ private Type ReadTypeAfterTag() private object ReadObject() { + var objectId = _objectReferenceMap.GetNextObjectId(); + + // reading an object may recurse. So we need to grab our ID up front as we'll + // end up making our sub-objects before we make this object. + var typeReader = _binderSnapshot.GetTypeReaderFromId(this.ReadInt32()); // recursive: read and construct instance immediately from member elements encoding next in the stream var instance = typeReader(this); - _objectReferenceMap.AddValue(instance); + _objectReferenceMap.AddValue(objectId, instance); return instance; } From a7ca8fd0fe286a7bd5415d400c23bbb6554f251a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 17:32:44 -0700 Subject: [PATCH 090/284] Formatting. --- src/Compilers/Core/Portable/Serialization/ObjectReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index ea876f3ffbefc..ae696efa9ac69 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -607,4 +607,4 @@ private static Exception NoSerializationReaderException(string typeName) return new InvalidOperationException(string.Format(Resources.Cannot_serialize_type_0, typeName)); } } -} +} \ No newline at end of file From 969b5cb1be9e60269b735c9fad135d66e4f566ad Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 9 May 2017 17:33:19 -0700 Subject: [PATCH 091/284] Simplify code. --- src/Compilers/Core/Portable/Serialization/IObjectWritable.cs | 2 +- src/Compilers/Core/Portable/Serialization/ObjectBinder.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/IObjectWritable.cs b/src/Compilers/Core/Portable/Serialization/IObjectWritable.cs index a5c494ff6d7bb..77956a643abf8 100644 --- a/src/Compilers/Core/Portable/Serialization/IObjectWritable.cs +++ b/src/Compilers/Core/Portable/Serialization/IObjectWritable.cs @@ -10,4 +10,4 @@ internal interface IObjectWritable { void WriteTo(ObjectWriter writer); } -} +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Serialization/ObjectBinder.cs b/src/Compilers/Core/Portable/Serialization/ObjectBinder.cs index 59821dd731bcc..d3fcc95b5d182 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectBinder.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectBinder.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; namespace Roslyn.Utilities { From 9364b8c6c0320da0c99c9a0642185992da43108e Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Wed, 10 May 2017 17:46:52 +0430 Subject: [PATCH 092/284] PR feedback --- .../ConvertNumericLiteral/ConvertNumericLiteralTests.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb index b38d228341454..d3810bad590af 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb @@ -109,7 +109,7 @@ End Class", &B1, &H2 } End Sub -End Class", index := Refactoring.ChangeBase2, ignoreTrivia := False) +End Class", index:=Refactoring.ChangeBase2, ignoreTrivia:=False) End Function End Class End Namespace From cc4ea4693cf28b60daf31113c680bdbd55d081cb Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Wed, 10 May 2017 09:41:55 -0700 Subject: [PATCH 093/284] Entrypoint detection stages (#19093) * simplify partial method return type syntax finder tests * Bug fixes and API use changes * address first review * better comment and compiler trait on class * fix tests, pull entrypoint check out into local function * unify diagnostics for intvoid or task mains * add more tests, change error text * address review * finish tests * be explicit on language version in tests * add another test, fix typo, add diagnostics even when they aren't likely to exist * rewrite error->warning code, add more cases to tests * change test language versions * formatting on comments and adding WithLocation to tests * fix formatting for some tests * fix some formatting and added withlocation * fix withlocation --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 9 +- .../Portable/CSharpResources.Designer.cs | 29 +- .../CSharp/Portable/CSharpResources.resx | 6 +- .../Portable/Compilation/CSharpCompilation.cs | 129 +++-- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../SynthesizedEntryPointSymbol.cs | 1 - .../Emit/CodeGen/CodeGenAsyncMainTests.cs | 459 +++++++++++++++--- .../Semantic/Semantics/BindingAsyncTests.cs | 10 +- .../Test/Symbol/Symbols/Source/MethodTests.cs | 23 +- .../Portable/Traits/CompilerFeature.cs | 1 + 10 files changed, 520 insertions(+), 149 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index d02e32d22e28e..2cc61856e9e13 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -2060,12 +2060,12 @@ 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) { var options = (CSharpParseOptions)syntax.SyntaxTree.Options; if (options.IsFeatureEnabled(feature)) { - return; + return true; } var location = locationOpt ?? syntax.GetLocation(); @@ -2073,7 +2073,7 @@ internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID featu if (requiredFeature != null) { diagnostics.Add(ErrorCode.ERR_FeatureIsExperimental, location, feature.Localize(), requiredFeature); - return; + return false; } LanguageVersion availableVersion = options.LanguageVersion; @@ -2081,7 +2081,10 @@ internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID featu if (requiredVersion > availableVersion) { diagnostics.Add(availableVersion.GetErrorCode(), location, feature.Localize(), new CSharpRequiredLanguageVersion(requiredVersion)); + return false; } + + return true; } } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 1280b1141bdb8..de6e009a64bba 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -6882,7 +6882,18 @@ internal static string ERR_NonInvocableMemberCalled { return ResourceManager.GetString("ERR_NonInvocableMemberCalled", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Async Main methods must return Task or Task<int>. + /// + internal static string ERR_NonTaskMainCantBeAsync + { + get + { + return ResourceManager.GetString("ERR_NonTaskMainCantBeAsync", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot embed interop types from assembly '{0}' because it is missing the '{1}' attribute.. /// @@ -9852,22 +9863,24 @@ internal static string IDS_Covariantly { return ResourceManager.GetString("IDS_Covariantly", resourceCulture); } } - + /// /// Looks up a localized string similar to /// Visual C# Compiler Options /// /// - OUTPUT FILES - - /// /out:<file> Specify output file name (default: base name of + /// /out:<file> Specify output file name (default: base name of /// file with main class or first file) - /// /target:exe Build a console executable (default) (Short + /// /target:exe Build a console executable (default) (Short /// form: /t:exe) - /// /target:winexe Build a Windows executable (Short form: + /// /target:winexe Build a Windows executable (Short form: /// /t:winexe) - /// /target:library [rest of string was truncated]";. + /// /target:library [rest of string was truncated]";. /// - internal static string IDS_CSCHelp { - get { + internal static string IDS_CSCHelp + { + get + { return ResourceManager.GetString("IDS_CSCHelp", resourceCulture); } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 78bf4e6b5f7b5..82afbcaa4e07b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3596,9 +3596,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi Async methods are not allowed in an Interface, Class, or Structure which has the 'SecurityCritical' or 'SecuritySafeCritical' attribute. - - '{0}': an entry point cannot be marked with the 'async' modifier - The 'await' operator may only be used in a query expression within the first collection expression of the initial 'from' clause or within the collection expression of a 'join' clause @@ -5073,4 +5070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A tuple may not contain a value of type 'void'. + + A void or int returning entry point cannot be async + diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index c1dada07c57d0..9e078424051a2 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1453,42 +1453,101 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm } } - DiagnosticBag warnings = DiagnosticBag.GetInstance(); - var viableEntryPoints = ArrayBuilder.GetInstance(); - foreach (var candidate in entryPointCandidates) + // 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(); + + // These diagnostics (warning only) are added to the compilation only if + // there were not any main methods found. + DiagnosticBag noMainFoundDiagnostics = DiagnosticBag.GetInstance(); + + bool CheckValid(MethodSymbol candidate, bool isCandidate, DiagnosticBag specificDiagnostics) { - if (!HasEntryPointSignature(candidate, warnings)) + if (!isCandidate) { - // a single error for partial methods - warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); - continue; + noMainFoundDiagnostics.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); + noMainFoundDiagnostics.AddRange(specificDiagnostics); + return false; } if (candidate.IsGenericMethod || candidate.ContainingType.IsGenericType) { // a single error for partial methods: - warnings.Add(ErrorCode.WRN_MainCantBeGeneric, candidate.Locations.First(), candidate); - continue; + noMainFoundDiagnostics.Add(ErrorCode.WRN_MainCantBeGeneric, candidate.Locations.First(), candidate); + return false; } + return true; + } + + var viableEntryPoints = ArrayBuilder.GetInstance(); + + foreach (var candidate in entryPointCandidates) + { + var perCandidateBag = DiagnosticBag.GetInstance(); + var (IsCandidate, IsTaskLike) = HasEntryPointSignature(candidate, perCandidateBag); - // PROTOTYPE(async-main): CheckFeatureAvailability should be called on non-async methods - // that return Task or Task - if (candidate.IsAsync) + if (IsTaskLike) { - // PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. - // PROTOTYPE(async-main): Switch diagnostics around if the type is not Task or Task - CheckFeatureAvailability(candidate.GetNonNullSyntaxNode(), MessageID.IDS_FeatureAsyncMain, diagnostics); + taskEntryPoints.Add((IsCandidate, candidate, perCandidateBag)); + } + else + { + if (CheckValid(candidate, IsCandidate, perCandidateBag)) + { + if (candidate.IsAsync) + { + diagnostics.Add(ErrorCode.ERR_NonTaskMainCantBeAsync, candidate.Locations.First(), candidate); + } + else + { + diagnostics.AddRange(perCandidateBag); + viableEntryPoints.Add(candidate); + } + } + perCandidateBag.Free(); } + } - viableEntryPoints.Add(candidate); + if (viableEntryPoints.Count == 0) + { + foreach (var (IsValid, Candidate, SpecificDiagnostics) in taskEntryPoints) + { + // PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. + if (CheckValid(Candidate, IsValid, SpecificDiagnostics) && + CheckFeatureAvailability(Candidate.GetNonNullSyntaxNode(), MessageID.IDS_FeatureAsyncMain, diagnostics)) + { + diagnostics.AddRange(SpecificDiagnostics); + viableEntryPoints.Add(Candidate); + } + } } - if ((object)mainType == null || viableEntryPoints.Count == 0) + foreach (var (_, _, SpecificDiagnostics) in taskEntryPoints) { - diagnostics.AddRange(warnings); + SpecificDiagnostics.Free(); } - warnings.Free(); + if (viableEntryPoints.Count == 0) + { + diagnostics.AddRange(noMainFoundDiagnostics); + } + else if ((object)mainType == null) + { + // Filters out diagnostics so that only InvalidMainSig and MainCant'BeGeneric are left. + // The reason that Error diagnostics can end up in `noMainFoundDiagnostics` is when + // HasEntryPointSignature yields some Error Diagnostics when people implement Task or Task incorrectly. + // + // 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 + foreach (var diagnostic in noMainFoundDiagnostics.AsEnumerable()) + { + if (diagnostic.Code == (int)ErrorCode.WRN_InvalidMainSig || diagnostic.Code == (int)ErrorCode.WRN_MainCantBeGeneric) + { + diagnostics.Add(diagnostic); + } + } + } MethodSymbol entryPoint = null; if (viableEntryPoints.Count == 0) @@ -1518,7 +1577,9 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm entryPoint = viableEntryPoints[0]; } + taskEntryPoints.Free(); viableEntryPoints.Free(); + noMainFoundDiagnostics.Free(); return entryPoint; } finally @@ -1561,15 +1622,16 @@ internal bool ReturnsAwaitableToVoidOrInt(MethodSymbol method, DiagnosticBag dia /// /// Checks if the method has an entry point compatible signature, i.e. - /// - the return type is either void, int, , - /// or where T is an int. + /// - the return type is either void, int, or returns a , + /// or where the return type of GetAwaiter().GetResult() + /// is either void or int. /// - has either no parameter or a single parameter of type string[] /// - private bool HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) + private (bool IsCandidate, bool IsTaskLike) HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) { if (method.IsVararg) { - return false; + return (false, false); } TypeSymbol returnType = method.ReturnType; @@ -1580,45 +1642,38 @@ private bool HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) returnsTaskOrTaskOfInt = ReturnsAwaitableToVoidOrInt(method, bag); if (!returnsTaskOrTaskOfInt) { - return false; + return (false, false); } } - // Prior to 7.1, async methods were considered to "have the entrypoint signature". - // In order to keep back-compat, we need to let these through if compiling using a previous language version. - if (!returnsTaskOrTaskOfInt && method.IsAsync && this.LanguageVersion >= LanguageVersion.CSharp7_1) - { - return false; - } - if (method.RefKind != RefKind.None) { - return false; + return (false, returnsTaskOrTaskOfInt); } if (method.Parameters.Length == 0) { - return true; + return (true, returnsTaskOrTaskOfInt); } if (method.Parameters.Length > 1) { - return false; + return (false, returnsTaskOrTaskOfInt); } if (!method.ParameterRefKinds.IsDefault) { - return false; + return (false, returnsTaskOrTaskOfInt); } var firstType = method.Parameters[0].Type; if (firstType.TypeKind != TypeKind.Array) { - return false; + return (false, returnsTaskOrTaskOfInt); } var array = (ArrayTypeSymbol)firstType; - return array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String; + return (array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String, returnsTaskOrTaskOfInt); } internal override bool IsUnreferencedAssemblyIdentityDiagnosticCode(int code) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index cdaf5879c9e2a..ea7ce6b4a38fd 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1104,7 +1104,7 @@ internal enum ErrorCode ERR_VarargsAsync = 4006, ERR_ByRefTypeAndAwait = 4007, ERR_BadAwaitArgVoidCall = 4008, - // ERR_MainCantBeAsync = 4009, + ERR_NonTaskMainCantBeAsync = 4009, ERR_CantConvAsyncAnonFuncReturns = 4010, ERR_BadAwaiterPattern = 4011, ERR_BadSpecialByRefLocal = 4012, diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 58187a5827b59..826b6d3ae3d80 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -388,7 +388,6 @@ internal override BoundBlock CreateBody() { var syntax = _userMainReturnTypeSyntax; - if (ReturnsVoid) { return new BoundBlock( diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs index 3dd02d4548495..fbaae3a50786b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs @@ -3,14 +3,15 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Xunit; using System.Threading; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.AsyncMain)] public class CodeGenAsyncMainTests : EmitMetadataTestBase { - [Fact] - public void MultipleMainsOneOfWhichHasBadTaskType_WithMainType() + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp71_WithMainType() { var source = @" using System.Threading.Tasks; @@ -35,7 +36,7 @@ static void Main(string[] args) { } } [Fact] - public void MultipleMainsOneOfWhichHasBadTaskType() + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp7() { var source = @" using System.Threading.Tasks; @@ -52,21 +53,114 @@ static Task Main() { } static void Main(string[] args) { } }"; - var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); sourceCompilation.VerifyEmitDiagnostics( // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. // static Task Main() { Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12), - // (11,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(11, 22)); + } + + [Fact] + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp7_WithExplicitMain() + { + var source = @" +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } + static void Main(string[] args) { } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe.WithMainTypeName("Program"), parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + sourceCompilation.VerifyEmitDiagnostics( + // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. // static Task Main() { - Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(11, 12), + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12)); + } + + [Fact] + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp71() + { + var source = @" +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } + static void Main(string[] args) { } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12), // (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point // static Task Main() { Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(11, 22)); } [Fact] - public void GetResultReturnsSomethingElse() + public void GetResultReturnsSomethingElse_CSharp7() + { + var source = @" +using System.Threading.Tasks; +using System; + +namespace System.Runtime.CompilerServices { + public interface INotifyCompletion { + void OnCompleted(Action action); + } +} + +namespace System.Threading.Tasks { + public class Awaiter: System.Runtime.CompilerServices.INotifyCompletion { + public double GetResult() { return 0.0; } + public bool IsCompleted => true; + public void OnCompleted(Action action) {} + } + public class Task { + public Awaiter GetAwaiter() { + return new Awaiter(); + } + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + sourceCompilation.VerifyEmitDiagnostics( + // (25,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(25, 12), + // (25,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(25, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void GetResultReturnsSomethingElse_CSharp71() { var source = @" using System.Threading.Tasks; @@ -105,12 +199,48 @@ static Task Main() { // static Task Main() { Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(25, 22), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1) -); + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact] - public void TaskOfTGetAwaiterReturnsVoid() + public void TaskOfTGetAwaiterReturnsVoid_CSharp7() + { + var source = @" +using System; +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + sourceCompilation.VerifyDiagnostics( + // (12,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // static Task Main() { + Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(12, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), + // (2,1): hidden CS8019: Unnecessary using directive. + // using System; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(2, 1)); + } + + [Fact] + public void TaskOfTGetAwaiterReturnsVoid_CSharp71() { var source = @" using System; @@ -145,6 +275,7 @@ static Task Main() { // using System; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(2, 1)); } + [Fact] public void TaskGetAwaiterReturnsVoid() { @@ -215,6 +346,8 @@ public void EmitTaskOfIntReturningMainWithoutInt() var corAssembly = @" namespace System { public class Object {} + public abstract class ValueType{} + public struct Int32{} }"; var corCompilation = CreateCompilation(corAssembly, options: TestOptions.DebugDll); corCompilation.VerifyDiagnostics(); @@ -241,9 +374,6 @@ static Task Main() { Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // (6,17): error CS0518: Predefined type 'System.Int32' is not defined or imported // static Task Main() { - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(6, 17), - // (6,12): error CS1061: 'Task' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly reference?) - // static Task Main() { Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Task").WithArguments("System.Threading.Tasks.Task", "GetAwaiter").WithLocation(6, 12), // (6,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point // static Task Main() { @@ -292,26 +422,6 @@ static Task Main() { Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } - [Fact] - public void AsyncEmitMainOfIntTest() - { - var source = @" -using System; -using System.Threading.Tasks; - -class Program { - static async Task Main() { - Console.Write(""hello ""); - await Task.Factory.StartNew(() => 5); - Console.Write(""async main""); - return 10; - } -}"; - var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 10); - - } - [Fact] public void AsyncEmitMainTest() { @@ -548,6 +658,8 @@ async static Task Main(string[] args) }"; var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); compilation.VerifyDiagnostics( + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async static Task Main(string[] args) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) @@ -596,7 +708,7 @@ static Task Main() } [Fact] - public void MainCantBeAsyncVoid() + public void MainCantBeAsyncVoid_CSharp7() { var source = @" using System.Threading.Tasks; @@ -608,38 +720,13 @@ async static void Main() await Task.Factory.StartNew(() => { }); } }"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); compilation.VerifyDiagnostics( - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // (6,23): error CS9003: Async Main methods must return Task or Task // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(6, 23), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.Null(entry); - } - - [Fact] - public void MainCantBeAsyncVoid_CSharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main() - { - await Task.Factory.StartNew(() => { }); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static void Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static void Main() - { - await Task.Factory.StartNew(() => { }); - }").WithArguments("async main", "7.1").WithLocation(6, 5)); } [Fact] @@ -660,9 +747,9 @@ async static int Main() // (6,22): error CS1983: The return type of an async method must be void, Task or Task // async static int Main() Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point - // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 22), + // (6,22): error CS4009: A void or int returning entry point cannot be async + // async static int Main() + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(6, 22), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); var entry = compilation.GetEntryPoint(CancellationToken.None); @@ -686,14 +773,12 @@ async static int Main() compilation.VerifyDiagnostics( // (6,22): error CS1983: The return type of an async method must be void, Task or Task // async static int Main() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main"), - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), + // (6,22): error CS9003: Async Main methods must return Task or Task // async static int Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static int Main() - { - return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) -); + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(6, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact] @@ -751,13 +836,14 @@ async static Task Main(string[] args) }"; var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); compilation.VerifyDiagnostics( + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async static Task Main(string[] args) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) { return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) -); + }").WithArguments("async main", "7.1").WithLocation(6, 5)); } [Fact] @@ -779,6 +865,7 @@ async static Task Main() Assert.NotNull(entry); Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); } + [Fact] public void MainCanReturnTaskAndGenericOnInt_NoAsync() { @@ -819,8 +906,9 @@ async static Task Main() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() { return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) - ); + }").WithArguments("async main", "7.1").WithLocation(6, 5), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact] @@ -860,9 +948,9 @@ async static void Main() } }"; CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // (6,23): warning CS0402: 'A.Main()': an entry point cannot be generic or in a generic type // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + Diagnostic(ErrorCode.WRN_MainCantBeGeneric, "Main").WithArguments("A.Main()").WithLocation(6, 23), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } @@ -908,5 +996,220 @@ async static void Main(bool truth) // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } + + [Fact] + public void TaskMainAndNonTaskMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void TaskMainAndNonTaskMain_CSharp71() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void AsyncVoidMain_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Async Void Main""); + } + async static int Main() + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Async Void Main""); + return 1; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + // (11,22): error CS1983: The return type of an async method must be void, Task or Task + // async static int Main() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(11, 22), + // (11,22): error CS4009: A void or int returning entry point cannot be async + // async static int Main() + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(11, 22), + // (6,23): error CS4009: A void or int returning entry point cannot be async + // async static void Main(string[] args) + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main(string[])").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void AsyncVoidMain_CSharp71() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static async void Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Async Void Main""); + } +}"; + 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 + // static async void Main(string[] args) + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main(string[])").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void TaskMainAndNonTaskMain_WithExplicitMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe.WithMainTypeName("A")).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void TaskIntAndTaskFloat_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + System.Console.WriteLine(""Task""); + return 0; + } + + async static Task Main(string[] args) + { + System.Console.WriteLine(""Task""); + return 0.0F; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseDebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( + // (6,28): 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 static Task Main() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(6, 28), + // (12,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 static Task Main(string[] args) + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(12, 30), + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() + { + System.Console.WriteLine(""Task""); + return 0; + }").WithArguments("async main", "7.1").WithLocation(6, 5), + // (12,30): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point + // async static Task Main(string[] args) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(12, 30), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + + [Fact] + public void TaskOfFloatMainAndNonTaskMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + return 0.0f; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + // (10,30): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point + // async static Task Main(string[] args) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(11, 30)); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void TaskOfFloatMainAndNonTaskMain_WithExplicitMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + return 0.0f; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe.WithMainTypeName("A")).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index ea6c34daf69c4..b5ceade1893ea 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -3403,7 +3403,7 @@ public static int Main() } [Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")] - public void Repro_17885() + public void Repro_17885_CSharp_71() { var source = @" using System.Threading.Tasks; @@ -3416,7 +3416,7 @@ async public static Task Main() CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( // (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")); + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(5, 30)); } [Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")] @@ -3433,12 +3433,14 @@ async public static Task Main() CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( // (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"), + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(5, 30), // (5,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async public static Task Main() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async public static Task Main() { - }").WithArguments("async main", "7.1").WithLocation(5, 5)); + }").WithArguments("async main", "7.1").WithLocation(5, 5), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact, WorkItem(547088, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547088")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index e0b17fba31631..7b9c0c19250a6 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -5,6 +5,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; @@ -222,12 +223,10 @@ partial void M() {} var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree); - var voidTokens = tree.GetRoot().DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).ToList(); - var node = tree.GetRoot().FindNode(voidTokens[0].Span); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).First(); - var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList(); - var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[1].Span)); + var otherSymbol = m.PartialImplementationPart; + Assert.True(otherSymbol.IsPartialImplementation()); Assert.Equal(node, returnSyntax); Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax()); @@ -252,12 +251,10 @@ public partial class A { var returnSyntax = m.ExtractReturnTypeSyntax(); 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); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Last(); - var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList(); - var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[0].Span)); + var otherSymbol = m.PartialImplementationPart; + Assert.True(otherSymbol.IsPartialImplementation()); Assert.Equal(node, returnSyntax); Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax()); @@ -279,8 +276,7 @@ partial void M() {} var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single().GetRoot(); - var token = tree.DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).First(); - var node = tree.FindNode(token.Span); + var node = tree.DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Single(); Assert.Equal(node, returnSyntax); } @@ -301,8 +297,7 @@ public void PartialExtractSyntaxLocation_OnlyDecl() var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single().GetRoot(); - var token = tree.DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).First(); - var node = tree.FindNode(token.Span); + var node = tree.DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Single(); Assert.Equal(node, returnSyntax); } diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs index e3ac493c97775..0530fb6217152 100644 --- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs @@ -20,5 +20,6 @@ public enum CompilerFeature OutVar, Patterns, DefaultLiteral, + AsyncMain, } } From 124865bf9b87e71972e7af9ca278385ac1a6a529 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 10 May 2017 09:46:36 -0700 Subject: [PATCH 094/284] updated following PR feedback --- .../Core/Next/Remote/JsonRpcSession.cs | 10 ++- .../Execution/PinnedRemotableDataScope.cs | 5 +- .../Remote/Core/RemoteWorkspaces.csproj | 1 + .../Core/Services/ISolutionController.cs | 17 ++++ .../Remote/Core/Services/SolutionService.cs | 84 +++++++++++-------- .../ServiceHub/Services/RemoteHostService.cs | 3 +- .../Shared/ServiceHubServiceBase.cs | 24 ++++-- 7 files changed, 97 insertions(+), 47 deletions(-) create mode 100644 src/Workspaces/Remote/Core/Services/ISolutionController.cs diff --git a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs index 5ce8f5f5472f7..49c560ffadd06 100644 --- a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs +++ b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs @@ -86,13 +86,17 @@ private JsonRpcSession( private async Task InitializeAsync() { - // all roslyn remote service must based on ServiceHubServiceBase which implements Initialize method + // All roslyn remote service must based on ServiceHubServiceBase which implements Initialize method + // This will set this session's solution and whether that solution is for primary branch or not + var primaryBranch = PinnedScopeOpt?.ForPrimaryBranch ?? false; + var solutionChecksum = PinnedScopeOpt?.SolutionChecksum; + if (_snapshotClientOpt != null) { - await _snapshotClientOpt.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt.Primary, PinnedScopeOpt.SolutionChecksum).ConfigureAwait(false); + await _snapshotClientOpt.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, primaryBranch, solutionChecksum).ConfigureAwait(false); } - await _serviceClient.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, PinnedScopeOpt?.Primary ?? false, PinnedScopeOpt?.SolutionChecksum).ConfigureAwait(false); + await _serviceClient.InvokeAsync(WellKnownServiceHubServices.ServiceHubServiceBase_Initialize, _currentSessionId, primaryBranch, solutionChecksum).ConfigureAwait(false); } public override Task InvokeAsync(string targetName, params object[] arguments) diff --git a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs index b318627aacac8..f09b9ada783c5 100644 --- a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs +++ b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs @@ -33,7 +33,10 @@ public PinnedRemotableDataScope( _storages.RegisterSnapshot(this, storage); } - public bool Primary => _storage.SolutionState.BranchId == Workspace.PrimaryBranchId; + /// + /// This indicates whether this scope is for primary branch or not (not forked solution) + /// + public bool ForPrimaryBranch => _storage.SolutionState.BranchId == Workspace.PrimaryBranchId; public Workspace Workspace => _storage.SolutionState.Workspace; diff --git a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj index 3790331862d61..8d74222e4ee66 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj +++ b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj @@ -70,6 +70,7 @@ + diff --git a/src/Workspaces/Remote/Core/Services/ISolutionController.cs b/src/Workspaces/Remote/Core/Services/ISolutionController.cs new file mode 100644 index 0000000000000..c84e03f5ed9ef --- /dev/null +++ b/src/Workspaces/Remote/Core/Services/ISolutionController.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Remote +{ + /// + /// Engine use this to set or update solution for given session (connection) + /// + internal interface ISolutionController + { + Task GetSolutionAsync(Checksum solutionChecksum, bool fromPrimaryBranch, CancellationToken cancellationToken); + + Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken); + } +} diff --git a/src/Workspaces/Remote/Core/Services/SolutionService.cs b/src/Workspaces/Remote/Core/Services/SolutionService.cs index c9dd90db2359d..0a2019cb24af7 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionService.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionService.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Roslyn.Utilities; @@ -13,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Remote /// /// TODO: change this to workspace service /// - internal class SolutionService + internal class SolutionService : ISolutionController { private static readonly SemaphoreSlim s_gate = new SemaphoreSlim(initialCount: 1); private static readonly RemoteWorkspace s_primaryWorkspace = new RemoteWorkspace(); @@ -31,13 +30,12 @@ public SolutionService(AssetService assetService) public Task GetSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { - return GetSolutionAsync(solutionChecksum, primary: false, cancellationToken: cancellationToken); + // this method is called by users which means we don't know whether the solution is from primary branch or not. + // so we will be conservative and assume it is not. meaning it won't update any internal caches but only consume cache if possible. + return GetSolutionInternalAsync(solutionChecksum, fromPrimaryBranch: false, cancellationToken: cancellationToken); } - /// - /// This should be only consumed by engine, not by user. - /// - public async Task GetSolutionAsync(Checksum solutionChecksum, bool primary, CancellationToken cancellationToken) + private async Task GetSolutionInternalAsync(Checksum solutionChecksum, bool fromPrimaryBranch, CancellationToken cancellationToken) { var currentSolution = GetAvailableSolution(solutionChecksum); if (currentSolution != null) @@ -54,65 +52,77 @@ public async Task GetSolutionAsync(Checksum solutionChecksum, bool pri return currentSolution; } - var solution = await CreateSolution_NoLockAsync(solutionChecksum, primary, s_primaryWorkspace.CurrentSolution, cancellationToken).ConfigureAwait(false); + var solution = await CreateSolution_NoLockAsync(solutionChecksum, fromPrimaryBranch, s_primaryWorkspace.CurrentSolution, cancellationToken).ConfigureAwait(false); s_lastSolution = Tuple.Create(solutionChecksum, solution); return solution; } } - /// - /// This should be only consumed by engine, not by user. - /// - public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken) - { - var currentSolution = s_primaryWorkspace.CurrentSolution; - - var primarySolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); - if (primarySolutionChecksum == solutionChecksum) - { - return; - } - - using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) - { - var primary = true; - var solution = await CreateSolution_NoLockAsync(solutionChecksum, primary, currentSolution, cancellationToken).ConfigureAwait(false); - s_primarySolution = Tuple.Create(solutionChecksum, solution); - } - } - - private async Task CreateSolution_NoLockAsync(Checksum solutionChecksum, bool primary, Solution baseSolution, CancellationToken cancellationToken) + private async Task CreateSolution_NoLockAsync(Checksum solutionChecksum, bool fromPrimaryBranch, Solution baseSolution, CancellationToken cancellationToken) { var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); + // check whether solution is update to the given base solution if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { - // solution has updated - if (primary) + // create updated solution off the baseSolution + var solution = await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); + + if (fromPrimaryBranch) { - s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); + // if the solutionChecksum is for primary branch, update primary workspace cache with the solution + s_primaryWorkspace.UpdateSolution(solution); return s_primaryWorkspace.CurrentSolution; } - return await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); + // otherwise, just return the solution + return solution; } - // new solution. bulk sync all asset for the solution + // we need new solution. bulk sync all asset for the solution first. await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); - if (primary) + // get new solution info + var solutionInfo = await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false); + + if (fromPrimaryBranch) { + // if the solutionChecksum is for primary branch, update primary workspace cache with new solution s_primaryWorkspace.ClearSolution(); - s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); + s_primaryWorkspace.AddSolution(solutionInfo); return s_primaryWorkspace.CurrentSolution; } + // otherwise, just return new solution var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); return workspace.CurrentSolution; } + async Task ISolutionController.GetSolutionAsync(Checksum solutionChecksum, bool primary, CancellationToken cancellationToken) + { + return await GetSolutionInternalAsync(solutionChecksum, primary, cancellationToken).ConfigureAwait(false); + } + + async Task ISolutionController.UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken) + { + var currentSolution = s_primaryWorkspace.CurrentSolution; + + var primarySolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); + if (primarySolutionChecksum == solutionChecksum) + { + return; + } + + using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + { + var primary = true; + var solution = await CreateSolution_NoLockAsync(solutionChecksum, primary, currentSolution, cancellationToken).ConfigureAwait(false); + s_primarySolution = Tuple.Create(solutionChecksum, solution); + } + } + private static Solution GetAvailableSolution(Checksum solutionChecksum) { var currentSolution = s_primarySolution; diff --git a/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs b/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs index 35449361371ce..3adee6306e184 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs @@ -81,7 +81,8 @@ public async Task SynchronizePrimaryWorkspaceAsync(Checksum checksum) { try { - await RoslynServices.SolutionService.UpdatePrimaryWorkspaceAsync(checksum, CancellationToken).ConfigureAwait(false); + var solutionController = (ISolutionController)RoslynServices.SolutionService; + await solutionController.UpdatePrimaryWorkspaceAsync(checksum, CancellationToken).ConfigureAwait(false); } catch (IOException) { diff --git a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs index 051a27624a059..a02a3ca22f613 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServices.Remote; using Roslyn.Utilities; using StreamJsonRpc; @@ -27,9 +26,22 @@ internal abstract class ServiceHubServiceBase : IDisposable protected readonly AssetStorage AssetStorage; protected readonly CancellationToken CancellationToken; + /// + /// Session Id of this service. caller and callee share this id which one + /// can use to find matching caller and callee when debugging or logging + /// private int _sessionId; - private bool _primary; + + /// + /// Mark whether the solution checksum it got is for primary branch or not + /// + private bool _fromPrimaryBranch; + + /// + /// solution this connection belong to + /// private Checksum _solutionChecksumOpt; + private RoslynServices _lazyRoslynServices; [Obsolete("For backward compatibility. this will be removed once all callers moved to new ctor")] @@ -91,7 +103,9 @@ protected RoslynServices RoslynServices protected Task GetSolutionAsync() { Contract.ThrowIfNull(_solutionChecksumOpt); - return RoslynServices.SolutionService.GetSolutionAsync(_solutionChecksumOpt, _primary, CancellationToken); + + var solutionController = (ISolutionController)RoslynServices.SolutionService; + return solutionController.GetSolutionAsync(_solutionChecksumOpt, _fromPrimaryBranch, CancellationToken); } protected virtual void Dispose(bool disposing) @@ -104,11 +118,11 @@ protected void LogError(string message) Log(TraceEventType.Error, message); } - public virtual void Initialize(int sessionId, bool primary, Checksum solutionChecksum) + public virtual void Initialize(int sessionId, bool fromPrimaryBranch, Checksum solutionChecksum) { // set session related information _sessionId = sessionId; - _primary = primary; + _fromPrimaryBranch = fromPrimaryBranch; if (solutionChecksum != null) { From f67bfae237fba950c0cc8732a31e81daba5bf539 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 10 May 2017 10:53:20 -0700 Subject: [PATCH 095/284] fix build break and add test --- .../Core/Test.Next/Remote/JsonConverterTests.cs | 12 ++++++++++++ .../Core/Test.Next/Services/SolutionServiceTests.cs | 10 +++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs b/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs index a98e79f19e12f..782fcae33614c 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs @@ -25,6 +25,18 @@ public void TestChecksum() VerifyJsonSerialization(new Checksum(Guid.NewGuid().ToByteArray())); } + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public void TestChecksum_Null() + { + VerifyJsonSerialization(null); + } + + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public void TestChecksumNull() + { + VerifyJsonSerialization(Checksum.Null); + } + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public void TestSolutionId() { diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 0d5aaf3a39ad7..17fdd709da3b1 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -60,8 +60,8 @@ public async Task TestGetSolutionWithPrimaryFlag() var solution = workspace.CurrentSolution; var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - var service = await GetSolutionServiceAsync(solution); - var synched = await service.GetSolutionAsync(solutionChecksum, primary: true, cancellationToken: CancellationToken.None); + var service = (ISolutionController)await GetSolutionServiceAsync(solution); + var synched = await service.GetSolutionAsync(solutionChecksum, fromPrimaryBranch: true, cancellationToken: CancellationToken.None); Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); Assert.True(synched.Workspace is RemoteWorkspace); } @@ -278,7 +278,7 @@ public async Task TestRemoteWorkspaceSolutionCrawler() // update primary workspace var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - await service.UpdatePrimaryWorkspaceAsync(solutionChecksum, CancellationToken.None); + await ((ISolutionController)service).UpdatePrimaryWorkspaceAsync(solutionChecksum, CancellationToken.None); // get solution in remote host var oopSolution = await service.GetSolutionAsync(solutionChecksum, CancellationToken.None); @@ -326,7 +326,7 @@ private static async Task VerifySolutionUpdate(TestWorkspace workspace, Func Date: Wed, 10 May 2017 10:53:27 -0700 Subject: [PATCH 096/284] Re-enable roslyn analyzers on Roslyn.sln and update public api file for an IOperation API change merged yesterday (#19278). --- build/Targets/Packages.props | 2 +- build/Targets/Settings.props | 2 +- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/Targets/Packages.props b/build/Targets/Packages.props index 638af15a836ae..3350acfec27bf 100644 --- a/build/Targets/Packages.props +++ b/build/Targets/Packages.props @@ -47,7 +47,7 @@ 8.0.0.0-alpha 2.0.1 2.0.0-rc2-61102-09 - 1.2.0-beta2 + 2.0.0-beta1-61628-06 1.1.0 2.3.0-dev-56735-00 5.0.0 diff --git a/build/Targets/Settings.props b/build/Targets/Settings.props index f0933dfe67e3a..84a23b39d3a49 100644 --- a/build/Targets/Settings.props +++ b/build/Targets/Settings.props @@ -14,7 +14,7 @@ $(RepoRoot)build\ToolsetPackages\ RoslynTools.Microsoft.VSIXExpInstaller 0.2.1-beta - 2.0.0-beta1-61628-06 + $(MicrosoftNetRoslynDiagnosticsVersion) $(NuGetPackageRoot)\Microsoft.Net.RoslynDiagnostics\$(RoslynDiagnosticsNugetPackageVersion)\build\Microsoft.Net.RoslynDiagnostics.props $(NuGetPackageRoot)\Roslyn.Build.Util\0.9.4-portable\lib\dotnet\Roslyn.MSBuild.Util.dll win7-x64 diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 66f8f5b0dd93c..e4e61df95b69f 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -452,7 +452,7 @@ Microsoft.CodeAnalysis.Semantics.INullCoalescingExpression.PrimaryOperand.get -> Microsoft.CodeAnalysis.Semantics.INullCoalescingExpression.SecondaryOperand.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.IObjectCreationExpression Microsoft.CodeAnalysis.Semantics.IObjectCreationExpression.Constructor.get -> Microsoft.CodeAnalysis.IMethodSymbol -Microsoft.CodeAnalysis.Semantics.IObjectCreationExpression.MemberInitializers.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Semantics.IObjectCreationExpression.Initializers.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Semantics.IOmittedArgumentExpression Microsoft.CodeAnalysis.Semantics.IParameterInitializer Microsoft.CodeAnalysis.Semantics.IParameterInitializer.Parameter.get -> Microsoft.CodeAnalysis.IParameterSymbol From 5e053aaf90864362af42d3d7d7b45040e4595fb8 Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Wed, 10 May 2017 11:22:55 -0700 Subject: [PATCH 097/284] remove _showPosition (#19353) --- .../Portable/Diagnostics/DiagnosticDescription.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Test/Utilities/Portable/Diagnostics/DiagnosticDescription.cs b/src/Test/Utilities/Portable/Diagnostics/DiagnosticDescription.cs index 33c8fee399f40..e272088218103 100644 --- a/src/Test/Utilities/Portable/Diagnostics/DiagnosticDescription.cs +++ b/src/Test/Utilities/Portable/Diagnostics/DiagnosticDescription.cs @@ -24,7 +24,6 @@ public sealed class DiagnosticDescription private readonly string _squiggledText; private readonly object[] _arguments; private readonly LinePosition? _startPosition; // May not have a value only in the case that we're constructed via factories - private bool _showPosition; // show start position in ToString if comparison fails private readonly bool _argumentOrderDoesNotMatter; private readonly Type _errorCodeType; private readonly bool _ignoreArgumentsWhenComparing; @@ -96,7 +95,7 @@ public DiagnosticDescription( _errorCodeType = errorCodeType ?? code.GetType(); } - public DiagnosticDescription(Diagnostic d, bool errorCodeOnly, bool showPosition = false) + public DiagnosticDescription(Diagnostic d, bool errorCodeOnly) { _code = d.Code; _isWarningAsError = d.IsWarningAsError; @@ -124,7 +123,6 @@ public DiagnosticDescription(Diagnostic d, bool errorCodeOnly, bool showPosition } _ignoreArgumentsWhenComparing = errorCodeOnly; - _showPosition = showPosition; if (!_ignoreArgumentsWhenComparing) { @@ -219,13 +217,8 @@ public override bool Equals(object obj) { if (_startPosition.Value != d._startPosition.Value) { - _showPosition = true; - d._showPosition = true; return false; } - - _showPosition = false; - d._showPosition = false; } } @@ -354,7 +347,7 @@ public override string ToString() sb.Append(")"); } - if (_startPosition != null && _showPosition) + if (_startPosition != null) { sb.Append(".WithLocation("); sb.Append(_startPosition.Value.Line + 1); @@ -438,7 +431,7 @@ public static string GetAssertText(DiagnosticDescription[] expected, IEnumerable } } - var description = new DiagnosticDescription(d, errorCodeOnly: false, showPosition: true); + var description = new DiagnosticDescription(d, errorCodeOnly: false); var diffDescription = description; var idx = Array.IndexOf(expected, description); if (idx != -1) From eddbafc4594b3e00d0e14f0e082e28ea60fe6fce Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 10 May 2017 12:15:15 -0700 Subject: [PATCH 098/284] Addressed pr feedback, export parts as shared and in the correct layer. --- Roslyn.sln | 2 +- .../Implementation/InfoBar/EditorInfoBarService.cs | 11 ++++------- .../InfoBar/VisualStudioInfoBarService.cs | 10 +++++----- .../VisualStudioErrorReportingServiceFactory.cs | 1 + 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Roslyn.sln b/Roslyn.sln index c865f2080720b..d84b0ca587bd8 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26507.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{A41D1B99-F489-4C43-BBDF-96D61B19A6B9}" EndProject diff --git a/src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs b/src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs index bd8a0567fd41b..f4931a206ae92 100644 --- a/src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs +++ b/src/EditorFeatures/Core/Implementation/InfoBar/EditorInfoBarService.cs @@ -1,22 +1,19 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Composition; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { - [ExportWorkspaceService(typeof(IInfoBarService))] + [ExportWorkspaceService(typeof(IInfoBarService)), Shared] internal class EditorInfoBarService : IInfoBarService { public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items) - { - ShowInfoBarInGlobalView(message, items); - } + => ShowInfoBarInGlobalView(message, items); public void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items) - { - Logger.Log(FunctionId.Extension_InfoBar, message); - } + => Logger.Log(FunctionId.Extension_InfoBar, message); } } diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 65c2f2f3d3b0c..905b9f9d2a2bb 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -using System.ComponentModel.Composition; +using System.Composition; using System.Linq; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -16,15 +16,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation { - [ExportWorkspaceService(typeof(IInfoBarService))] + [ExportWorkspaceService(typeof(IInfoBarService), layer: ServiceLayer.Host), Shared] internal class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject, IInfoBarService { - private readonly IServiceProvider _serviceProvider; + private readonly SVsServiceProvider _serviceProvider; private readonly IForegroundNotificationService _foregroundNotificationService; private readonly IAsynchronousOperationListener _listener; [ImportingConstructor] - public VisualStudioInfoBarService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, + public VisualStudioInfoBarService(SVsServiceProvider serviceProvider, IForegroundNotificationService foregroundNotificationService, [ImportMany] IEnumerable> asyncListeners) { @@ -54,7 +54,7 @@ private void ShowInfoBar(bool activeView, string message, params InfoBarUI[] ite { CreateInfoBar(infoBarHost, message, items); } - }, _listener.BeginAsyncOperation("Show InfoBar")); + }, _listener.BeginAsyncOperation(nameof(ShowInfoBar))); } private bool TryGetInfoBarData(bool activeView, out IVsInfoBarHost infoBarHost) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs index 4fdc31eaf3991..9a4b54adb60a2 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingServiceFactory.cs @@ -23,6 +23,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) { _singleton = new VisualStudioErrorReportingService(workspaceServices.GetRequiredService()); } + return _singleton; } } From b5c01a460a007f284169dab2cc66c5085a2b2f5e Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 9 May 2017 10:45:29 -0700 Subject: [PATCH 099/284] Added post-suggestion listener --- src/EditorFeatures/Core/EditorFeatures.csproj | 1 + .../Suggestions/ISuggestedActionCallback.cs | 13 ++++++ .../SuggestedActions/SuggestedAction.cs | 10 +++-- .../SuggestedActionsSourceProvider.cs | 5 ++- .../AnalyzerVsixSuggestedActionCallback.cs | 45 +++++++++++++++++++ .../Core/Def/ServicesVSResources.Designer.cs | 20 ++++++++- .../Core/Def/ServicesVSResources.resx | 10 ++++- .../Core/Def/ServicesVisualStudio.csproj | 2 + 8 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 src/EditorFeatures/Core/Implementation/Suggestions/ISuggestedActionCallback.cs create mode 100644 src/VisualStudio/Core/Def/Implementation/Experimentation/AnalyzerVsixSuggestedActionCallback.cs diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index c4ca714ba6082..ebf13ede91907 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -166,6 +166,7 @@ + diff --git a/src/EditorFeatures/Core/Implementation/Suggestions/ISuggestedActionCallback.cs b/src/EditorFeatures/Core/Implementation/Suggestions/ISuggestedActionCallback.cs new file mode 100644 index 0000000000000..9c8ab22467d9d --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/Suggestions/ISuggestedActionCallback.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions +{ + internal interface ISuggestedActionCallback + { + void OnSuggestedActionExecuted(SuggestedAction action); + } +} diff --git a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActions/SuggestedAction.cs b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActions/SuggestedAction.cs index 07b363341747d..b2b75345f4a4e 100644 --- a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActions/SuggestedAction.cs @@ -90,21 +90,25 @@ protected Task> GetPreviewOperationsAsync(Ca public void Invoke(CancellationToken cancellationToken) { - // While we're not technically doing anything async here, we need to let the + // While we're not technically doing anything async here, we need to let the // integration test harness know that it should not proceed until all this // work is done. Otherwise it might ask to do some work before we finish. // That can happen because although we're on the UI thread, we may do things // that end up causing VS to pump the messages that the test harness enqueues - // to the UI thread as well. + // to the UI thread as well. using (SourceProvider.OperationListener.BeginAsyncOperation($"{nameof(SuggestedAction)}.{nameof(Invoke)}")) { - // WaitIndicator cannot be used with async/await. Even though we call async methods + // WaitIndicator cannot be used with async/await. Even though we call async methods // later in this call chain, do not await them. SourceProvider.WaitIndicator.Wait(CodeAction.Title, CodeAction.Message, allowCancel: true, showProgress: true, action: waitContext => { using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, waitContext.CancellationToken)) { InnerInvoke(waitContext.ProgressTracker, linkedSource.Token); + foreach (var actionCallback in SourceProvider.ActionCallbacks) + { + actionCallback.Value.OnSuggestedActionExecuted(this); + } } }); } diff --git a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSourceProvider.cs b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSourceProvider.cs index 357f649bb1576..41559e3c9cf1d 100644 --- a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSourceProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSourceProvider.cs @@ -39,6 +39,7 @@ internal partial class SuggestedActionsSourceProvider : ISuggestedActionsSourceP public readonly ICodeActionEditHandlerService EditHandler; public readonly IAsynchronousOperationListener OperationListener; public readonly IWaitIndicator WaitIndicator; + public readonly ImmutableArray> ActionCallbacks; public readonly ImmutableArray> ImageMonikerServices; @@ -50,11 +51,13 @@ public SuggestedActionsSourceProvider( ICodeActionEditHandlerService editHandler, IWaitIndicator waitIndicator, [ImportMany] IEnumerable> asyncListeners, - [ImportMany] IEnumerable> imageMonikerServices) + [ImportMany] IEnumerable> imageMonikerServices, + [ImportMany] IEnumerable> actionCallbacks) { _codeRefactoringService = codeRefactoringService; _diagnosticService = diagnosticService; _codeFixService = codeFixService; + ActionCallbacks = actionCallbacks.ToImmutableArray(); EditHandler = editHandler; WaitIndicator = waitIndicator; OperationListener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.LightBulb); diff --git a/src/VisualStudio/Core/Def/Implementation/Experimentation/AnalyzerVsixSuggestedActionCallback.cs b/src/VisualStudio/Core/Def/Implementation/Experimentation/AnalyzerVsixSuggestedActionCallback.cs new file mode 100644 index 0000000000000..25430d51d5538 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/Experimentation/AnalyzerVsixSuggestedActionCallback.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; +using Microsoft.CodeAnalysis.Experiments; +using Microsoft.CodeAnalysis.Extensions; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Experimentation +{ + [Export(typeof(ISuggestedActionCallback))] + class AnalyzerVsixSuggestedActionCallback : ISuggestedActionCallback + { + private const string AnalyzerEnabledFlight = @"LiveCA/LiveCAcf"; + private const string AnalyzerVsixHyperlink = @"https://aka.ms/livecodeanalysis"; + + private readonly VisualStudioWorkspace _workspace; + private readonly IInfoBarService _infoBarService; + + [ImportingConstructor] + public AnalyzerVsixSuggestedActionCallback(IInfoBarService infoBarService, VisualStudioWorkspace workspace) + { + _workspace = workspace; + _infoBarService = infoBarService; + } + + public void OnSuggestedActionExecuted(SuggestedAction action) + { + var experimentationService = _workspace.Services.GetRequiredService(); + if (experimentationService.IsExperimentEnabled(AnalyzerEnabledFlight)) + { + _infoBarService.ShowInfoBarInGlobalView( + ServicesVSResources.Analyzer_vsix_try_description, + new InfoBarUI(title: ServicesVSResources.Analyzer_vsix_hyperlink, + kind: InfoBarUI.UIKind.HyperLink, + action: new Action(OpenInstallHyperlink))); + } + } + + private void OpenInstallHyperlink() + { + System.Diagnostics.Process.Start(AnalyzerVsixHyperlink); + } + } +} diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs b/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs index f151708ab124c..09fb93e50562c 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs +++ b/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.LanguageServices { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class ServicesVSResources { @@ -226,6 +226,24 @@ internal static string Analyzer_reference_to_0_in_project_1 { } } + /// + /// Looks up a localized string similar to Install. + /// + internal static string Analyzer_vsix_hyperlink { + get { + return ResourceManager.GetString("Analyzer_vsix_hyperlink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Try the new analyzer extension!. + /// + internal static string Analyzer_vsix_try_description { + get { + return ResourceManager.GetString("Analyzer_vsix_try_description", resourceCulture); + } + } + /// /// Looks up a localized string similar to AnalyzerChangedOnDisk. /// diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index e23b643793526..b1042e7f920e0 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -906,10 +906,16 @@ Additional information: {1} prefer throwing properties - + When generating properties: - + Options + + Install + + + Try the new analyzer extension! + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 4c7b48649bd96..ac3b8ac9c1909 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -62,6 +62,7 @@ + @@ -801,6 +802,7 @@ Designer + \ No newline at end of file From a4dbea3760cc7b3f3093957312077876a59959d8 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Wed, 10 May 2017 12:29:24 -0700 Subject: [PATCH 100/284] Use file path to hook up analyzers nodes in Solution Explorer Fixes https://github.com/dotnet/project-system/issues/2062. Currently we match a hierarchy node representing an analyzer in Solution Explorer up to the proper Roslyn `AnalyzerReference` based on the "display name"--basically, the name of the assembly without the extension. This mostly works, but you can run into problems if a project uses two different versions of the same analyzer, each with a different set of diagnostics. In this case expanding one of the analyzer nodes in Solution Explorer will show you the wrong set of diagnostics for one or the other, since they both have the same display name. To uniquely identify an analyzer assembly we should use its full path rather than the display name. However, the project system initially didn't expose the full path through the hierarchy node. This has been fixed, and now the full path is one of the components of the node's "canonical name", along with other bits of information such as the project directory and target framework. This commit updates our code to extract and use the full path instead of the display name. --- .../CpsDiagnosticItemProvider.cs | 8 ++- .../DiagnosticItem/CpsDiagnosticItemSource.cs | 51 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemProvider.cs b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemProvider.cs index c645392f6df45..ce905c3315d24 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemProvider.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemProvider.cs @@ -121,7 +121,13 @@ private IAttachedCollectionSource CreateCollectionSourceCore(IVsHierarchyItem pr { var workspace = TryGetWorkspace(); var analyzerService = GetAnalyzerService(); - return new CpsDiagnosticItemSource(workspace, projectId, item, _commandHandler, analyzerService); + + var hierarchy = projectRootItem.HierarchyIdentity.NestedHierarchy; + var itemId = projectRootItem.HierarchyIdentity.NestedItemID; + if (hierarchy.GetCanonicalName(itemId, out string projectCanonicalName) == VSConstants.S_OK) + { + return new CpsDiagnosticItemSource(workspace, projectCanonicalName, projectId, item, _commandHandler, analyzerService); + } } return null; diff --git a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs index 81713c046cdb1..af77b64267226 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs @@ -1,11 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.ComponentModel; +using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.Internal.VisualStudio.PlatformUI; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer @@ -13,15 +14,17 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplore internal partial class CpsDiagnosticItemSource : BaseDiagnosticItemSource, INotifyPropertyChanged { private readonly IVsHierarchyItem _item; + private readonly string _projectDirectoryPath; private AnalyzerReference _analyzerReference; public event PropertyChangedEventHandler PropertyChanged; - public CpsDiagnosticItemSource(Workspace workspace, ProjectId projectId, IVsHierarchyItem item, IAnalyzersCommandHandler commandHandler, IDiagnosticAnalyzerService analyzerService) + public CpsDiagnosticItemSource(Workspace workspace, string projectPath, ProjectId projectId, IVsHierarchyItem item, IAnalyzersCommandHandler commandHandler, IDiagnosticAnalyzerService analyzerService) : base(workspace, projectId, commandHandler, analyzerService) { _item = item; + _projectDirectoryPath = Path.GetDirectoryName(projectPath); _analyzerReference = TryGetAnalyzerReference(_workspace.CurrentSolution); if (_analyzerReference == null) @@ -91,12 +94,50 @@ private AnalyzerReference TryGetAnalyzerReference(Solution solution) return null; } - if (!_item.HierarchyIdentity.NestedHierarchy.TryGetItemName(_item.HierarchyIdentity.NestedItemID, out string name)) + var canonicalName = _item.CanonicalName; + var analyzerFilePath = ExtractAnalyzerFilePath(canonicalName); + + return project.AnalyzerReferences.FirstOrDefault(r => r.FullPath.Equals(analyzerFilePath, StringComparison.OrdinalIgnoreCase)); + } + + /// + /// Given the canonical name of a node representing an analyzer assembly in the + /// CPS-based project system extracts out the full path to the assembly. + /// + /// + /// The canonical name takes the following form: + /// + /// [{path to project directory}\]{target framework}\analyzerdependency\{path to assembly} + /// + /// e.g.: + /// + /// C:\projects\solutions\MyProj\netstandard2.0\analyzerdependency\C:\users\me\.packages\somePackage\lib\someAnalyzer.dll + /// + /// This method exists solely to extract out the "path to assembly" part, i.e. + /// "C:\users\me\.packages\somePackage\lib\someAnalyzer.dll". We don't need the + /// other parts. + /// + /// Note that the path to the project directory is optional. It's not clear if + /// this is intentional or a bug in the project system, but either way it + /// doesn't really matter. + /// + private string ExtractAnalyzerFilePath(string canonicalName) + { + // The canonical name may or may not start with the path to the project's directory. + if (canonicalName.StartsWith(_projectDirectoryPath, StringComparison.OrdinalIgnoreCase)) { - return null; + // Extract the rest of the string, taking into account the "\" separating the directory + // path from the rest of the canonical name + canonicalName = canonicalName.Substring(_projectDirectoryPath.Length + 1); } - return project.AnalyzerReferences.FirstOrDefault(r => r.Display.Equals(name)); + // Find the slash after the target framework + var backslashIndex = canonicalName.IndexOf('\\'); + // Find the slash after "analyzerdependency" + backslashIndex = canonicalName.IndexOf('\\', backslashIndex + 1); + + // The rest of the string is the path. + return canonicalName.Substring(backslashIndex + 1); } } } \ No newline at end of file From 4a1b805ff47f6e25cd9ebb3cb4c81d35c25f36ce Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 12:30:40 -0700 Subject: [PATCH 101/284] Simplify check. --- ...actUseThrowExpressionDiagnosticAnalyzer.cs | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs b/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs index a0c92f5d6c605..e405a6e5757c0 100644 --- a/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs @@ -116,12 +116,6 @@ private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol return; } - if (!IsOnlyStatementOfIf(ifOperation, throwOperation)) - { - // The if-statement can only have a single throw-statement in it. - return; - } - var containingBlock = GetOperation( semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement; if (containingBlock == null) @@ -195,18 +189,6 @@ private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol } } - private bool IsOnlyStatementOfIf(IIfStatement ifOperation, IThrowStatement throwStatement) - { - if (ifOperation.IfTrueStatement.Syntax == throwStatement.Syntax) - { - return true; - } - - return ifOperation.IfTrueStatement is IBlockStatement block && - block.Statements.Length == 1 && - block.Statements[0].Syntax == throwStatement.Syntax; - } - protected abstract ISyntaxFactsService GetSyntaxFactsService(); protected abstract ISemanticFactsService GetSemanticFactsService(); @@ -318,8 +300,15 @@ private IIfStatement GetContainingIfOperation( var containingOperation = GetOperation( semanticModel, throwStatement.Parent, cancellationToken); - if (containingOperation?.Kind == OperationKind.BlockStatement) + if (containingOperation is IBlockStatement block) { + if (block.Statements.Length != 1) + { + // If we are in a block, then the block must only contain + // the throw statement. + return null; + } + // C# may have an intermediary block between the throw-statement // and the if-statement. Walk up one operation higher in that case. containingOperation = GetOperation( From 7c5039453c6af7dd78d47fb372da402a083f73bb Mon Sep 17 00:00:00 2001 From: Ivan Basov Date: Wed, 10 May 2017 13:43:49 -0700 Subject: [PATCH 102/284] Migrate BasicEditAndContinue integration tests from Tao to .NET (#19381) Tests only. No ask mode approval is required. --- .../VisualBasic/BasicEditAndContinue.cs | 326 ++++++++++++++++++ .../VisualStudioIntegrationTests.csproj | 1 + .../TestUtilities/Common/Expression.cs | 23 ++ .../InProcess/Debugger_InProc.cs | 37 ++ .../TestUtilities/InProcess/Editor_InProc.cs | 39 +++ .../InProcess/LocalsWindow_InProc.cs | 30 ++ .../InProcess/VisualStudio_InProc.cs | 4 +- .../TestUtilities/IntegrationHelper.cs | 10 +- .../OutOfProcess/Debugger_OutOfProc.cs | 51 +++ .../OutOfProcess/Dialog_OutOfProc.cs | 30 ++ .../OutOfProcess/Editor_OutOfProc.Verifier.cs | 5 + .../OutOfProcess/Editor_OutOfProc.cs | 10 + .../ErrorList_OutOfProc.Verifier.cs | 5 + .../LocalsWindow_OutOfProc.Verifier.cs | 26 ++ .../OutOfProcess/LocalsWindow_OutOfProc.cs | 19 + .../TestUtilities/VisualStudioInstance.cs | 13 +- ...isualStudioIntegrationTestUtilities.csproj | 7 + 17 files changed, 629 insertions(+), 7 deletions(-) create mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs new file mode 100644 index 0000000000000..7643daa7d20bd --- /dev/null +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs @@ -0,0 +1,326 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; +using Xunit; +using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; + +namespace Roslyn.VisualStudio.IntegrationTests.VisualBasic +{ + [Collection(nameof(SharedIntegrationHostFixture))] + public class BasicEditAndContinue : AbstractEditorTest + { + private const string module1FileName = "Module1.vb"; + + public BasicEditAndContinue(VisualStudioInstanceFactory instanceFactory) : base(instanceFactory) + { + VisualStudio.SolutionExplorer.CreateSolution(nameof(BasicBuild)); + var testProj = new ProjectUtils.Project("TestProj"); + VisualStudio.SolutionExplorer.AddProject(testProj, WellKnownProjectTemplates.ConsoleApplication, LanguageNames.VisualBasic); + } + + protected override string LanguageName => LanguageNames.VisualBasic; + + [Fact] + public void UpdateActiveStatementLeafNode() + { + VisualStudio.Editor.SetText(@" +Imports System +Imports System.Collections.Generic +Imports System.Linq + +Module Module1 + Sub Main() + Dim names(2) As String + names(0) = ""foo"" + names(1) = ""bar"" + + For index = 0 To names.GetUpperBound(0) + Console.WriteLine(names(index)) + Next + End Sub +End Module +"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "names(0)"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("names(0)", "names(1)"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.CheckExpression("names(1)", "String", "\"foo\""); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.CheckExpression("names(1)", "String", "\"bar\""); + } + + [Fact] + public void AddTryCatchAroundActiveStatement() + { + VisualStudio.Editor.SetText(@" +Imports System +Module Module1 + Sub Main() + Foo() + End Sub + + Private Sub Foo() + Console.WriteLine(1) + End Sub +End Module"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Console.WriteLine(1)"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("Console.WriteLine(1)", + @"Try +Console.WriteLine(1) +Catch ex As Exception +End Try"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Editor.Verify.CurrentLineText("End Try"); + } + + [Fact] + public void EditLambdaExpression() + { + VisualStudio.Editor.SetText(@" +Imports System +Module Module1 + Private Delegate Function del(i As Integer) As Integer + + Sub Main() + Dim myDel As del = Function(x) x * x + Dim j As Integer = myDel(5) + End Sub +End Module"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "x * x", charsOffset: -1); + + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("x * x", "x * 2"); + + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: false); + VisualStudio.Debugger.Stop(waitForDesignMode: true); + VisualStudio.ErrorList.Verify.NoBuildErrors(); + + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("x * 2", "x * x"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.Stop(waitForDesignMode: true); + VisualStudio.ErrorList.Verify.NoBuildErrors(); + } + + [Fact] + public void EnCWhileDebuggingFromImmediateWindow() + { + VisualStudio.Editor.SetText(@" +Imports System + +Module Module1 + Sub Main() + Dim x = 4 + Console.WriteLine(x) + End Sub +End Module"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Dim x", charsOffset: 1); + VisualStudio.Debugger.ExecuteStatement("Module1.Main()"); + VisualStudio.Editor.ReplaceText("x = 4", "x = 42"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.CheckExpression("x", "Integer", "42"); + VisualStudio.Debugger.ExecuteStatement("Module1.Main()"); + } + + private void SetupMultiProjectSolution() + { + var basicLibrary = new ProjectUtils.Project("BasicLibrary1"); + VisualStudio.SolutionExplorer.AddProject(basicLibrary, WellKnownProjectTemplates.ClassLibrary, LanguageNames.VisualBasic); + + var cSharpLibrary = new ProjectUtils.Project("CSharpLibrary1"); + VisualStudio.SolutionExplorer.AddProject(cSharpLibrary, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp); + VisualStudio.SolutionExplorer.AddFile(cSharpLibrary, "File1.cs"); + + VisualStudio.SolutionExplorer.OpenFile(basicLibrary, "Class1.vb"); + VisualStudio.Editor.SetText(@" +Imports System +Public Class Class1 + Public Sub New() + End Sub + + Public Sub PrintX(x As Integer) + Console.WriteLine(x) + End Sub +End Class +"); + + var project = new ProjectUtils.Project(ProjectName); + VisualStudio.SolutionExplorer.AddProjectReference(project, new ProjectUtils.ProjectReference("BasicLibrary1")); + VisualStudio.SolutionExplorer.OpenFile(project, module1FileName); + + VisualStudio.Editor.SetText(@" +Imports System +Imports BasicLibrary1 + +Module Module1 + Sub Main() + Dim c As New Class1() + c.PrintX(5) + End Sub +End Module +"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + } + + [Fact] + public void MultiProjectDebuggingWhereNotAllModulesAreLoaded() + { + SetupMultiProjectSolution(); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "PrintX", charsOffset: 1); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("5", "42"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: false); + VisualStudio.ErrorList.Verify.NoErrors(); + } + + [Fact] + public void DocumentStateTrackingReadonlyInRunMode() + { + SetupMultiProjectSolution(); + var project = new ProjectUtils.Project(ProjectName); + var basicLibrary = new ProjectUtils.Project("BasicLibrary1"); + var cSharpLibrary = new ProjectUtils.Project("CSharpLibrary1"); + + VisualStudio.Editor.SetText(@" +Imports System +Imports BasicLibrary1 +Module Module1 + Sub Main() + Console.Read() + End Sub +End Module +"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.Go(waitForBreakMode: false); + VisualStudio.ActivateMainWindow(skipAttachingThreads: true); + VisualStudio.SolutionExplorer.OpenFile(project, module1FileName); + + VisualStudio.SendKeys.Send(VirtualKey.T); + string editAndContinueDialogName = "Edit and Continue"; + VisualStudio.Dialog.VerifyOpen(editAndContinueDialogName); + VisualStudio.Dialog.Click(editAndContinueDialogName, "OK"); + VisualStudio.Dialog.VerifyClosed(editAndContinueDialogName); + VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false); + + // This module is referred by the loaded module, but not used. So this will not be loaded + VisualStudio.SolutionExplorer.OpenFile(basicLibrary, "Class1.vb"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.SendKeys.Send(VirtualKey.T); + VisualStudio.Dialog.VerifyOpen(editAndContinueDialogName); + VisualStudio.Dialog.Click(editAndContinueDialogName, "OK"); + VisualStudio.Dialog.VerifyClosed(editAndContinueDialogName); + VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false); + + // This module is not referred by the loaded module. this will not be loaded + VisualStudio.SolutionExplorer.OpenFile(cSharpLibrary, "File1.cs"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.SendKeys.Send(VirtualKey.T); + + string microsoftVisualStudionDialogName = "Microsoft Visual Studio"; + VisualStudio.Dialog.VerifyOpen(microsoftVisualStudionDialogName); + VisualStudio.Dialog.Click(microsoftVisualStudionDialogName, "OK"); + VisualStudio.Dialog.VerifyClosed(microsoftVisualStudionDialogName); + VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false); + } + + [Fact] + public void LocalsWindowUpdatesAfterLocalGetsItsTypeUpdatedDuringEnC() + { + VisualStudio.Editor.SetText(@" +Imports System +Module Module1 + Sub Main() + Dim foo As String = ""abc"" + Console.WriteLine(foo) + End Sub +End Module +"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "End Sub"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("Dim foo As String = \"abc\"", "Dim foo As Single = 10"); + VisualStudio.Editor.SelectTextInCurrentDocument("Sub Main()"); + VisualStudio.Debugger.SetNextStatement(); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.LocalsWindow.Verify.CheckEntry("foo", "Single", "10"); + } + + [Fact] + public void LocalsWindowUpdatesCorrectlyDuringEnC() + { + VisualStudio.Editor.SetText(@" +Imports System + +Module Module1 + Sub Main() + bar(5) + End Sub + + Function bar(ByVal moo As Long) As Decimal + Dim iInt As Integer = 0 + Dim lLng As Long = 5 + + iInt += 30 + Return 4 + End Function +End Module +"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Function bar(ByVal moo As Long) As Decimal"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("Dim lLng As Long = 5", "Dim lLng As Long = 444"); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Return 4"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.LocalsWindow.Verify.CheckEntry("bar", "Decimal", "0"); + VisualStudio.LocalsWindow.Verify.CheckEntry("moo", "Long", "5"); + VisualStudio.LocalsWindow.Verify.CheckEntry("iInt", "Integer", "30"); + VisualStudio.LocalsWindow.Verify.CheckEntry("lLng", "Long", "444"); + } + + [Fact] + public void WatchWindowUpdatesCorrectlyDuringEnC() + { + VisualStudio.Editor.SetText(@" +Imports System + +Module Module1 + Sub Main() + Dim iInt As Integer = 0 + System.Diagnostics.Debugger.Break() + End Sub +End Module +"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.Debugger.CheckExpression("iInt", "Integer", "0"); + + VisualStudio.Editor.ReplaceText("System.Diagnostics.Debugger.Break()", @"iInt = 5 +System.Diagnostics.Debugger.Break()"); + + VisualStudio.Editor.SelectTextInCurrentDocument("iInt = 5"); + VisualStudio.Debugger.SetNextStatement(); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.Debugger.CheckExpression("iInt", "Integer", "5"); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj index 94fe76840afc8..0529e228bc986 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj @@ -64,6 +64,7 @@ + diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs b/src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs new file mode 100644 index 0000000000000..30e6235a3872a --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.Common +{ + [Serializable] + public class Expression + { + public string Name { get; set; } + + public string Type { get; set; } + + public string Value { get; set; } + + public Expression(EnvDTE.Expression input) + { + Name = input.Name; + Type = input.Type; + Value = input.Value; + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs new file mode 100644 index 0000000000000..ccbda1f1747a1 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using EnvDTE; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess +{ + internal class Debugger_InProc : InProcComponent + { + private readonly Debugger _debugger; + + private Debugger_InProc() + { + _debugger = GetDTE().Debugger; + } + + public static Debugger_InProc Create() + => new Debugger_InProc(); + + public void SetBreakPoint(string fileName, int lineNumber, int columnIndex) + { + // Need to increment the line number because editor line numbers starts from 0 but the debugger ones starts from 1. + _debugger.Breakpoints.Add(File: fileName, Line: lineNumber + 1, Column: columnIndex); + } + + public void Go(bool waitForBreakMode) => _debugger.Go(waitForBreakMode); + + public void StepOver(bool waitForBreakOrEnd) => _debugger.StepOver(waitForBreakOrEnd); + + public void Stop(bool waitForDesignMode) => _debugger.Stop(WaitForDesignMode: waitForDesignMode); + + public void SetNextStatement() => _debugger.SetNextStatement(); + + public void ExecuteStatement(string statement) => _debugger.ExecuteStatement(statement); + + public Common.Expression GetExpression(string expressionText) => new Common.Expression(_debugger.GetExpression(expressionText)); + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index a9337b07b26ba..8b57ac5cd43fd 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -78,6 +78,9 @@ public void WaitForActiveView(string expectedView) public void Activate() => GetDTE().ActiveDocument.Activate(); + public bool IsProjectItemDirty() + => GetDTE().ActiveDocument.ProjectItem.IsDirty; + public string GetText() => ExecuteOnActiveView(view => view.TextSnapshot.GetText()); @@ -89,6 +92,21 @@ public void SetText(string text) view.TextBuffer.Replace(replacementSpan, text); }); + public void SelectText(string text) + { + PlaceCaret(text, charsOffset: -1, occurrence: 0, extendSelection: false, selectBlock: false); + PlaceCaret(text, charsOffset: 0, occurrence: 0, extendSelection: true, selectBlock: false); + } + + public void ReplaceText(string oldText, string newText) + => ExecuteOnActiveView(view => + { + var textSnapshot = view.TextSnapshot; + SelectText(oldText); + var replacementSpan = new SnapshotSpan(textSnapshot, view.Selection.Start.Position, view.Selection.End.Position - view.Selection.Start.Position); + view.TextBuffer.Replace(replacementSpan, newText); + }); + public string GetCurrentLineText() => ExecuteOnActiveView(view => { @@ -99,6 +117,20 @@ public string GetCurrentLineText() return line.GetText(); }); + public int GetLine() + => ExecuteOnActiveView(view => + { + view.Caret.Position.BufferPosition.GetLineAndColumn(out int lineNumber, out int columnIndex); + return lineNumber; + }); + + public int GetColumn() + => ExecuteOnActiveView(view => + { + view.Caret.Position.BufferPosition.GetLineAndColumn(out int lineNumber, out int columnIndex); + return columnIndex; + }); + public string GetLineTextBeforeCaret() => ExecuteOnActiveView(view => { @@ -670,5 +702,12 @@ public int[] GetTagSpans(string tagId) return matchingTags.Select(t => t.Span.GetSpans(view.TextBuffer).Single().Span.ToTextSpan()).SelectMany(t => new List { t.Start, t.Length }).ToArray(); }); + + public void SendExplicitFocus() + => InvokeOnUIThread(() => + { + var view = GetActiveVsTextView(); + view.SendExplicitFocus(); + }); } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs new file mode 100644 index 0000000000000..dc952b1ede9f5 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using EnvDTE80; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess +{ + internal class LocalsWindow_InProc : InProcComponent + { + public static LocalsWindow_InProc Create() => new LocalsWindow_InProc(); + + public Common.Expression GetEntry(string entryName) + { + var dte = ((DTE2)GetDTE()); + if (dte.Debugger.CurrentStackFrame != null) // Ensure that debugger is running + { + EnvDTE.Expressions locals = dte.Debugger.CurrentStackFrame.Locals; + foreach (EnvDTE.Expression local in locals) + { + if (local.Name == entryName) + { + return new Common.Expression(local); + } + } + } + + throw new Exception($"Could not find the local named {entryName}."); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs index 58d6202387d01..3066e17b38ca9 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs @@ -47,7 +47,7 @@ public string[] GetAvailableCommands() return result.ToArray(); } - public void ActivateMainWindow() + public void ActivateMainWindow(bool skipAttachingThreads = false) => InvokeOnUIThread(() => { var dte = GetDTE(); @@ -60,7 +60,7 @@ public void ActivateMainWindow() Debug.WriteLine($"DTE.MainWindow.HWnd = {activeVisualStudioWindow}"); } - IntegrationHelper.SetForegroundWindow(activeVisualStudioWindow); + IntegrationHelper.SetForegroundWindow(activeVisualStudioWindow, skipAttachingThreads); }); public int GetErrorListErrorCount() diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs b/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs index 5aeb931844402..30aca200a733d 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs @@ -217,7 +217,7 @@ public static void KillProcess(string processName) } } - public static void SetForegroundWindow(IntPtr window) + public static void SetForegroundWindow(IntPtr window, bool skipAttachingThread = false) { var foregroundWindow = GetForegroundWindow(); @@ -233,8 +233,12 @@ public static void SetForegroundWindow(IntPtr window) try { - // Attach the thread inputs so that 'SetActiveWindow' and 'SetFocus' work - threadInputsAttached = AttachThreadInput(currentThreadId, activeThreadId); + // No need to re-attach threads in case when VS initializaed an UI thread for a debugged application. + if (!skipAttachingThread) + { + // Attach the thread inputs so that 'SetActiveWindow' and 'SetFocus' work + threadInputsAttached = AttachThreadInput(currentThreadId, activeThreadId); + } // Make the window a top-most window so it will appear above any existing top-most windows NativeMethods.SetWindowPos(window, (IntPtr)NativeMethods.HWND_TOPMOST, 0, 0, 0, 0, (NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE)); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs new file mode 100644 index 0000000000000..91987fb488043 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess; +using Xunit; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + /// + /// Provides a means of interacting with the Visual Studio debugger by remoting calls into Visual Studio. + /// + public partial class Debugger_OutOfProc : OutOfProcComponent + { + private readonly Debugger_InProc _debuggerInProc; + private readonly VisualStudioInstance _instance; + + public Debugger_OutOfProc(VisualStudioInstance visualStudioInstance) : base(visualStudioInstance) + { + _instance = visualStudioInstance; + _debuggerInProc = CreateInProcComponent(visualStudioInstance); + } + + public void SetBreakPoint(string fileName, int lineNumber, int columnIndex) => + _debuggerInProc.SetBreakPoint(fileName, lineNumber, columnIndex); + + public void SetBreakPoint(string fileName, string text, int charsOffset = 0) + { + _instance.Editor.SelectTextInCurrentDocument(text); + int lineNumber = _instance.Editor.GetLine(); + int columnIndex = _instance.Editor.GetColumn(); + + SetBreakPoint(fileName, lineNumber, columnIndex + charsOffset); + } + + public void Go(bool waitForBreakMode) => _debuggerInProc.Go(waitForBreakMode); + + public void StepOver(bool waitForBreakOrEnd) => _debuggerInProc.StepOver(waitForBreakOrEnd); + + public void Stop(bool waitForDesignMode) => _debuggerInProc.Stop(waitForDesignMode); + + public void SetNextStatement() => _debuggerInProc.SetNextStatement(); + + public void ExecuteStatement(string statement) => _debuggerInProc.ExecuteStatement(statement); + + public void CheckExpression(string expressionText, string expectedType, string expectedValue) + { + var entry = _debuggerInProc.GetExpression(expressionText); + Assert.Equal(expectedType, entry.Type); + Assert.Equal(expectedValue, entry.Value); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs new file mode 100644 index 0000000000000..bf3c528a6c01e --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + public class Dialog_OutOfProc : OutOfProcComponent + { + public Dialog_OutOfProc(VisualStudioInstance visualStudioInstance) + : base(visualStudioInstance) + { + } + + public void VerifyOpen(string dialogName) + { + // FindDialog will wait until the dialog is open, so the return value is unused. + DialogHelpers.FindDialogByName(GetMainWindowHWnd(), dialogName, isOpen: true); + } + + public void VerifyClosed(string dialogName) + { + // FindDialog will wait until the dialog is closed, so the return value is unused. + DialogHelpers.FindDialogByName(GetMainWindowHWnd(), dialogName, isOpen: false); + } + + public void Click(string dialogName, string buttonName) + => DialogHelpers.PressButtonWithNameFromDialogWithName(GetMainWindowHWnd(), dialogName, buttonName); + + private int GetMainWindowHWnd() + => VisualStudioInstance.Shell.GetHWnd(); + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs index 72bc88f457edd..3f98699bb715a 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs @@ -194,6 +194,11 @@ public void ErrorTags(params string[] expectedTags) var actualTags = _textViewWindow.GetErrorTags(); Assert.Equal(expectedTags, actualTags); } + + public void IsProjectItemDirty(bool expectedValue) + { + Assert.Equal(expectedValue, _textViewWindow._editorInProc.IsProjectItemDirty()); + } } } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index 0b68f0c090d8c..a96117770d89f 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -127,12 +127,19 @@ public void SelectTextInCurrentDocument(string text) PlaceCaret(text, charsOffset: 0, occurrence: 0, extendSelection: true, selectBlock: false); } + public int GetLine() => _editorInProc.GetLine(); + + public int GetColumn() => _editorInProc.GetColumn(); + public void DeleteText(string text) { SelectTextInCurrentDocument(text); SendKeys(VirtualKey.Delete); } + public void ReplaceText(string oldText, string newText) + => _editorInProc.ReplaceText(oldText, newText); + public bool IsCaretOnScreen() => _editorInProc.IsCaretOnScreen(); @@ -324,5 +331,8 @@ public void GoToDefinition() public void GoToImplementation() => _editorInProc.GoToImplementation(); + + public void SendExplicitFocus() + => _editorInProc.SendExplicitFocus(); } } \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs index 6159b9d754672..b3598f2cc61bf 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs @@ -21,6 +21,11 @@ public Verifier(ErrorList_OutOfProc errorList, VisualStudioInstance instance) public void NoBuildErrors() { _instance.SolutionExplorer.BuildSolution(waitForBuildToFinish: true); + NoErrors(); + } + + public void NoErrors() + { Assert.Equal(0, _errorList.GetErrorListErrorCount()); } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs new file mode 100644 index 0000000000000..43b3512651eff --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + public partial class LocalsWindow_OutOfProc + { + public class Verifier + { + private readonly LocalsWindow_OutOfProc _localsWindow; + + public Verifier(LocalsWindow_OutOfProc localsWindow) + { + _localsWindow = localsWindow; + } + + public void CheckEntry(string entryName, string expectedType, string expectedValue) + { + var entry =_localsWindow._localsWindowInProc.GetEntry(entryName); + Assert.Equal(expectedType, entry.Type); + Assert.Equal(expectedValue, entry.Value); + } + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs new file mode 100644 index 0000000000000..f9bf5ded7f801 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + public partial class LocalsWindow_OutOfProc : OutOfProcComponent + { + public Verifier Verify { get; } + + private readonly LocalsWindow_InProc _localsWindowInProc; + + public LocalsWindow_OutOfProc(VisualStudioInstance visualStudioInstance) : base(visualStudioInstance) + { + _localsWindowInProc = CreateInProcComponent(visualStudioInstance); + Verify = new Verifier(this); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs index 3c0ca3f7d88c5..c06bb58536ecf 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs @@ -25,6 +25,10 @@ public class VisualStudioInstance public CSharpInteractiveWindow_OutOfProc InteractiveWindow { get; } + public Debugger_OutOfProc Debugger { get; } + + public Dialog_OutOfProc Dialog { get; } + public Editor_OutOfProc Editor { get; } public EncapsulateField_OutOfProc EncapsulateField { get; } @@ -39,6 +43,8 @@ public class VisualStudioInstance public InlineRenameDialog_OutOfProc InlineRenameDialog { get; set; } + public LocalsWindow_OutOfProc LocalsWindow { get; set; } + public PreviewChangesDialog_OutOfProc PreviewChangesDialog { get; } public SendKeys SendKeys { get; } @@ -91,6 +97,8 @@ public VisualStudioInstance(Process hostProcess, DTE dte, ImmutableHashSet(Type type, string methodName) return (T)Activator.GetObject(typeof(T), $"{_integrationService.BaseUri}/{objectUri}"); } - public void ActivateMainWindow() - => _inProc.ActivateMainWindow(); + public void ActivateMainWindow(bool skipAttachingThreads = false) + => _inProc.ActivateMainWindow(skipAttachingThreads); public void WaitForApplicationIdle() => _inProc.WaitForApplicationIdle(); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj index 14769b8476d50..d621c7c85d0ac 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj @@ -28,15 +28,18 @@ + + + @@ -54,11 +57,15 @@ + + + + From 6a3f93458b173804209dc773283cc76d74907b0b Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 10 May 2017 13:56:11 -0700 Subject: [PATCH 103/284] added more comments --- .../Core/Portable/Execution/PinnedRemotableDataScope.cs | 3 +++ .../Remote/ServiceHub/Shared/ServiceHubServiceBase.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs index f09b9ada783c5..42504565df844 100644 --- a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs +++ b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs @@ -35,6 +35,9 @@ public PinnedRemotableDataScope( /// /// This indicates whether this scope is for primary branch or not (not forked solution) + /// + /// Features like OOP will use this flag to see whether caching information related to this solution + /// can benefit other requests or not /// public bool ForPrimaryBranch => _storage.SolutionState.BranchId == Workspace.PrimaryBranchId; diff --git a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs index a02a3ca22f613..9fe7c75f4e5a4 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs @@ -34,6 +34,9 @@ internal abstract class ServiceHubServiceBase : IDisposable /// /// Mark whether the solution checksum it got is for primary branch or not + /// + /// this flag will be passed down to solution controller to help + /// solution service's cache policy. for more detail, see /// private bool _fromPrimaryBranch; From 11f3f8abdf68ae34dd3a2350807367cf0223291b Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 10 May 2017 14:00:19 -0700 Subject: [PATCH 104/284] add assert --- .../Core/Portable/Execution/PinnedRemotableDataScope.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs index 42504565df844..995c8c30e25aa 100644 --- a/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs +++ b/src/Workspaces/Core/Portable/Execution/PinnedRemotableDataScope.cs @@ -25,6 +25,8 @@ public PinnedRemotableDataScope( AssetStorages.Storage storage, Checksum solutionChecksum) { + Contract.ThrowIfNull(solutionChecksum); + _storages = storages; _storage = storage; From 934fbe934b4833e8cf991a3fdfdd3170fc0313a1 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 14:21:16 -0700 Subject: [PATCH 105/284] Createing indices for metadata can fail. Be resilient to that. --- ...ymbolTreeInfoIncrementalAnalyzerProvider.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 6b961a156ffdf..50a4a27e38837 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Composition; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -42,7 +43,7 @@ private struct MetadataInfo /// Note: can be null if were unable to create a SymbolTreeInfo /// (for example, if the metadata was bogus and we couldn't read it in). /// - public readonly SymbolTreeInfo SymbolTreeInfo; + public readonly SymbolTreeInfo SymbolTreeInfoOpt; /// /// Note: the Incremental-Analyzer infrastructure guarantees that it will call all the methods @@ -53,7 +54,7 @@ private struct MetadataInfo public MetadataInfo(SymbolTreeInfo info, HashSet referencingProjects) { - SymbolTreeInfo = info; + SymbolTreeInfoOpt = info; ReferencingProjects = referencingProjects; } } @@ -114,9 +115,9 @@ public async Task TryGetMetadataSymbolTreeInfoAsync( if (key != null) { if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) && - metadataInfo.SymbolTreeInfo.Checksum == checksum) + metadataInfo.SymbolTreeInfoOpt?.Checksum == checksum) { - return metadataInfo.SymbolTreeInfo; + return metadataInfo.SymbolTreeInfoOpt; } } @@ -194,11 +195,6 @@ private async Task UpdateSymbolTreeInfoAsync(Project project, CancellationToken return; } - if (!project.SupportsCompilation) - { - return; - } - // Produce the indices for the source and metadata symbols in parallel. var projectTask = UpdateSourceSymbolTreeInfoAsync(project, cancellationToken); var referencesTask = UpdateReferencesAync(project, cancellationToken); @@ -215,6 +211,8 @@ private async Task UpdateSourceSymbolTreeInfoAsync(Project project, Cancellation projectInfo = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( project, checksum, cancellationToken).ConfigureAwait(false); + Debug.Assert(projectInfo != null); + // Mark that we're up to date with this project. Future calls with the same // semantic version can bail out immediately. _projectToInfo.AddOrUpdate(project.Id, projectInfo, (_1, _2) => projectInfo); @@ -242,7 +240,7 @@ private async Task UpdateReferenceAsync( var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken); if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || - metadataInfo.SymbolTreeInfo.Checksum != checksum) + metadataInfo.SymbolTreeInfoOpt?.Checksum != checksum) { var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( project.Solution, reference, checksum, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); From 737d993c1e67d42e9b86e9cd4e6f5a7d0bbe8a7e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 14:28:34 -0700 Subject: [PATCH 106/284] Store a copy of the checksum as we may have failed to create an index for a piece of metadata. --- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 50a4a27e38837..53fe078fd37f8 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -39,6 +39,15 @@ internal class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerP { private struct MetadataInfo { + /// + /// The metadata checksum that we calcuated when we computed this MetadataInfo. + /// Note: this should be the same as . + /// *when* is not null. We have to store this + /// in ourself so that we don't keep trying to recompute the index for a piece + /// of metadata that we failed to produce a for. + /// + public readonly Checksum Checksum; + /// /// Note: can be null if were unable to create a SymbolTreeInfo /// (for example, if the metadata was bogus and we couldn't read it in). @@ -52,8 +61,13 @@ private struct MetadataInfo /// public readonly HashSet ReferencingProjects; - public MetadataInfo(SymbolTreeInfo info, HashSet referencingProjects) + public MetadataInfo(Checksum checksum, SymbolTreeInfo info, HashSet referencingProjects) { + Contract.ThrowIfNull(checksum); + Contract.ThrowIfTrue(info != null && info.Checksum != checksum, + "If we computed a SymbolTreeInfo, then it's checksum much match our checksum."); + + Checksum = checksum; SymbolTreeInfoOpt = info; ReferencingProjects = referencingProjects; } @@ -115,7 +129,7 @@ public async Task TryGetMetadataSymbolTreeInfoAsync( if (key != null) { if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) && - metadataInfo.SymbolTreeInfoOpt?.Checksum == checksum) + metadataInfo.Checksum == checksum) { return metadataInfo.SymbolTreeInfoOpt; } @@ -211,7 +225,8 @@ private async Task UpdateSourceSymbolTreeInfoAsync(Project project, Cancellation projectInfo = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( project, checksum, cancellationToken).ConfigureAwait(false); - Debug.Assert(projectInfo != null); + Contract.ThrowIfNull(projectInfo); + Contract.ThrowIfTrue(projectInfo.Checksum != checksum, "If we computed a SymbolTreeInfo, then it's checksum much match our checksum."); // Mark that we're up to date with this project. Future calls with the same // semantic version can bail out immediately. @@ -240,7 +255,7 @@ private async Task UpdateReferenceAsync( var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken); if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || - metadataInfo.SymbolTreeInfoOpt?.Checksum != checksum) + metadataInfo.Checksum != checksum) { var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( project.Solution, reference, checksum, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -248,7 +263,7 @@ private async Task UpdateReferenceAsync( // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. - metadataInfo = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet()); + metadataInfo = new MetadataInfo(checksum, info, metadataInfo.ReferencingProjects ?? new HashSet()); _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo); } From 174035e6e9774b024bb07b6a67ee1298e83948cc Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 10 May 2017 14:56:17 -0700 Subject: [PATCH 107/284] fix build break. --- src/Workspaces/Remote/Core/Services/SolutionCreator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Remote/Core/Services/SolutionCreator.cs b/src/Workspaces/Remote/Core/Services/SolutionCreator.cs index a785663ffafeb..2ea7c774bd1d3 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionCreator.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionCreator.cs @@ -616,7 +616,7 @@ private ImmutableArray GetStrongNameKeyPaths(ProjectInfo.ProjectAttribut private async Task ValidateChecksumAsync(Checksum givenSolutionChecksum, Solution solution) { // have this to avoid error on async - await SpecializedTasks.EmptyTask; + await SpecializedTasks.EmptyTask.ConfigureAwait(false); #if DEBUG var currentSolutionChecksum = await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false); From a785172867b340542dfc7162564d1dce3c7f3b07 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:00:28 -0700 Subject: [PATCH 108/284] Take an approach where we cannot get Null Symbol Tree infos. --- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 40 +++++++------------ .../Declarations/DeclarationFinder.cs | 10 ++--- .../FindReferences/DependentTypeFinder.cs | 6 +-- .../FindSymbols/SymbolTree/SymbolTreeInfo.cs | 13 +++++- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 27 ++++++------- .../SymbolTreeInfo_Serialization.cs | 21 +++++----- .../SymbolTree/SymbolTreeInfo_Source.cs | 10 ++--- 7 files changed, 59 insertions(+), 68 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 53fe078fd37f8..cdf69e1500b48 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -40,19 +40,10 @@ internal class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerP private struct MetadataInfo { /// - /// The metadata checksum that we calcuated when we computed this MetadataInfo. - /// Note: this should be the same as . - /// *when* is not null. We have to store this - /// in ourself so that we don't keep trying to recompute the index for a piece - /// of metadata that we failed to produce a for. + /// Can't be null. Even if we weren't able to read in metadata, we'll still create an empty + /// index. /// - public readonly Checksum Checksum; - - /// - /// Note: can be null if were unable to create a SymbolTreeInfo - /// (for example, if the metadata was bogus and we couldn't read it in). - /// - public readonly SymbolTreeInfo SymbolTreeInfoOpt; + public readonly SymbolTreeInfo SymbolTreeInfo; /// /// Note: the Incremental-Analyzer infrastructure guarantees that it will call all the methods @@ -61,14 +52,10 @@ private struct MetadataInfo /// public readonly HashSet ReferencingProjects; - public MetadataInfo(Checksum checksum, SymbolTreeInfo info, HashSet referencingProjects) + public MetadataInfo(SymbolTreeInfo info, HashSet referencingProjects) { - Contract.ThrowIfNull(checksum); - Contract.ThrowIfTrue(info != null && info.Checksum != checksum, - "If we computed a SymbolTreeInfo, then it's checksum much match our checksum."); - - Checksum = checksum; - SymbolTreeInfoOpt = info; + Contract.ThrowIfNull(info); + SymbolTreeInfo = info; ReferencingProjects = referencingProjects; } } @@ -129,16 +116,16 @@ public async Task TryGetMetadataSymbolTreeInfoAsync( if (key != null) { if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) && - metadataInfo.Checksum == checksum) + metadataInfo.SymbolTreeInfo.Checksum == checksum) { - return metadataInfo.SymbolTreeInfoOpt; + return metadataInfo.SymbolTreeInfo; } } // If we didn't have it in our cache, see if we can load it from disk. // Note: pass 'loadOnly' so we only attempt to load from disk, not to actually // try to create the metadata. - var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( + var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( solution, reference, checksum, loadOnly: true, cancellationToken: cancellationToken).ConfigureAwait(false); return info; } @@ -255,15 +242,18 @@ private async Task UpdateReferenceAsync( var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken); if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || - metadataInfo.Checksum != checksum) + metadataInfo.SymbolTreeInfo.Checksum != checksum) { - var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( + var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( project.Solution, reference, checksum, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); + Contract.ThrowIfNull(info); + Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then it's checksum much match our checksum."); + // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. - metadataInfo = new MetadataInfo(checksum, info, metadataInfo.ReferencingProjects ?? new HashSet()); + metadataInfo = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet()); _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs index da95fee4a35c0..3206101af27ec 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs @@ -74,14 +74,12 @@ private static async Task AddMetadataDeclarationsWithNormalQueryAsync( { if (referenceOpt != null) { - var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( + var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( project.Solution, referenceOpt, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); - if (info != null) - { - var symbols = await info.FindAsync( + + var symbols = await info.FindAsync( query, assembly, project.Id, filter, cancellationToken).ConfigureAwait(false); - list.AddRange(symbols); - } + list.AddRange(symbols); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs index b5a1cf9179d3c..37cf8cfeb9ba8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs @@ -469,12 +469,8 @@ private static async Task FindImmediateMatchingMetadataTypesInMetadataReferenceA // implement that type. Because the mapping is from the simple name // we might get false positives. But that's fine as we still use // 'metadataTypeMatches' to make sure the match is correct. - var symbolTreeInfo = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( + var symbolTreeInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( project.Solution, reference, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); - if (symbolTreeInfo == null) - { - return; - } // For each type we care about, see if we can find any derived types // in this index. diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index b7bd6d9184ed3..88a743df9e63e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -105,7 +105,7 @@ private SymbolTreeInfo( private SymbolTreeInfo( Checksum checksum, string concatenatedNames, - Node[] sortedNodes, + Node[] sortedNodes, Task spellCheckerTask) { Checksum = checksum; @@ -114,6 +114,15 @@ private SymbolTreeInfo( _spellCheckerTask = spellCheckerTask; } + public static SymbolTreeInfo CreateEmpty(Checksum checksum) + { + var unsortedNodes = ImmutableArray.Create(new BuilderNode(name: "", parentIndex: RootNodeParentIndex)); + SortNodes(unsortedNodes, out var concatenatedNames, out var sortedNodes); + + return new SymbolTreeInfo(checksum, concatenatedNames, sortedNodes, + CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes)); + } + public Task> FindAsync( SearchQuery query, IAssemblySymbol assembly, ProjectId assemblyProjectId, SymbolFilter filter, CancellationToken cancellationToken) { @@ -209,7 +218,7 @@ private async Task> FindAsync( Bind(node, assemblySymbol.GlobalNamespace, results, cancellationToken); } - return results.ToImmutableAndFree(); ; + return results.ToImmutableAndFree(); } private static StringSliceComparer GetComparer(bool ignoreCase) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 680635d5627ea..923fdd6d9ba3b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -59,22 +59,21 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference } } - public static Task TryGetInfoForMetadataReferenceAsync( + public static Task GetInfoForMetadataReferenceAsync( Solution solution, PortableExecutableReference reference, bool loadOnly, CancellationToken cancellationToken) { var checksum = GetMetadataChecksum(solution, reference, cancellationToken); - return TryGetInfoForMetadataReferenceAsync( + return GetInfoForMetadataReferenceAsync( solution, reference, checksum, loadOnly, cancellationToken); } /// /// Produces a for a given . - /// Note: can return null if we weren't able to actually load the metadata for some - /// reason. + /// Note: will never return null; /// - public static Task TryGetInfoForMetadataReferenceAsync( + public static Task GetInfoForMetadataReferenceAsync( Solution solution, PortableExecutableReference reference, Checksum checksum, @@ -84,7 +83,7 @@ public static Task TryGetInfoForMetadataReferenceAsync( var metadataId = GetMetadataIdNoThrow(reference); if (metadataId == null) { - return SpecializedTasks.Default(); + return Task.FromResult(CreateEmpty(checksum)); } // Try to acquire the data outside the lock. That way we can avoid any sort of @@ -99,14 +98,14 @@ public static Task TryGetInfoForMetadataReferenceAsync( var metadata = GetMetadataNoThrow(reference); if (metadata == null) { - return SpecializedTasks.Default(); + return Task.FromResult(CreateEmpty(checksum)); } - return TryGetInfoForMetadataReferenceSlowAsync( + return GetInfoForMetadataReferenceSlowAsync( solution, reference, checksum, loadOnly, metadata, cancellationToken); } - private static async Task TryGetInfoForMetadataReferenceSlowAsync( + private static async Task GetInfoForMetadataReferenceSlowAsync( Solution solution, PortableExecutableReference reference, Checksum checksum, bool loadOnly, Metadata metadata, CancellationToken cancellationToken) { @@ -121,11 +120,11 @@ private static async Task TryGetInfoForMetadataReferenceSlowAsyn return await infoTask.ConfigureAwait(false); } - var info = await LoadOrCreateMetadataSymbolTreeInfoAsync( + var info = await TryLoadOrCreateMetadataSymbolTreeInfoAsync( solution, reference, checksum, loadOnly, cancellationToken).ConfigureAwait(false); if (info == null && loadOnly) { - return null; + return CreateEmpty(checksum); } // Cache the result in our dictionary. Store it as a completed task so that @@ -147,7 +146,7 @@ public static Checksum GetMetadataChecksum( return checksum; } - private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( + private static Task TryLoadOrCreateMetadataSymbolTreeInfoAsync( Solution solution, PortableExecutableReference reference, Checksum checksum, @@ -156,7 +155,7 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( { var filePath = reference.FilePath; - return LoadOrCreateAsync( + return TryLoadOrCreateAsync( solution, checksum, filePath, @@ -263,7 +262,7 @@ internal SymbolTreeInfo Create() } var unsortedNodes = GenerateUnsortedNodes(); - return SymbolTreeInfo.CreateSymbolTreeInfo( + return CreateSymbolTreeInfo( _solution, _checksum, _reference.FilePath, unsortedNodes, _inheritanceMap); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index d7a9b48c474f5..5d19ceb9081d7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -26,7 +26,7 @@ internal partial class SymbolTreeInfo : IObjectWritable private static Task LoadOrCreateSourceSymbolTreeInfoAsync( Project project, Checksum checksum, bool loadOnly, CancellationToken cancellationToken) { - return LoadOrCreateAsync( + return TryLoadOrCreateAsync( project.Solution, checksum, project.FilePath, @@ -48,7 +48,7 @@ private static Task LoadOrCreateSpellCheckerAsync( string filePath, Func> createAsync) { - return LoadOrCreateAsync( + return TryLoadOrCreateAsync( solution, checksum, filePath, @@ -64,7 +64,7 @@ private static Task LoadOrCreateSpellCheckerAsync( /// Generalized function for loading/creating/persisting data. Used as the common core /// code for serialization of SymbolTreeInfos and SpellCheckers. /// - private static async Task LoadOrCreateAsync( + private static async Task TryLoadOrCreateAsync( Solution solution, Checksum checksum, string filePath, @@ -116,16 +116,15 @@ private static async Task LoadOrCreateAsync( // Now, try to create a new instance and write it to the persistence service. result = await createAsync().ConfigureAwait(false); - if (result != null) + Contract.ThrowIfNull(result); + + using (var stream = SerializableBytes.CreateWritableStream()) + using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { - using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) - { - result.WriteTo(writer); - stream.Position = 0; + result.WriteTo(writer); + stream.Position = 0; - await storage.WriteStreamAsync(key, stream, cancellationToken).ConfigureAwait(false); - } + await storage.WriteStreamAsync(key, stream, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index f4a1b4c540f13..8c200861b3b2f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -18,9 +18,7 @@ internal partial class SymbolTreeInfo new SimplePool>(() => new MultiDictionary()); private static MultiDictionary AllocateSymbolMap() - { - return s_symbolMapPool.Allocate(); - } + => s_symbolMapPool.Allocate(); private static void FreeSymbolMap(MultiDictionary symbolMap) { @@ -31,8 +29,10 @@ private static void FreeSymbolMap(MultiDictionary symbolMap) public static Task GetInfoForSourceAssemblyAsync( Project project, Checksum checksum, CancellationToken cancellationToken) { - return LoadOrCreateSourceSymbolTreeInfoAsync( + var result = LoadOrCreateSourceSymbolTreeInfoAsync( project, checksum, loadOnly: false, cancellationToken: cancellationToken); + Contract.ThrowIfNull(result); + return result; } public static async Task GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) @@ -81,7 +81,7 @@ internal static async Task CreateSourceSymbolTreeInfoAsync( var assembly = compilation.Assembly; if (assembly == null) { - return null; + return CreateEmpty(checksum); } var unsortedNodes = ArrayBuilder.GetInstance(); From c10565ecf1d0f769d74f62b373db9d760906b82f Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:01:16 -0700 Subject: [PATCH 109/284] Fix grammar. --- .../SymbolTreeInfoIncrementalAnalyzerProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index cdf69e1500b48..529898596f1be 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -213,7 +213,7 @@ private async Task UpdateSourceSymbolTreeInfoAsync(Project project, Cancellation project, checksum, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(projectInfo); - Contract.ThrowIfTrue(projectInfo.Checksum != checksum, "If we computed a SymbolTreeInfo, then it's checksum much match our checksum."); + Contract.ThrowIfTrue(projectInfo.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); // Mark that we're up to date with this project. Future calls with the same // semantic version can bail out immediately. @@ -248,7 +248,7 @@ private async Task UpdateReferenceAsync( project.Solution, reference, checksum, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(info); - Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then it's checksum much match our checksum."); + Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce From 9862f3605f79d50dfc8b6bd67fb11077a73cdf10 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:02:59 -0700 Subject: [PATCH 110/284] Share code. --- .../Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.Node.cs | 2 ++ .../Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs | 2 +- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.Node.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.Node.cs index acc2c6825c66a..e28d0bd46b879 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.Node.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.Node.cs @@ -19,6 +19,8 @@ internal partial class SymbolTreeInfo [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] private struct BuilderNode { + public static readonly BuilderNode RootNode = new BuilderNode("", RootNodeParentIndex); + public readonly string Name; public readonly int ParentIndex; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 88a743df9e63e..e35d3329e8244 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -116,7 +116,7 @@ private SymbolTreeInfo( public static SymbolTreeInfo CreateEmpty(Checksum checksum) { - var unsortedNodes = ImmutableArray.Create(new BuilderNode(name: "", parentIndex: RootNodeParentIndex)); + var unsortedNodes = ImmutableArray.Create(BuilderNode.RootNode); SortNodes(unsortedNodes, out var concatenatedNames, out var sortedNodes); return new SymbolTreeInfo(checksum, concatenatedNames, sortedNodes, diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 923fdd6d9ba3b..7ca0acbe92aba 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -634,7 +634,7 @@ private MetadataNode GetOrCreateChildNode( private ImmutableArray GenerateUnsortedNodes() { var unsortedNodes = ArrayBuilder.GetInstance(); - unsortedNodes.Add(new BuilderNode(name: "", parentIndex: RootNodeParentIndex)); + unsortedNodes.Add(BuilderNode.RootNode); AddUnsortedNodes(unsortedNodes, parentNode: _rootNode, parentIndex: 0); From b4c9c9e6c5399dec4016c7e17c3dce80d6880413 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:10:29 -0700 Subject: [PATCH 111/284] Add more contract guarantees --- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 4 +++- .../SymbolTreeInfo_Serialization.cs | 23 +++---------------- .../SymbolTree/SymbolTreeInfo_Source.cs | 14 ++++++++--- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 7ca0acbe92aba..3fa988e32885d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -155,7 +155,7 @@ private static Task TryLoadOrCreateMetadataSymbolTreeInfoAsync( { var filePath = reference.FilePath; - return TryLoadOrCreateAsync( + var result = TryLoadOrCreateAsync( solution, checksum, filePath, @@ -165,6 +165,8 @@ private static Task TryLoadOrCreateMetadataSymbolTreeInfoAsync( getPersistedChecksum: info => info.Checksum, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), cancellationToken: cancellationToken); + Contract.ThrowIfFalse(result != null || loadOnly == true, "Result can only be null if 'loadOnly: true' was passed."); + return result; } private static Task CreateMetadataSymbolTreeInfoAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 5d19ceb9081d7..cb1a23976f48c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -19,25 +19,6 @@ internal partial class SymbolTreeInfo : IObjectWritable private const string PrefixMetadataSymbolTreeInfo = ""; private const string SerializationFormat = "17"; - /// - /// Loads the SymbolTreeInfo for a given assembly symbol (metadata or project). If the - /// info can't be loaded, it will be created (and persisted if possible). - /// - private static Task LoadOrCreateSourceSymbolTreeInfoAsync( - Project project, Checksum checksum, bool loadOnly, CancellationToken cancellationToken) - { - return TryLoadOrCreateAsync( - project.Solution, - checksum, - project.FilePath, - loadOnly, - createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken), - keySuffix: "_Source", - getPersistedChecksum: info => info.Checksum, - readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)), - cancellationToken: cancellationToken); - } - /// /// Loads the SpellChecker for a given assembly symbol (metadata or project). If the /// info can't be loaded, it will be created (and persisted if possible). @@ -48,7 +29,7 @@ private static Task LoadOrCreateSpellCheckerAsync( string filePath, Func> createAsync) { - return TryLoadOrCreateAsync( + var result = TryLoadOrCreateAsync( solution, checksum, filePath, @@ -58,6 +39,8 @@ private static Task LoadOrCreateSpellCheckerAsync( getPersistedChecksum: s => s.Checksum, readObject: SpellChecker.ReadFrom, cancellationToken: CancellationToken.None); + Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'."); + return result; } /// diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 8c200861b3b2f..d8ca927426eac 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -29,9 +29,17 @@ private static void FreeSymbolMap(MultiDictionary symbolMap) public static Task GetInfoForSourceAssemblyAsync( Project project, Checksum checksum, CancellationToken cancellationToken) { - var result = LoadOrCreateSourceSymbolTreeInfoAsync( - project, checksum, loadOnly: false, cancellationToken: cancellationToken); - Contract.ThrowIfNull(result); + var result = TryLoadOrCreateAsync( + project.Solution, + checksum, + project.FilePath, + loadOnly: false, + createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken), + keySuffix: "_Source", + getPersistedChecksum: info => info.Checksum, + readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)), + cancellationToken: cancellationToken); + Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'."); return result; } From 8b7ad834409d5c3434fb6aeea11e699ec3d7d96e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:16:03 -0700 Subject: [PATCH 112/284] Don't require spell checker. --- .../FindSymbols/SymbolTree/SymbolTreeInfo.cs | 4 ++-- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 4 +--- .../SymbolTree/SymbolTreeInfo_Serialization.cs | 12 ++++-------- .../FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 4 +--- .../Core/Portable/Utilities/SpellChecker.cs | 2 +- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index e35d3329e8244..24ca8762d24cb 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -16,9 +16,9 @@ namespace Microsoft.CodeAnalysis.FindSymbols { - internal partial class SymbolTreeInfo + internal partial class SymbolTreeInfo : IChecksummedObject { - public readonly Checksum Checksum; + public Checksum Checksum { get; } /// /// To prevent lots of allocations, we concatenate all the names in all our diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 3fa988e32885d..9981c79f3940c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -158,11 +158,9 @@ private static Task TryLoadOrCreateMetadataSymbolTreeInfoAsync( var result = TryLoadOrCreateAsync( solution, checksum, - filePath, loadOnly, createAsync: () => CreateMetadataSymbolTreeInfoAsync(solution, checksum, reference, cancellationToken), - keySuffix: "_Metadata", - getPersistedChecksum: info => info.Checksum, + keySuffix: "_Metadata_" + filePath, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), cancellationToken: cancellationToken); Contract.ThrowIfFalse(result != null || loadOnly == true, "Result can only be null if 'loadOnly: true' was passed."); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index cb1a23976f48c..f9787e62a75b1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -32,11 +32,9 @@ private static Task LoadOrCreateSpellCheckerAsync( var result = TryLoadOrCreateAsync( solution, checksum, - filePath, loadOnly: false, createAsync: createAsync, - keySuffix: "_SpellChecker", - getPersistedChecksum: s => s.Checksum, + keySuffix: "_SpellChecker_" + filePath, readObject: SpellChecker.ReadFrom, cancellationToken: CancellationToken.None); Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'."); @@ -50,13 +48,11 @@ private static Task LoadOrCreateSpellCheckerAsync( private static async Task TryLoadOrCreateAsync( Solution solution, Checksum checksum, - string filePath, bool loadOnly, Func> createAsync, string keySuffix, - Func getPersistedChecksum, Func readObject, - CancellationToken cancellationToken) where T : class, IObjectWritable + CancellationToken cancellationToken) where T : class, IObjectWritable, IChecksummedObject { if (checksum == null) { @@ -70,7 +66,7 @@ private static async Task TryLoadOrCreateAsync( using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) { // Get the unique key to identify our data. - var key = PrefixMetadataSymbolTreeInfo + keySuffix + "_" + filePath; + var key = PrefixMetadataSymbolTreeInfo + keySuffix; using (var stream = await storage.ReadStreamAsync(key, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { @@ -80,7 +76,7 @@ private static async Task TryLoadOrCreateAsync( // If we're able to, and the version of the persisted data matches // our version, then we can reuse this instance. result = readObject(reader); - if (result != null && checksum == getPersistedChecksum(result)) + if (result != null && checksum == result.Checksum) { return result; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index d8ca927426eac..05fd264c3a56c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -32,11 +32,9 @@ public static Task GetInfoForSourceAssemblyAsync( var result = TryLoadOrCreateAsync( project.Solution, checksum, - project.FilePath, loadOnly: false, createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken), - keySuffix: "_Source", - getPersistedChecksum: info => info.Checksum, + keySuffix: "_Source_" + project.FilePath, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)), cancellationToken: cancellationToken); Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'."); diff --git a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs index 31035b894eba2..a00ccf77c00a4 100644 --- a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs +++ b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs @@ -10,7 +10,7 @@ namespace Roslyn.Utilities { - internal class SpellChecker : IObjectWritable + internal class SpellChecker : IObjectWritable, IChecksummedObject { private const string SerializationFormat = "3"; From c0adc6242fff999e0dc84d2aac1ee6d52ffaf2a3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:19:15 -0700 Subject: [PATCH 113/284] Simplify code. --- .../Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs | 4 ++-- .../FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 24ca8762d24cb..c5d5d35e6ac54 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -331,8 +331,8 @@ private static Task GetSpellCheckerTask( // SymbolTreeInfo. This way the SymbolTreeInfo will be ready immediately // for non-fuzzy searches, and soon afterwards it will be able to perform // fuzzy searches as well. - return Task.Run(() => LoadOrCreateSpellCheckerAsync(solution, checksum, filePath, - () => CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes))); + return Task.Run(() => LoadOrCreateSpellCheckerAsync( + solution, checksum, filePath, concatenatedNames, sortedNodes)); } private static Task CreateSpellCheckerAsync(Checksum checksum, string concatenatedNames, Node[] sortedNodes) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index f9787e62a75b1..3c26fec56c5ca 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -27,13 +27,14 @@ private static Task LoadOrCreateSpellCheckerAsync( Solution solution, Checksum checksum, string filePath, - Func> createAsync) + string concatenatedNames, + Node[] sortedNodes) { var result = TryLoadOrCreateAsync( solution, checksum, loadOnly: false, - createAsync: createAsync, + createAsync: () => CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes), keySuffix: "_SpellChecker_" + filePath, readObject: SpellChecker.ReadFrom, cancellationToken: CancellationToken.None); From 51de32fb9afb742a3e6e9889a4f9ca78536969f0 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:38:55 -0700 Subject: [PATCH 114/284] Rename parameter. --- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 2 +- .../SymbolTree/SymbolTreeInfo_Serialization.cs | 12 ++++++------ .../FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 2 +- .../Core/Portable/Utilities/SpellChecker.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 9981c79f3940c..20cac6563cfcc 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -161,7 +161,7 @@ private static Task TryLoadOrCreateMetadataSymbolTreeInfoAsync( loadOnly, createAsync: () => CreateMetadataSymbolTreeInfoAsync(solution, checksum, reference, cancellationToken), keySuffix: "_Metadata_" + filePath, - readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), + tryReadObject: reader => TryReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), cancellationToken: cancellationToken); Contract.ThrowIfFalse(result != null || loadOnly == true, "Result can only be null if 'loadOnly: true' was passed."); return result; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 3c26fec56c5ca..b397e5ee967d1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -36,7 +36,7 @@ private static Task LoadOrCreateSpellCheckerAsync( loadOnly: false, createAsync: () => CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes), keySuffix: "_SpellChecker_" + filePath, - readObject: SpellChecker.ReadFrom, + tryReadObject: SpellChecker.TryReadFrom, cancellationToken: CancellationToken.None); Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'."); return result; @@ -52,7 +52,7 @@ private static async Task TryLoadOrCreateAsync( bool loadOnly, Func> createAsync, string keySuffix, - Func readObject, + Func tryReadObject, CancellationToken cancellationToken) where T : class, IObjectWritable, IChecksummedObject { if (checksum == null) @@ -76,8 +76,8 @@ private static async Task TryLoadOrCreateAsync( // We have some previously persisted data. Attempt to read it back. // If we're able to, and the version of the persisted data matches // our version, then we can reuse this instance. - result = readObject(reader); - if (result != null && checksum == result.Checksum) + result = tryReadObject(reader); + if (result?.Checksum == result.Checksum) { return result; } @@ -142,12 +142,12 @@ public void WriteTo(ObjectWriter writer) internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly( ObjectReader reader, Checksum checksum) { - return ReadSymbolTreeInfo(reader, + return TryReadSymbolTreeInfo(reader, (names, nodes) => Task.FromResult( new SpellChecker(checksum, nodes.Select(n => new StringSlice(names, n.NameSpan))))); } - private static SymbolTreeInfo ReadSymbolTreeInfo( + private static SymbolTreeInfo TryReadSymbolTreeInfo( ObjectReader reader, Func> createSpellCheckerTask) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 05fd264c3a56c..002edd9d30c83 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -35,7 +35,7 @@ public static Task GetInfoForSourceAssemblyAsync( loadOnly: false, createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken), keySuffix: "_Source_" + project.FilePath, - readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)), + tryReadObject: reader => TryReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)), cancellationToken: cancellationToken); Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'."); return result; diff --git a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs index a00ccf77c00a4..968174a7fdc42 100644 --- a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs +++ b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs @@ -49,7 +49,7 @@ void IObjectWritable.WriteTo(ObjectWriter writer) _bkTree.WriteTo(writer); } - internal static SpellChecker ReadFrom(ObjectReader reader) + internal static SpellChecker TryReadFrom(ObjectReader reader) { try { From 4ddfe1fd78ef0badafe20d4e8e3f498dc3a678d2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 15:39:22 -0700 Subject: [PATCH 115/284] Fix comparison. --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index b397e5ee967d1..6ec42087d2908 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -77,7 +77,7 @@ private static async Task TryLoadOrCreateAsync( // If we're able to, and the version of the persisted data matches // our version, then we can reuse this instance. result = tryReadObject(reader); - if (result?.Checksum == result.Checksum) + if (result?.Checksum == checksum) { return result; } From 71c8e3e2cf575647e8573f1fe633ec3bfe139de6 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 17:29:34 -0700 Subject: [PATCH 116/284] Cache the delegates we use in simplification. --- ...ractCSharpReducer.AbstractExpressionRewriter.cs | 9 --------- .../Simplification/CSharpCastReducer.Rewriter.cs | 2 +- .../Portable/Simplification/CSharpCastReducer.cs | 5 ++++- .../CSharpEscapingReducer.Rewriter.cs | 4 ++-- .../Simplification/CSharpEscapingReducer.cs | 7 +++++-- .../CSharpExtensionMethodReducer.Rewriter.cs | 4 ++-- .../Simplification/CSharpExtensionMethodReducer.cs | 3 +++ .../CSharpInferredMemberNameReducer.Rewriter.cs | 4 ++-- .../CSharpInferredMemberNameReducer.cs | 4 ++++ .../CSharpMiscellaneousReducer.Rewriter.cs | 6 +++--- .../Simplification/CSharpMiscellaneousReducer.cs | 5 +++++ .../Simplification/CSharpNameReducer.Rewriter.cs | 14 +++++++------- .../Portable/Simplification/CSharpNameReducer.cs | 3 +++ .../CSharpParenthesesReducer.Rewriter.cs | 4 ++-- .../Simplification/CSharpParenthesesReducer.cs | 5 ++++- .../Simplification/AbstractVisualBasicReducer.vb | 5 +++-- .../VisualBasicCallReducer.Rewriter.vb | 7 +++---- .../Simplification/VisualBasicCallReducer.vb | 2 ++ .../VisualBasicCastReducer.Rewriter.vb | 11 +++++------ .../Simplification/VisualBasicCastReducer.vb | 8 ++++++-- .../VisualBasicExtensionMethodReducer.Rewriter.vb | 4 ++-- .../VisualBasicExtensionMethodReducer.vb | 2 ++ ...isualBasicInferredMemberNameReducer.Rewriter.vb | 4 ++-- .../VisualBasicInferredMemberNameReducer.vb | 4 ++++ .../VisualBasicMiscellaneousReducer.Rewriter.vb | 9 ++++----- .../VisualBasicMiscellaneousReducer.vb | 6 ++++++ .../VisualBasicNameReducer.Rewriter.vb | 12 ++++++------ .../Simplification/VisualBasicNameReducer.vb | 2 ++ .../VisualBasicParenthesesReducer.Rewriter.vb | 4 ++-- ...isualBasicVariableDeclaratorReducer.Rewriter.vb | 2 +- .../VisualBasicVariableDeclaratorReducer.vb | 2 ++ 31 files changed, 99 insertions(+), 64 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs index 301e1ab1f4031..4b7745d97e5c2 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs @@ -128,15 +128,6 @@ protected SyntaxNode SimplifyExpression( return SimplifyNode(expression, newNode, parentNode, simplifier); } - protected SyntaxNode SimplifyStatement( - TStatement statement, - SyntaxNode newNode, - Func simplifier) - where TStatement : ExpressionSyntax - { - return SimplifyNode(statement, newNode, GetParentNode(statement), simplifier); - } - protected SyntaxToken SimplifyToken(SyntaxToken token, Func simplifier) { _cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs index decd0c467d6c8..61142fd6bb734 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs @@ -22,7 +22,7 @@ public override SyntaxNode VisitCastExpression(CastExpressionSyntax node) return SimplifyExpression( node, newNode: base.VisitCastExpression(node), - simplifier: SimplifyCast); + simplifier: s_simplifyCast); } public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node) diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs index 2278227450219..09fb9da7ea087 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -17,6 +18,8 @@ public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet return new Rewriter(optionSet, cancellationToken); } + private static readonly Func s_simplifyCast = SimplifyCast; + private static ExpressionSyntax SimplifyCast(CastExpressionSyntax node, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken) { if (!node.IsUnnecessaryCast(semanticModel, cancellationToken)) @@ -44,4 +47,4 @@ private static ExpressionSyntax SimplifyCast(CastExpressionSyntax node, Semantic return resultNode; } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs index 9006697fe7631..b4c07b6fd3750 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs @@ -20,8 +20,8 @@ public Rewriter(CSharpEscapingReducer escapingSimplifierService, OptionSet optio public override SyntaxToken VisitToken(SyntaxToken token) { var newToken = base.VisitToken(token); - return SimplifyToken(newToken, _escapingSimplifierService.SimplifyIdentifierToken); + return SimplifyToken(newToken, s_simplifyIdentifierToken); } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs index 5cb68433eec07..b87d5a4b22cd9 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -17,7 +18,9 @@ public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet return new Rewriter(this, optionSet, cancellationToken); } - private SyntaxToken SimplifyIdentifierToken( + private static Func s_simplifyIdentifierToken = SimplifyIdentifierToken; + + private static SyntaxToken SimplifyIdentifierToken( SyntaxToken token, SemanticModel semanticModel, OptionSet optionSet, @@ -114,7 +117,7 @@ private SyntaxToken SimplifyIdentifierToken( return result; } - private SyntaxToken CreateNewIdentifierTokenFromToken(SyntaxToken originalToken, bool escape) + private static SyntaxToken CreateNewIdentifierTokenFromToken(SyntaxToken originalToken, bool escape) { var isVerbatimIdentifier = originalToken.IsVerbatimIdentifier(); if (isVerbatimIdentifier == escape) diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs index 1f46ee17ee5f8..624e77dcee453 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs @@ -20,8 +20,8 @@ public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax return SimplifyExpression( node, newNode: base.VisitInvocationExpression(node), - simplifier: SimplifyExtensionMethod); + simplifier: s_simplifyExtensionMethod); } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs index 28b2919a3da1b..15de394df2cf7 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Diagnostics; using System.Linq; using System.Threading; @@ -17,6 +18,8 @@ public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet return new Rewriter(optionSet, cancellationToken); } + private static readonly Func s_simplifyExtensionMethod = SimplifyExtensionMethod; + private static SyntaxNode SimplifyExtensionMethod( InvocationExpressionSyntax node, SemanticModel semanticModel, diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs index f6a9aae9a8ca7..f19da855592f0 100755 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs @@ -26,7 +26,7 @@ public override SyntaxNode VisitArgument(ArgumentSyntax node) node, parentNode: node.Parent, newNode: newNode, - simplifier: SimplifyTupleName); + simplifier: s_simplifyTupleName); } return newNode; @@ -38,7 +38,7 @@ public override SyntaxNode VisitAnonymousObjectMemberDeclarator(AnonymousObjectM node, parentNode: node.Parent, newNode: base.VisitAnonymousObjectMemberDeclarator(node), - simplifier: SimplifyAnonymousTypeMemberName); + simplifier: s_simplifyAnonymousTypeMemberName); } } } diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs index 21dc2d5051648..3983736cf0d41 100755 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -19,6 +20,8 @@ public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet return new Rewriter(optionSet, cancellationToken); } + private static readonly Func s_simplifyTupleName = SimplifyTupleName; + private static ArgumentSyntax SimplifyTupleName(ArgumentSyntax node, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken) { // Tuple elements are arguments in a tuple expression @@ -37,6 +40,7 @@ private static ArgumentSyntax SimplifyTupleName(ArgumentSyntax node, SemanticMod return node.WithNameColon(null).WithTriviaFrom(node); } + private static readonly Func s_simplifyAnonymousTypeMemberName = SimplifyAnonymousTypeMemberName; private static SyntaxNode SimplifyAnonymousTypeMemberName(AnonymousObjectMemberDeclaratorSyntax node, SemanticModel semanticModel, OptionSet optionSet, CancellationToken canellationToken) { diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs index 4ac9b91e11c28..c338efd9e1ccd 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs @@ -21,7 +21,7 @@ public override SyntaxNode VisitParameter(ParameterSyntax node) node, newNode: base.VisitParameter(node), parentNode: node.Parent, - simplifier: SimplifyParameter); + simplifier: s_simplifyParameter); } public override SyntaxNode VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node) @@ -30,8 +30,8 @@ public override SyntaxNode VisitParenthesizedLambdaExpression(ParenthesizedLambd node, newNode: base.VisitParenthesizedLambdaExpression(node), parentNode: node.Parent, - simplifier: SimplifyParenthesizedLambdaExpression); + simplifier: s_simplifyParenthesizedLambdaExpression); } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs index 33ccbe2bf2b23..4d3c315c30b31 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -50,6 +51,8 @@ private static bool CanRemoveTypeFromParameter( return false; } + private static Func s_simplifyParameter = SimplifyParameter; + private static SyntaxNode SimplifyParameter( SyntaxNode node, SemanticModel semanticModel, @@ -66,6 +69,8 @@ private static SyntaxNode SimplifyParameter( return node; } + private static readonly Func s_simplifyParenthesizedLambdaExpression = SimplifyParenthesizedLambdaExpression; + private static SyntaxNode SimplifyParenthesizedLambdaExpression( SyntaxNode node, SemanticModel semanticModel, diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs index 6a98638a369c3..87e19ddd40d73 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs @@ -30,7 +30,7 @@ public override SyntaxNode VisitPredefinedType(PredefinedTypeSyntax node) var result = SimplifyExpression( node, newNode: base.VisitPredefinedType(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; @@ -48,7 +48,7 @@ public override SyntaxNode VisitAliasQualifiedName(AliasQualifiedNameSyntax node var result = SimplifyExpression( node, newNode: base.VisitAliasQualifiedName(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; @@ -66,7 +66,7 @@ public override SyntaxNode VisitQualifiedName(QualifiedNameSyntax node) var result = SimplifyExpression( node, newNode: base.VisitQualifiedName(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; @@ -84,7 +84,7 @@ public override SyntaxNode VisitMemberAccessExpression(MemberAccessExpressionSyn var result = SimplifyExpression( node, newNode: base.VisitMemberAccessExpression(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; @@ -102,7 +102,7 @@ public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node) var result = SimplifyExpression( node, newNode: base.VisitIdentifierName(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; @@ -120,7 +120,7 @@ public override SyntaxNode VisitGenericName(GenericNameSyntax node) var result = SimplifyExpression( node, newNode: base.VisitGenericName(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; @@ -138,7 +138,7 @@ public override SyntaxNode VisitQualifiedCref(QualifiedCrefSyntax node) var result = SimplifyExpression( node, newNode: base.VisitQualifiedCref(node), - simplifier: SimplifyName); + simplifier: s_simplifyName); this.alwaysSimplify = oldAlwaysSimplify; diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs index 28b1974ff6e06..d9bc79dfeea30 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -17,6 +18,8 @@ public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet return new Rewriter(optionSet, cancellationToken); } + private static readonly Func s_simplifyName = SimplifyName; + private static SyntaxNode SimplifyName( SyntaxNode node, SemanticModel semanticModel, diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs index d0f4124e6f49d..c20a0b1c4c015 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs @@ -20,8 +20,8 @@ public override SyntaxNode VisitParenthesizedExpression(ParenthesizedExpressionS return SimplifyExpression( node, newNode: base.VisitParenthesizedExpression(node), - simplifier: SimplifyParentheses); + simplifier: s_simplifyParentheses); } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs index cc14cf1eb4227..1e525ed1fe5e1 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -17,6 +18,8 @@ public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet return new Rewriter(optionSet, cancellationToken); } + private static readonly Func s_simplifyParentheses = SimplifyParentheses; + private static SyntaxNode SimplifyParentheses( ParenthesizedExpressionSyntax node, SemanticModel semanticModel, @@ -54,4 +57,4 @@ private static SyntaxNode SimplifyParentheses( return node; } } -} +} \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb index 38e7bae8fd0b1..0212ce261c265 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb @@ -9,6 +9,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend MustInherit Class AbstractVisualBasicReducer Inherits AbstractReducer + Protected Shared ReadOnly s_reduceParentheses As Func(Of ParenthesizedExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf ReduceParentheses + Protected Shared Function ReduceParentheses( node As ParenthesizedExpressionSyntax, semanticModel As SemanticModel, @@ -45,6 +47,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification ' We don't know how to simplify this. Return node End Function - End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb index 42a058bd30f3f..5b1aef9915c0d 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb @@ -17,16 +17,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyStatement( node, newNode:=MyBase.VisitCallStatement(node), - simplifier:=AddressOf SimplifyCallStatement) + simplifier:=s_simplifyCallStatement) End Function Public Overrides Function VisitParenthesizedExpression(node As ParenthesizedExpressionSyntax) As SyntaxNode Return SimplifyExpression( node, newNode:=MyBase.VisitParenthesizedExpression(node), - simplifier:=AddressOf ReduceParentheses) + simplifier:=s_reduceParentheses) End Function - End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb index 6db8e03ebf950..3f8be8e1277f0 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb @@ -13,6 +13,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return New Rewriter(optionSet, cancellationToken) End Function + Private Shared ReadOnly s_simplifyCallStatement As Func(Of CallStatementSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyCallStatement + Private Shared Function SimplifyCallStatement( callStatement As CallStatementSyntax, semanticModel As SemanticModel, diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb index 86759bb92efd2..cd876badbc161 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb @@ -17,30 +17,29 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyExpression( node, newNode:=MyBase.VisitCTypeExpression(node), - simplifier:=AddressOf SimplifyCast) + simplifier:=s_simplifyCast) End Function Public Overrides Function VisitDirectCastExpression(node As DirectCastExpressionSyntax) As SyntaxNode Return SimplifyExpression( node, newNode:=MyBase.VisitDirectCastExpression(node), - simplifier:=AddressOf SimplifyCast) + simplifier:=s_simplifyCast) End Function Public Overrides Function VisitTryCastExpression(node As TryCastExpressionSyntax) As SyntaxNode Return SimplifyExpression( node, newNode:=MyBase.VisitTryCastExpression(node), - simplifier:=AddressOf SimplifyCast) + simplifier:=s_simplifyCast) End Function Public Overrides Function VisitPredefinedCastExpression(node As PredefinedCastExpressionSyntax) As SyntaxNode Return SimplifyExpression( node, newNode:=MyBase.VisitPredefinedCastExpression(node), - simplifier:=AddressOf SimplifyCast) + simplifier:=s_simplifyPredefinedCast) End Function - End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb index f0a09e91223ad..5b968bf017042 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb @@ -29,6 +29,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return resultNode End Function + Private Shared ReadOnly s_simplifyCast As Func(Of CastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = s_simplifyCast + Private Overloads Shared Function SimplifyCast( node As CastExpressionSyntax, semanticModel As SemanticModel, @@ -43,7 +45,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyCast(node, node.Expression, optionSet, cancellationToken) End Function - Private Overloads Shared Function SimplifyCast( + Private Shared ReadOnly s_simplifyPredefinedCast As Func(Of PredefinedCastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyPredefinedCast + + Private Overloads Shared Function SimplifyPredefinedCast( node As PredefinedCastExpressionSyntax, semanticModel As SemanticModel, optionSet As OptionSet, @@ -57,4 +61,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyCast(node, node.Expression, optionSet, cancellationToken) End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb index 051af71841f0d..7c82c5718df76 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb @@ -19,8 +19,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyExpression( node, newNode:=MyBase.VisitInvocationExpression(node), - simplifier:=AddressOf SimplifyInvocationExpression) + simplifier:=s_simplifyInvocationExpression) End Function End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb index 0dcc233ea25ae..4117cc5c82c1a 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb @@ -12,6 +12,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return New Rewriter(optionSet, cancellationToken) End Function + Private Shared ReadOnly s_simplifyInvocationExpression As Func(Of InvocationExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyInvocationExpression + Private Shared Function SimplifyInvocationExpression( invocationExpression As InvocationExpressionSyntax, semanticModel As SemanticModel, diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb index 47d16bdfbb964..84b9c68f0ee09 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb @@ -24,7 +24,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification node, parentNode:=node.Parent, newNode:=newNode, - simplifyFunc:=AddressOf SimplifyTupleName) + simplifyFunc:=s_simplifyTupleName) End If Return newNode @@ -37,7 +37,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification node, parentNode:=node.Parent, newNode:=newNode, - simplifyFunc:=AddressOf SimplifyNamedFieldInitializer) + simplifyFunc:=s_simplifyNamedFieldInitializer) End Function End Class End Class diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb index b3e14d8dd7613..97f1ff8b4ccf5 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb @@ -18,6 +18,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return New VisualBasicInferredMemberNameReducer.Rewriter(optionSet, cancellationToken) End Function + Private Shared ReadOnly s_simplifyTupleName As Func(Of SimpleArgumentSyntax, SemanticModel, OptionSet, CancellationToken, SimpleArgumentSyntax) = AddressOf SimplifyTupleName + Private Shared Function SimplifyTupleName( node As SimpleArgumentSyntax, semanticModel As SemanticModel, @@ -41,6 +43,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return node.WithNameColonEquals(Nothing).WithTriviaFrom(node) End Function + Private Shared ReadOnly s_simplifyNamedFieldInitializer As Func(Of NamedFieldInitializerSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyNamedFieldInitializer + Private Shared Function SimplifyNamedFieldInitializer(node As NamedFieldInitializerSyntax, arg2 As SemanticModel, arg3 As OptionSet, arg4 As CancellationToken) As SyntaxNode Dim inferredName = node.Expression.TryGetInferredMemberName() diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb index 178e52712c088..05ae22615b503 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyExpression( node, newNode:=MyBase.VisitInvocationExpression(node), - simplifier:=AddressOf SimplifyInvocationExpression) + simplifier:=s_simplifyInvocationExpression) End Function Public Overrides Function VisitObjectCreationExpression(node As ObjectCreationExpressionSyntax) As SyntaxNode @@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyExpression( node, newNode:=MyBase.VisitObjectCreationExpression(node), - simplifier:=AddressOf SimplifyObjectCreationExpression) + simplifier:=s_simplifyObjectCreationExpression) End Function Public Overrides Function VisitParameter(node As ParameterSyntax) As SyntaxNode @@ -38,9 +38,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification node, newNode:=MyBase.VisitParameter(node), parentNode:=node.Parent, - simplifyFunc:=AddressOf SimplifyParameter) + simplifyFunc:=s_simplifyParameter) End Function - End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb index 864d6d1341aad..4589357439bd1 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb @@ -13,6 +13,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return New Rewriter(optionSet, cancellationToken) End Function + Private Shared ReadOnly s_simplifyParameter As Func(Of ParameterSyntax, SemanticModel, OptionSet, CancellationToken, ParameterSyntax) = AddressOf SimplifyParameter + Private Shared Function SimplifyParameter( parameter As ParameterSyntax, semanticModel As SemanticModel, @@ -28,6 +30,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return parameter End Function + Private Shared ReadOnly s_simplifyInvocationExpression As Func(Of InvocationExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyInvocationExpression + Private Shared Function SimplifyInvocationExpression( invocationExpression As InvocationExpressionSyntax, semanticModel As SemanticModel, @@ -49,6 +53,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return invocationExpression End Function + Private Shared ReadOnly s_simplifyObjectCreationExpression As Func(Of ObjectCreationExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyObjectCreationExpression + Private Shared Function SimplifyObjectCreationExpression( objectCreationExpression As ObjectCreationExpressionSyntax, semanticModel As SemanticModel, diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb index 87fc446f4a74c..0e6d8abba873b 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb @@ -23,7 +23,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Dim result = SimplifyExpression( node, newNode:=MyBase.VisitGenericName(node), - simplifier:=AddressOf SimplifyName) + simplifier:=s_simplifyName) Me._alwaysSimplify = oldAlwaysSimplify @@ -39,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Dim result = SimplifyExpression( node, newNode:=MyBase.VisitIdentifierName(node), - simplifier:=AddressOf SimplifyName) + simplifier:=s_simplifyName) Me._alwaysSimplify = oldAlwaysSimplify @@ -55,7 +55,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Dim result = SimplifyExpression( node, newNode:=MyBase.VisitQualifiedName(node), - simplifier:=AddressOf SimplifyName) + simplifier:=s_simplifyName) Me._alwaysSimplify = oldAlwaysSimplify @@ -71,7 +71,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Dim result = SimplifyExpression( node, newNode:=MyBase.VisitMemberAccessExpression(node), - simplifier:=AddressOf SimplifyName) + simplifier:=s_simplifyName) Me._alwaysSimplify = oldAlwaysSimplify @@ -87,7 +87,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Dim result = SimplifyExpression( node, newNode:=MyBase.VisitNullableType(node), - simplifier:=AddressOf SimplifyName) + simplifier:=s_simplifyName) Me._alwaysSimplify = oldAlwaysSimplify @@ -103,7 +103,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Dim result = SimplifyExpression( node, newNode:=MyBase.VisitArrayType(node), - simplifier:=AddressOf SimplifyName) + simplifier:=s_simplifyName) Me._alwaysSimplify = oldAlwaysSimplify diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb index 56163216a1e79..c31bb23a9e74e 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb @@ -16,6 +16,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return New Rewriter(optionSet, cancellationToken) End Function + Private Shared ReadOnly s_simplifyName As Func(Of ExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyName + Private Overloads Shared Function SimplifyName( node As ExpressionSyntax, semanticModel As SemanticModel, diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb index 74ccb04635703..7766e4a29ba78 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb @@ -17,8 +17,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyExpression( node, newNode:=MyBase.VisitParenthesizedExpression(node), - simplifier:=AddressOf ReduceParentheses) + simplifier:=s_reduceParentheses) End Function End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb index e8a14a2feaea7..3236a101873a0 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification node, newNode:=MyBase.VisitVariableDeclarator(node), parentNode:=node, - simplifyFunc:=AddressOf SimplifyVariableDeclarator) + simplifyFunc:=s_simplifyVariableDeclarator) End Function End Class End Class diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb index 997bc0a110574..a9204fae01301 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb @@ -15,6 +15,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return New Rewriter(optionSet, cancellationToken) End Function + Private Shared ReadOnly s_simplifyVariableDeclarator As Func(Of VariableDeclaratorSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyVariableDeclarator + Private Overloads Shared Function SimplifyVariableDeclarator( node As VariableDeclaratorSyntax, semanticModel As SemanticModel, From 8e4e29b08d6608fecd2f415f1abec66e71689764 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 18:47:30 -0700 Subject: [PATCH 117/284] Pool the simplifiers we create while simplifying code. --- .../CSharp/Portable/CSharpWorkspace.csproj | 32 ++-- .../CodeGeneration/CSharpSyntaxGenerator.cs | 4 +- ...SharpReducer.AbstractReductionRewriter.cs} | 41 +++-- .../{ => Reducers}/AbstractCSharpReducer.cs | 6 +- .../CSharpCastReducer.Rewriter.cs | 11 +- .../{ => Reducers}/CSharpCastReducer.cs | 7 +- .../CSharpEscapingReducer.Rewriter.cs | 12 +- .../{ => Reducers}/CSharpEscapingReducer.cs | 9 +- .../CSharpExtensionMethodReducer.Rewriter.cs | 9 +- .../CSharpExtensionMethodReducer.cs | 15 +- ...SharpInferredMemberNameReducer.Rewriter.cs | 12 +- .../CSharpInferredMemberNameReducer.cs | 9 +- .../CSharpMiscellaneousReducer.Rewriter.cs | 9 +- .../CSharpMiscellaneousReducer.cs | 9 +- .../CSharpNameReducer.Rewriter.cs | 12 +- .../{ => Reducers}/CSharpNameReducer.cs | 9 +- .../CSharpParenthesesReducer.Rewriter.cs | 9 +- .../CSharpParenthesesReducer.cs | 7 +- .../AbstractReducer.IExpressionRewriter.cs | 10 +- .../Simplification/AbstractReducer.cs | 13 +- .../AbstractSimplificationService.cs | 143 +++++++++--------- .../Portable/BasicWorkspace.vbproj | 40 ++--- ...BasicReducer.AbstractReductionRewriter.vb} | 48 ++++-- .../AbstractVisualBasicReducer.vb | 4 + .../VisualBasicCallReducer.Rewriter.vb | 8 +- .../{ => Reducers}/VisualBasicCallReducer.vb | 12 +- .../VisualBasicCastReducer.Rewriter.vb | 8 +- .../{ => Reducers}/VisualBasicCastReducer.vb | 9 +- .../VisualBasicEscapingReducer.Rewriter.vb | 10 +- .../VisualBasicEscapingReducer.vb | 13 +- ...ualBasicExtensionMethodReducer.Rewriter.vb | 7 +- .../VisualBasicExtensionMethodReducer.vb | 12 +- ...BasicInferredMemberNameReducer.Rewriter.vb | 9 +- .../VisualBasicInferredMemberNameReducer.vb | 11 +- ...isualBasicMiscellaneousReducer.Rewriter.vb | 7 +- .../VisualBasicMiscellaneousReducer.vb | 12 +- .../VisualBasicNameReducer.Rewriter.vb | 11 +- .../{ => Reducers}/VisualBasicNameReducer.vb | 12 +- .../VisualBasicParenthesesReducer.Rewriter.vb | 8 +- .../VisualBasicParenthesesReducer.vb | 13 +- ...BasicVariableDeclaratorReducer.Rewriter.vb | 11 +- .../VisualBasicVariableDeclaratorReducer.vb | 12 +- 42 files changed, 364 insertions(+), 301 deletions(-) rename src/Workspaces/CSharp/Portable/Simplification/{AbstractCSharpReducer.AbstractExpressionRewriter.cs => Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs} (86%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/AbstractCSharpReducer.cs (72%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpCastReducer.Rewriter.cs (92%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpCastReducer.cs (89%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpEscapingReducer.Rewriter.cs (54%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpEscapingReducer.cs (96%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpExtensionMethodReducer.Rewriter.cs (73%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpExtensionMethodReducer.cs (94%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpInferredMemberNameReducer.Rewriter.cs (82%) mode change 100755 => 100644 rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpInferredMemberNameReducer.cs (91%) mode change 100755 => 100644 rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpMiscellaneousReducer.Rewriter.cs (80%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpMiscellaneousReducer.cs (95%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpNameReducer.Rewriter.cs (95%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpNameReducer.cs (90%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpParenthesesReducer.Rewriter.cs (73%) rename src/Workspaces/CSharp/Portable/Simplification/{ => Reducers}/CSharpParenthesesReducer.cs (91%) rename src/Workspaces/VisualBasic/Portable/Simplification/{AbstractVisualBasicSimplifier.AbstractExpressionRewriter.vb => Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb} (86%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/AbstractVisualBasicReducer.vb (95%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicCallReducer.Rewriter.vb (80%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicCallReducer.vb (85%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicCastReducer.Rewriter.vb (86%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicCastReducer.vb (91%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicEscapingReducer.Rewriter.vb (74%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicEscapingReducer.vb (96%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicExtensionMethodReducer.Rewriter.vb (78%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicExtensionMethodReducer.vb (93%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicInferredMemberNameReducer.Rewriter.vb (85%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicInferredMemberNameReducer.vb (90%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicMiscellaneousReducer.Rewriter.vb (87%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicMiscellaneousReducer.vb (93%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicNameReducer.Rewriter.vb (93%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicNameReducer.vb (87%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicParenthesesReducer.Rewriter.vb (74%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicParenthesesReducer.vb (52%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicVariableDeclaratorReducer.Rewriter.vb (71%) rename src/Workspaces/VisualBasic/Portable/Simplification/{ => Reducers}/VisualBasicVariableDeclaratorReducer.vb (86%) diff --git a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj index 2e41f200b1739..1edfa6cb05691 100644 --- a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj +++ b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj @@ -209,22 +209,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index c85c224bc1b65..276ac06c8ff00 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -3524,9 +3524,7 @@ public override SyntaxNode IfStatement(SyntaxNode condition, IEnumerable statements) - { - return SyntaxFactory.Block(AsStatementList(statements)); - } + => SyntaxFactory.Block(AsStatementList(statements)).WithAdditionalAnnotations(Simplifier.Annotation); private SyntaxList AsStatementList(IEnumerable nodes) { diff --git a/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs similarity index 86% rename from src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs index 4b7745d97e5c2..48bc70d426c05 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.AbstractExpressionRewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs @@ -5,34 +5,51 @@ using System.Diagnostics; using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal abstract partial class AbstractCSharpReducer { - protected abstract class AbstractExpressionRewriter : CSharpSyntaxRewriter, IExpressionRewriter + protected abstract class AbstractReductionRewriter : CSharpSyntaxRewriter, IReductionRewriter { - private readonly OptionSet _optionSet; - private readonly CancellationToken _cancellationToken; - private readonly HashSet _processedParentNodes; + private readonly ObjectPool _pool; + + private OptionSet _optionSet; + private CancellationToken _cancellationToken; private SemanticModel _semanticModel; - protected AbstractExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + public bool HasMoreWork { get; private set; } + + // can be used to simplify whole subtrees while just annotating one syntax node. + // This is e.g. useful in the name simplification, where a whole qualified name is annotated + protected bool alwaysSimplify; + + private HashSet _processedParentNodes = new HashSet(); + + protected AbstractReductionRewriter(ObjectPool pool) + => _pool = pool; + + public void Initialize(OptionSet optionSet, CancellationToken cancellationToken) { _optionSet = optionSet; _cancellationToken = cancellationToken; - _processedParentNodes = new HashSet(); } - public bool HasMoreWork { get; private set; } + public void Dispose() + { + _optionSet = null; + _cancellationToken = CancellationToken.None; + _processedParentNodes.Clear(); + _semanticModel = null; + HasMoreWork = false; + alwaysSimplify = false; - // can be used to simplify whole subtrees while just annotating one syntax node. - // This is e.g. useful in the name simplification, where a whole qualified name is annotated - protected bool alwaysSimplify; + _pool.Free(this); + } private static SyntaxNode GetParentNode(SyntaxNode node) { @@ -173,4 +190,4 @@ public SyntaxNodeOrToken VisitNodeOrToken(SyntaxNodeOrToken nodeOrToken, Semanti } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.cs similarity index 72% rename from src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.cs index 276840f49798b..c58f9b875fa78 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/AbstractCSharpReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.cs @@ -1,10 +1,14 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal abstract partial class AbstractCSharpReducer : AbstractReducer { + protected AbstractCSharpReducer(ObjectPool pool) : base(pool) + { + } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs similarity index 92% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs index 61142fd6bb734..ceecc0991547c 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs @@ -1,19 +1,18 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpCastReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { } @@ -69,4 +68,4 @@ public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node) } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.cs similarity index 89% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.cs index 09fb9da7ea087..848328cac71d2 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpCastReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.cs @@ -8,14 +8,17 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpCastReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpCastReducer() : base(s_pool) { - return new Rewriter(optionSet, cancellationToken); } private static readonly Func s_simplifyCast = SimplifyCast; diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpEscapingReducer.Rewriter.cs similarity index 54% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpEscapingReducer.Rewriter.cs index b4c07b6fd3750..9db3a35690159 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpEscapingReducer.Rewriter.cs @@ -1,22 +1,18 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; -using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpEscapingReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(CSharpEscapingReducer escapingSimplifierService, OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { - _escapingSimplifierService = escapingSimplifierService; } - private readonly CSharpEscapingReducer _escapingSimplifierService; - public override SyntaxToken VisitToken(SyntaxToken token) { var newToken = base.VisitToken(token); diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpEscapingReducer.cs similarity index 96% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpEscapingReducer.cs index b87d5a4b22cd9..b4473f26b5ddc 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpEscapingReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpEscapingReducer.cs @@ -8,14 +8,17 @@ using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpEscapingReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpEscapingReducer() : base(s_pool) { - return new Rewriter(this, optionSet, cancellationToken); } private static Func s_simplifyIdentifierToken = SimplifyIdentifierToken; @@ -132,4 +135,4 @@ private static SyntaxToken CreateNewIdentifierTokenFromToken(SyntaxToken origina : originalToken.CopyAnnotationsTo(SyntaxFactory.Identifier(originalToken.LeadingTrivia, SyntaxKind.IdentifierToken, unescapedText, originalToken.ValueText, originalToken.TrailingTrivia)); } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs similarity index 73% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs index 624e77dcee453..62f8dc0b68393 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs @@ -1,17 +1,16 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpExtensionMethodReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { } diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.cs similarity index 94% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.cs index 15de394df2cf7..b9913bc6bc7fa 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpExtensionMethodReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.cs @@ -8,14 +8,17 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpExtensionMethodReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpExtensionMethodReducer() : base(s_pool) { - return new Rewriter(optionSet, cancellationToken); } private static readonly Func s_simplifyExtensionMethod = SimplifyExtensionMethod; @@ -67,19 +70,19 @@ private static InvocationExpressionSyntax TryReduceExtensionMethod(InvocationExp { newMemberAccess = SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, expression, - ((MemberAccessExpressionSyntax)invocationExpressionNodeExpression).OperatorToken, + ((MemberAccessExpressionSyntax)invocationExpressionNodeExpression).OperatorToken, ((MemberAccessExpressionSyntax)invocationExpressionNodeExpression).Name); } else if (node.Expression.Kind() == SyntaxKind.IdentifierName) { newMemberAccess = SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, expression, + SyntaxKind.SimpleMemberAccessExpression, expression, (IdentifierNameSyntax)invocationExpressionNodeExpression.WithoutLeadingTrivia()); } else if (node.Expression.Kind() == SyntaxKind.GenericName) { newMemberAccess = SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, expression, + SyntaxKind.SimpleMemberAccessExpression, expression, (GenericNameSyntax)invocationExpressionNodeExpression.WithoutLeadingTrivia()); } else @@ -122,4 +125,4 @@ private static InvocationExpressionSyntax TryReduceExtensionMethod(InvocationExp return rewrittenNode; } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs old mode 100755 new mode 100644 similarity index 82% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs index f19da855592f0..b4c98521bca6c --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs @@ -1,18 +1,16 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpInferredMemberNameReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { } @@ -42,4 +40,4 @@ public override SyntaxNode VisitAnonymousObjectMemberDeclarator(AnonymousObjectM } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.cs old mode 100755 new mode 100644 similarity index 91% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.cs index 3983736cf0d41..9d60e373a2beb --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpInferredMemberNameReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { @@ -15,9 +16,11 @@ namespace Microsoft.CodeAnalysis.CSharp.Simplification /// internal partial class CSharpInferredMemberNameReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpInferredMemberNameReducer() : base(s_pool) { - return new Rewriter(optionSet, cancellationToken); } private static readonly Func s_simplifyTupleName = SimplifyTupleName; @@ -59,4 +62,4 @@ private static SyntaxNode SimplifyAnonymousTypeMemberName(AnonymousObjectMemberD return node.WithNameEquals(null).WithTriviaFrom(node); } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs similarity index 80% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs index c338efd9e1ccd..eab89aaac85ce 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs @@ -1,17 +1,16 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpMiscellaneousReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { } diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs similarity index 95% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs index 4d3c315c30b31..f6e0e23cb9915 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpMiscellaneousReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs @@ -8,14 +8,17 @@ using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpMiscellaneousReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpMiscellaneousReducer() : base(s_pool) { - return new Rewriter(optionSet, cancellationToken); } private static bool CanRemoveTypeFromParameter( @@ -101,4 +104,4 @@ private static SyntaxNode SimplifyParenthesizedLambdaExpression( return node; } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs similarity index 95% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs index 87e19ddd40d73..98d9e5d17389b 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs @@ -1,21 +1,19 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpNameReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { } @@ -201,4 +199,4 @@ public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node) } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs similarity index 90% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs index d9bc79dfeea30..5248fa24eaf57 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpNameReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs @@ -8,14 +8,17 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpNameReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpNameReducer() : base(s_pool) { - return new Rewriter(optionSet, cancellationToken); } private static readonly Func s_simplifyName = SimplifyName; @@ -54,4 +57,4 @@ private static SyntaxNode SimplifyName( return node.WithoutAnnotations(Simplifier.Annotation); } } -} +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesesReducer.Rewriter.cs similarity index 73% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesesReducer.Rewriter.cs index c20a0b1c4c015..995eac3b5c4a5 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesesReducer.Rewriter.cs @@ -1,17 +1,16 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpParenthesesReducer { - private class Rewriter : AbstractExpressionRewriter + private class Rewriter : AbstractReductionRewriter { - public Rewriter(OptionSet optionSet, CancellationToken cancellationToken) - : base(optionSet, cancellationToken) + public Rewriter(ObjectPool pool) + : base(pool) { } diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesesReducer.cs similarity index 91% rename from src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs rename to src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesesReducer.cs index 1e525ed1fe5e1..4f35546c51e9a 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpParenthesesReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesesReducer.cs @@ -8,14 +8,17 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpParenthesesReducer : AbstractCSharpReducer { - public override IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken) + private static readonly ObjectPool s_pool = new ObjectPool( + () => new Rewriter(s_pool)); + + public CSharpParenthesesReducer() : base(s_pool) { - return new Rewriter(optionSet, cancellationToken); } private static readonly Func s_simplifyParentheses = SimplifyParentheses; diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractReducer.IExpressionRewriter.cs b/src/Workspaces/Core/Portable/Simplification/AbstractReducer.IExpressionRewriter.cs index 8f8586fe1b8b2..c3e57a10aa5d5 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractReducer.IExpressionRewriter.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractReducer.IExpressionRewriter.cs @@ -1,14 +1,20 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Threading; +using Microsoft.CodeAnalysis.Options; + namespace Microsoft.CodeAnalysis.Simplification { internal abstract partial class AbstractReducer { - internal interface IExpressionRewriter + internal interface IReductionRewriter : IDisposable { + void Initialize(OptionSet optionSet, CancellationToken cancellationToken); + SyntaxNodeOrToken VisitNodeOrToken(SyntaxNodeOrToken nodeOrTokenToReduce, SemanticModel semanticModel, bool simplifyAllDescendants); bool HasMoreWork { get; } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractReducer.cs b/src/Workspaces/Core/Portable/Simplification/AbstractReducer.cs index a24784daeaa9e..94b1992234788 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractReducer.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractReducer.cs @@ -1,12 +1,17 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; -using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Simplification { internal abstract partial class AbstractReducer { - public abstract IExpressionRewriter CreateExpressionRewriter(OptionSet optionSet, CancellationToken cancellationToken); + private readonly ObjectPool _pool; + + protected AbstractReducer(ObjectPool pool) + => _pool = pool; + + public IReductionRewriter GetOrCreateRewriter() + => _pool.Allocate(); } -} +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index ac9b8cdb7a33e..96c5941cbbab3 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -163,7 +163,7 @@ private Task ReduceAsync( Document document, SyntaxNode root, ImmutableArray nodesAndTokensToReduce, - IEnumerable reducers, + ImmutableArray reducers, OptionSet optionSet, SemanticModel semanticModel, ConcurrentDictionary reducedNodesMap, @@ -178,89 +178,92 @@ private Task ReduceAsync( { var nodeOrTokenToReduce = nodesAndTokensToReduce[i]; simplifyTasks[i] = Task.Run(async () => - { - var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; - var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; - var semanticModelForReduce = semanticModel; - var currentNodeOrToken = nodeOrTokenToReduce.NodeOrToken; - var isNode = nodeOrToken.IsNode; - - foreach (var reducer in reducers) { - cancellationToken.ThrowIfCancellationRequested(); + var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; + var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; + var semanticModelForReduce = semanticModel; + var currentNodeOrToken = nodeOrTokenToReduce.NodeOrToken; + var isNode = nodeOrToken.IsNode; - var rewriter = reducer.CreateExpressionRewriter(optionSet, cancellationToken); - - do + foreach (var reducer in reducers) { - if (currentNodeOrToken.SyntaxTree != semanticModelForReduce.SyntaxTree) - { - // currentNodeOrToken was simplified either by a previous reducer or - // a previous iteration of the current reducer. - // Create a speculative semantic model for the simplified node for semantic queries. - - // Certain node kinds (expressions/statements) require non-null parent nodes during simplification. - // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. - if (currentNodeOrToken.Parent == null && - nodeOrToken.Parent != null && - (currentNodeOrToken.IsToken || - currentNodeOrToken.AsNode() is TExpressionSyntax || - currentNodeOrToken.AsNode() is TStatementSyntax || - currentNodeOrToken.AsNode() is TCrefSyntax)) - { - var annotation = new SyntaxAnnotation(); - currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); + cancellationToken.ThrowIfCancellationRequested(); - var replacedParent = isNode ? - nodeOrToken.Parent.ReplaceNode(nodeOrToken.AsNode(), currentNodeOrToken.AsNode()) : - nodeOrToken.Parent.ReplaceToken(nodeOrToken.AsToken(), currentNodeOrToken.AsToken()); - - currentNodeOrToken = replacedParent - .ChildNodesAndTokens() - .Single((c) => c.HasAnnotation(annotation)); - } + using (var rewriter = reducer.GetOrCreateRewriter()) + { + rewriter.Initialize(optionSet, cancellationToken); - if (isNode) + do { - var currentNode = currentNodeOrToken.AsNode(); - if (this.CanNodeBeSimplifiedWithoutSpeculation(nodeOrToken.AsNode())) - { - // Since this node cannot be speculated, we are replacing the Document with the changes and get a new SemanticModel - SyntaxAnnotation marker = new SyntaxAnnotation(); - var newRoot = root.ReplaceNode(nodeOrToken.AsNode(), currentNode.WithAdditionalAnnotations(marker)); - var newDocument = document.WithSyntaxRoot(newRoot); - semanticModelForReduce = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - newRoot = await semanticModelForReduce.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - currentNodeOrToken = newRoot.DescendantNodes().Single((c) => c.HasAnnotation(marker)); - } - else + if (currentNodeOrToken.SyntaxTree != semanticModelForReduce.SyntaxTree) { - // Create speculative semantic model for simplified node. - semanticModelForReduce = GetSpeculativeSemanticModel(ref currentNode, semanticModel, nodeOrToken.AsNode()); - currentNodeOrToken = currentNode; + // currentNodeOrToken was simplified either by a previous reducer or + // a previous iteration of the current reducer. + // Create a speculative semantic model for the simplified node for semantic queries. + + // Certain node kinds (expressions/statements) require non-null parent nodes during simplification. + // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. + if (currentNodeOrToken.Parent == null && + nodeOrToken.Parent != null && + (currentNodeOrToken.IsToken || + currentNodeOrToken.AsNode() is TExpressionSyntax || + currentNodeOrToken.AsNode() is TStatementSyntax || + currentNodeOrToken.AsNode() is TCrefSyntax)) + { + var annotation = new SyntaxAnnotation(); + currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); + + var replacedParent = isNode ? + nodeOrToken.Parent.ReplaceNode(nodeOrToken.AsNode(), currentNodeOrToken.AsNode()) : + nodeOrToken.Parent.ReplaceToken(nodeOrToken.AsToken(), currentNodeOrToken.AsToken()); + + currentNodeOrToken = replacedParent + .ChildNodesAndTokens() + .Single((c) => c.HasAnnotation(annotation)); + } + + if (isNode) + { + var currentNode = currentNodeOrToken.AsNode(); + if (this.CanNodeBeSimplifiedWithoutSpeculation(nodeOrToken.AsNode())) + { + // Since this node cannot be speculated, we are replacing the Document with the changes and get a new SemanticModel + SyntaxAnnotation marker = new SyntaxAnnotation(); + var newRoot = root.ReplaceNode(nodeOrToken.AsNode(), currentNode.WithAdditionalAnnotations(marker)); + var newDocument = document.WithSyntaxRoot(newRoot); + semanticModelForReduce = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + newRoot = await semanticModelForReduce.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + currentNodeOrToken = newRoot.DescendantNodes().Single((c) => c.HasAnnotation(marker)); + } + else + { + // Create speculative semantic model for simplified node. + semanticModelForReduce = GetSpeculativeSemanticModel(ref currentNode, semanticModel, nodeOrToken.AsNode()); + currentNodeOrToken = currentNode; + } + } } + + // Reduce the current node or token. + currentNodeOrToken = rewriter.VisitNodeOrToken(currentNodeOrToken, semanticModelForReduce, simplifyAllDescendants); } + while (rewriter.HasMoreWork); } - - // Reduce the current node or token. - currentNodeOrToken = rewriter.VisitNodeOrToken(currentNodeOrToken, semanticModelForReduce, simplifyAllDescendants); } - while (rewriter.HasMoreWork); - } - // If nodeOrToken was simplified, add it to the appropriate dictionary of replaced nodes/tokens. - if (currentNodeOrToken != nodeOrToken) - { - if (isNode) - { - reducedNodesMap[nodeOrToken.AsNode()] = currentNodeOrToken.AsNode(); - } - else + // If nodeOrToken was simplified, add it to the appropriate dictionary of replaced nodes/tokens. + if (currentNodeOrToken != nodeOrToken) { - reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); + if (isNode) + { + reducedNodesMap[nodeOrToken.AsNode()] = currentNodeOrToken.AsNode(); + } + else + { + reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); + } } - } - }, cancellationToken); + }, cancellationToken); } return Task.WhenAll(simplifyTasks); diff --git a/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj b/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj index db2cf36f964f2..f220e38764338 100644 --- a/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj +++ b/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj @@ -184,29 +184,29 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + + diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicSimplifier.AbstractExpressionRewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb similarity index 86% rename from src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicSimplifier.AbstractExpressionRewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb index 919887d6b4b07..b3495c1852870 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicSimplifier.AbstractExpressionRewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb @@ -7,25 +7,43 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend MustInherit Class AbstractVisualBasicReducer - Friend MustInherit Class AbstractExpressionRewriter + Friend MustInherit Class AbstractReductionRewriter Inherits VisualBasicSyntaxRewriter - Implements IExpressionRewriter + Implements IReductionRewriter + + Private ReadOnly _pool As ObjectPool(Of IReductionRewriter) + + Protected CancellationToken As CancellationToken + Private _simplificationOptions As OptionSet + + Private ReadOnly _processedParentNodes As HashSet(Of SyntaxNode) = New HashSet(Of SyntaxNode)() + Private _semanticModel As SemanticModel - Protected ReadOnly CancellationToken As CancellationToken - Private ReadOnly _processedParentNodes As HashSet(Of SyntaxNode) Private _hasMoreWork As Boolean Protected _alwaysSimplify As Boolean - Private ReadOnly _simplificationOptions As OptionSet - Private _semanticModel As SemanticModel - Protected Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New() - Me.CancellationToken = cancellationToken - Me._simplificationOptions = optionSet - _processedParentNodes = New HashSet(Of SyntaxNode) + Protected Sub New(pool As ObjectPool(Of IReductionRewriter)) + _pool = pool + End Sub + + Public Sub Initialize(optionSet As OptionSet, cancellationToken As CancellationToken) Implements IReductionRewriter.Initialize + _simplificationOptions = optionSet + cancellationToken = cancellationToken + End Sub + + Public Sub Dispose() Implements IDisposable.Dispose + _simplificationOptions = Nothing + CancellationToken = CancellationToken.None + _processedParentNodes.Clear() + _semanticModel = Nothing + + _hasMoreWork = False + _alwaysSimplify = False + + _pool.Free(Me) End Sub - Public ReadOnly Property HasMoreWork As Boolean Implements IExpressionRewriter.HasMoreWork + Public ReadOnly Property HasMoreWork As Boolean Implements IReductionRewriter.HasMoreWork Get Return _hasMoreWork End Get @@ -140,8 +158,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SimplifyNode(statement, newNode, GetParentNode(statement), simplifier) End Function - Public Function VisitNodeOrToken(nodeOrToken As SyntaxNodeOrToken, semanticModel As SemanticModel, simplifyAllDescendants As Boolean) As SyntaxNodeOrToken Implements IExpressionRewriter.VisitNodeOrToken - _semanticModel = DirectCast(semanticModel, semanticModel) + Public Function VisitNodeOrToken(nodeOrToken As SyntaxNodeOrToken, semanticModel As SemanticModel, simplifyAllDescendants As Boolean) As SyntaxNodeOrToken Implements IReductionRewriter.VisitNodeOrToken + _semanticModel = DirectCast(semanticModel, SemanticModel) _alwaysSimplify = simplifyAllDescendants _hasMoreWork = False _processedParentNodes.Clear() @@ -186,4 +204,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification End Function End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.vb similarity index 95% rename from src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.vb index 0212ce261c265..6b8868ddc0975 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/AbstractVisualBasicReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.vb @@ -9,6 +9,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend MustInherit Class AbstractVisualBasicReducer Inherits AbstractReducer + Protected Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) + End Sub + Protected Shared ReadOnly s_reduceParentheses As Func(Of ParenthesizedExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf ReduceParentheses Protected Shared Function ReduceParentheses( diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCallReducer.Rewriter.vb similarity index 80% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCallReducer.Rewriter.vb index 5b1aef9915c0d..ecc4d1bdeee88 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCallReducer.Rewriter.vb @@ -1,16 +1,14 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicCallReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitCallStatement(node As CallStatementSyntax) As SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCallReducer.vb similarity index 85% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCallReducer.vb index 3f8be8e1277f0..57ecc9e254ccc 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCallReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCallReducer.vb @@ -9,9 +9,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicCallReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared ReadOnly s_simplifyCallStatement As Func(Of CallStatementSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyCallStatement @@ -36,6 +39,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification ' We don't know how to simplify this. Return callStatement End Function - End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.Rewriter.vb similarity index 86% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.Rewriter.vb index cd876badbc161..583f01700aa68 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.Rewriter.vb @@ -1,16 +1,14 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicCastReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitCTypeExpression(node As CTypeExpressionSyntax) As SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb similarity index 91% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb index 5b968bf017042..b2adc52b8021d 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb @@ -9,9 +9,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicCastReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Overloads Shared Function SimplifyCast( castNode As ExpressionSyntax, diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicEscapingReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicEscapingReducer.Rewriter.vb similarity index 74% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicEscapingReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicEscapingReducer.Rewriter.vb index a92543df6e1c7..1e050b52d51cd 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicEscapingReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicEscapingReducer.Rewriter.vb @@ -1,15 +1,14 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicEscapingReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitToken(token As SyntaxToken) As SyntaxToken @@ -20,7 +19,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification newToken:=MyBase.VisitToken(token), simplifyFunc:=AddressOf TryUnescapeToken) End Function - End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicEscapingReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicEscapingReducer.vb similarity index 96% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicEscapingReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicEscapingReducer.vb index 96f0f0080b6cf..d1639ce90cedb 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicEscapingReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicEscapingReducer.vb @@ -3,17 +3,18 @@ Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Options -Imports Microsoft.CodeAnalysis.Simplification -Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicEscapingReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New VisualBasicEscapingReducer.Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared Function TryUnescapeToken(identifier As SyntaxToken, semanticModel As SemanticModel, optionSet As OptionSet, cancellationToken As CancellationToken) As SyntaxToken If Not identifier.IsBracketed Then @@ -216,4 +217,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return False End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicExtensionMethodReducer.Rewriter.vb similarity index 78% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicExtensionMethodReducer.Rewriter.vb index 7c82c5718df76..3d1365ce118ba 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicExtensionMethodReducer.Rewriter.vb @@ -1,16 +1,15 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicExtensionMethodReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitInvocationExpression(node As InvocationExpressionSyntax) As SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicExtensionMethodReducer.vb similarity index 93% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicExtensionMethodReducer.vb index 4117cc5c82c1a..c9cb45c1eb8e1 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicExtensionMethodReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicExtensionMethodReducer.vb @@ -8,9 +8,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicExtensionMethodReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared ReadOnly s_simplifyInvocationExpression As Func(Of InvocationExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyInvocationExpression @@ -61,6 +64,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return rewrittenNode End Function - End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicInferredMemberNameReducer.Rewriter.vb similarity index 85% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicInferredMemberNameReducer.Rewriter.vb index 84b9c68f0ee09..c85451f744a54 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicInferredMemberNameReducer.Rewriter.vb @@ -1,17 +1,16 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicInferredMemberNameReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitSimpleArgument(node As SimpleArgumentSyntax) As SyntaxNode @@ -41,4 +40,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification End Function End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicInferredMemberNameReducer.vb similarity index 90% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicInferredMemberNameReducer.vb index 97f1ff8b4ccf5..1bc3da7b7cda7 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicInferredMemberNameReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicInferredMemberNameReducer.vb @@ -14,9 +14,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicInferredMemberNameReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New VisualBasicInferredMemberNameReducer.Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared ReadOnly s_simplifyTupleName As Func(Of SimpleArgumentSyntax, SemanticModel, OptionSet, CancellationToken, SimpleArgumentSyntax) = AddressOf SimplifyTupleName @@ -56,4 +59,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return SyntaxFactory.InferredFieldInitializer(node.Expression).WithTriviaFrom(node) End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicMiscellaneousReducer.Rewriter.vb similarity index 87% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicMiscellaneousReducer.Rewriter.vb index 05ae22615b503..873dc98961023 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicMiscellaneousReducer.Rewriter.vb @@ -1,16 +1,15 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicMiscellaneousReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitInvocationExpression(node As InvocationExpressionSyntax) As SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicMiscellaneousReducer.vb similarity index 93% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicMiscellaneousReducer.vb index 4589357439bd1..08973c38d1ba0 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicMiscellaneousReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicMiscellaneousReducer.vb @@ -9,9 +9,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicMiscellaneousReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared ReadOnly s_simplifyParameter As Func(Of ParameterSyntax, SemanticModel, OptionSet, CancellationToken, ParameterSyntax) = AddressOf SimplifyParameter @@ -75,6 +78,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification ' We don't know how to simplify this. Return objectCreationExpression End Function - End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.Rewriter.vb similarity index 93% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.Rewriter.vb index 0e6d8abba873b..12705b4346bf0 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.Rewriter.vb @@ -1,17 +1,15 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicNameReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitGenericName(node As GenericNameSyntax) As SyntaxNode @@ -109,7 +107,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return result End Function - End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb similarity index 87% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb index c31bb23a9e74e..e705d2f9efc85 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicNameReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb @@ -12,9 +12,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicNameReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared ReadOnly s_simplifyName As Func(Of ExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyName @@ -39,6 +42,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification node = node.CopyAnnotationsTo(replacementNode).WithAdditionalAnnotations(Formatter.Annotation) Return node.WithoutAnnotations(Simplifier.Annotation) End Function - End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicParenthesesReducer.Rewriter.vb similarity index 74% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicParenthesesReducer.Rewriter.vb index 7766e4a29ba78..fcec66136a35c 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicParenthesesReducer.Rewriter.vb @@ -1,16 +1,14 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicParenthesesReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitParenthesizedExpression(node As ParenthesizedExpressionSyntax) As SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicParenthesesReducer.vb similarity index 52% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicParenthesesReducer.vb index 59cce7f2fe698..6a04e9558ca2c 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicParenthesesReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicParenthesesReducer.vb @@ -1,15 +1,14 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Threading -Imports Microsoft.CodeAnalysis.Options - Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicParenthesesReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + Public Sub New() + MyBase.New(s_pool) + End Sub End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicVariableDeclaratorReducer.Rewriter.vb similarity index 71% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicVariableDeclaratorReducer.Rewriter.vb index 3236a101873a0..5a36bd6869ca3 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.Rewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicVariableDeclaratorReducer.Rewriter.vb @@ -1,17 +1,14 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Imports System.Threading -Imports Microsoft.CodeAnalysis.Options Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicVariableDeclaratorReducer Private Class Rewriter - Inherits AbstractExpressionRewriter + Inherits AbstractReductionRewriter - Public Sub New(optionSet As OptionSet, cancellationToken As CancellationToken) - MyBase.New(optionSet, cancellationToken) + Public Sub New(pool As ObjectPool(Of IReductionRewriter)) + MyBase.New(pool) End Sub Public Overrides Function VisitVariableDeclarator(node As VariableDeclaratorSyntax) As SyntaxNode @@ -23,4 +20,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification End Function End Class End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicVariableDeclaratorReducer.vb similarity index 86% rename from src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb rename to src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicVariableDeclaratorReducer.vb index a9204fae01301..87506e172b302 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicVariableDeclaratorReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicVariableDeclaratorReducer.vb @@ -11,9 +11,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicVariableDeclaratorReducer Inherits AbstractVisualBasicReducer - Public Overrides Function CreateExpressionRewriter(optionSet As OptionSet, cancellationToken As CancellationToken) As IExpressionRewriter - Return New Rewriter(optionSet, cancellationToken) - End Function + Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) = + New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool)) + + Public Sub New() + MyBase.New(s_pool) + End Sub Private Shared ReadOnly s_simplifyVariableDeclarator As Func(Of VariableDeclaratorSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyVariableDeclarator @@ -39,5 +42,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return replacementNode.WithoutAnnotations(Simplifier.Annotation) End Function End Class -End Namespace - +End Namespace \ No newline at end of file From be8d91b6b7e08e5b1a026de636ab8e9f38291758 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 18:49:36 -0700 Subject: [PATCH 118/284] Fix null ref. --- .../Portable/Simplification/Reducers/VisualBasicCastReducer.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb index b2adc52b8021d..131f28659c2bd 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicCastReducer.vb @@ -32,7 +32,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return resultNode End Function - Private Shared ReadOnly s_simplifyCast As Func(Of CastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = s_simplifyCast + Private Shared ReadOnly s_simplifyCast As Func(Of CastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyCast Private Overloads Shared Function SimplifyCast( node As CastExpressionSyntax, From ea768e052c5083e17920adb2b73836c70f9c9217 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 18:49:36 -0700 Subject: [PATCH 119/284] Fix null ref. --- .../Portable/Simplification/VisualBasicCastReducer.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb index 5b968bf017042..ceb8f74b2150f 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicCastReducer.vb @@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return resultNode End Function - Private Shared ReadOnly s_simplifyCast As Func(Of CastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = s_simplifyCast + Private Shared ReadOnly s_simplifyCast As Func(Of CastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyCast Private Overloads Shared Function SimplifyCast( node As CastExpressionSyntax, From db8ed0ffb9655a7f07e26ea8f47798e70bf314b2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 18:52:43 -0700 Subject: [PATCH 120/284] Revert change. --- .../CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 276ac06c8ff00..c85c224bc1b65 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -3524,7 +3524,9 @@ public override SyntaxNode IfStatement(SyntaxNode condition, IEnumerable statements) - => SyntaxFactory.Block(AsStatementList(statements)).WithAdditionalAnnotations(Simplifier.Annotation); + { + return SyntaxFactory.Block(AsStatementList(statements)); + } private SyntaxList AsStatementList(IEnumerable nodes) { From 451641b3ab82b091736e3129b5cf88bd970e2106 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 10 May 2017 21:20:03 -0700 Subject: [PATCH 121/284] Respect user settings around 'Blocks' when adding parameter null checks. --- .../AddParameterCheckTests.cs | 29 +++++++ .../CodeGeneration/CSharpSyntaxGenerator.cs | 4 +- .../CSharpMiscellaneousReducer.Rewriter.cs | 9 ++ .../Reducers/CSharpMiscellaneousReducer.cs | 86 +++++++++++++------ 4 files changed, 100 insertions(+), 28 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs index b700377c7cebf..7241d5f7c5821 100644 --- a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs @@ -755,5 +755,34 @@ static void Main(String bar) CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, CodeStyleOptions.FalseWithSuggestionEnforcement))); } + + [WorkItem(19172, "https://github.com/dotnet/roslyn/issues/19172")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] + public async Task TestPreferNoBlock() + { + await TestInRegularAndScript1Async( +@" +using System; + +class C +{ + public C([||]string s) + { + } +}", +@" +using System; + +class C +{ + public C(string s) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + } +}", ignoreTrivia: false, + parameters: new TestParameters(options: + Option(CSharpCodeStyleOptions.PreferBraces, CodeStyleOptions.FalseWithNoneEnforcement))); + } } } \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index c85c224bc1b65..276ac06c8ff00 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -3524,9 +3524,7 @@ public override SyntaxNode IfStatement(SyntaxNode condition, IEnumerable statements) - { - return SyntaxFactory.Block(AsStatementList(statements)); - } + => SyntaxFactory.Block(AsStatementList(statements)).WithAdditionalAnnotations(Simplifier.Annotation); private SyntaxList AsStatementList(IEnumerable nodes) { diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs index eab89aaac85ce..c8a1d670ce039 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs @@ -31,6 +31,15 @@ public override SyntaxNode VisitParenthesizedLambdaExpression(ParenthesizedLambd parentNode: node.Parent, simplifier: s_simplifyParenthesizedLambdaExpression); } + + public override SyntaxNode VisitBlock(BlockSyntax node) + { + return SimplifyNode( + node, + newNode: base.VisitBlock(node), + parentNode: node.Parent, + simplifier: s_simplifyBlock); + } } } } \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs index f6e0e23cb9915..931a90684ed35 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; @@ -22,20 +23,20 @@ public CSharpMiscellaneousReducer() : base(s_pool) } private static bool CanRemoveTypeFromParameter( - SyntaxNode node, + ParameterSyntax parameterSyntax, SemanticModel semanticModel, CancellationToken cancellationToken) { // We reduce any the parameters that are contained inside ParameterList - if (node != null && node.IsParentKind(SyntaxKind.ParameterList) && node.Parent.IsParentKind(SyntaxKind.ParenthesizedLambdaExpression)) + if (parameterSyntax.IsParentKind(SyntaxKind.ParameterList) && + parameterSyntax.Parent.IsParentKind(SyntaxKind.ParenthesizedLambdaExpression)) { - var parameterSyntax = (ParameterSyntax)node; if (parameterSyntax.Type != null) { var annotation = new SyntaxAnnotation(); var newParameterSyntax = parameterSyntax.WithType(null).WithAdditionalAnnotations(annotation); - var oldLambda = node.FirstAncestorOrSelf(); + var oldLambda = parameterSyntax.FirstAncestorOrSelf(); var newLambda = oldLambda.ReplaceNode(parameterSyntax, newParameterSyntax); var speculationAnalyzer = new SpeculationAnalyzer(oldLambda, newLambda, semanticModel, cancellationToken); newParameterSyntax = (ParameterSyntax)speculationAnalyzer.ReplacedExpression.GetAnnotatedNodesAndTokens(annotation).First(); @@ -54,17 +55,17 @@ private static bool CanRemoveTypeFromParameter( return false; } - private static Func s_simplifyParameter = SimplifyParameter; + private static Func s_simplifyParameter = SimplifyParameter; private static SyntaxNode SimplifyParameter( - SyntaxNode node, + ParameterSyntax node, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken) { if (CanRemoveTypeFromParameter(node, semanticModel, cancellationToken)) { - var newParameterSyntax = ((ParameterSyntax)node).WithType(null); + var newParameterSyntax = node.WithType(null); newParameterSyntax = SimplificationHelpers.CopyAnnotations(node, newParameterSyntax).WithoutAnnotations(Simplifier.Annotation); return newParameterSyntax; } @@ -72,36 +73,71 @@ private static SyntaxNode SimplifyParameter( return node; } - private static readonly Func s_simplifyParenthesizedLambdaExpression = SimplifyParenthesizedLambdaExpression; + private static readonly Func s_simplifyParenthesizedLambdaExpression = SimplifyParenthesizedLambdaExpression; private static SyntaxNode SimplifyParenthesizedLambdaExpression( - SyntaxNode node, + ParenthesizedLambdaExpressionSyntax parenthesizedLambda, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken) { - if (node != null && node is ParenthesizedLambdaExpressionSyntax) + if (parenthesizedLambda.ParameterList != null && + parenthesizedLambda.ParameterList.Parameters.Count == 1) { - var parenthesizedLambda = (ParenthesizedLambdaExpressionSyntax)node; - if (parenthesizedLambda.ParameterList != null && - parenthesizedLambda.ParameterList.Parameters.Count == 1) + var parameter = parenthesizedLambda.ParameterList.Parameters.First(); + if (CanRemoveTypeFromParameter(parameter, semanticModel, cancellationToken)) { - var parameter = parenthesizedLambda.ParameterList.Parameters.First(); - if (CanRemoveTypeFromParameter(parameter, semanticModel, cancellationToken)) - { - var newParameterSyntax = parameter.WithType(null); - var newSimpleLambda = SyntaxFactory.SimpleLambdaExpression( - parenthesizedLambda.AsyncKeyword, - newParameterSyntax.WithTrailingTrivia(parenthesizedLambda.ParameterList.GetTrailingTrivia()), - parenthesizedLambda.ArrowToken, - parenthesizedLambda.Body); - - return SimplificationHelpers.CopyAnnotations(parenthesizedLambda, newSimpleLambda).WithoutAnnotations(Simplifier.Annotation); - } + var newParameterSyntax = parameter.WithType(null); + var newSimpleLambda = SyntaxFactory.SimpleLambdaExpression( + parenthesizedLambda.AsyncKeyword, + newParameterSyntax.WithTrailingTrivia(parenthesizedLambda.ParameterList.GetTrailingTrivia()), + parenthesizedLambda.ArrowToken, + parenthesizedLambda.Body); + + return SimplificationHelpers.CopyAnnotations(parenthesizedLambda, newSimpleLambda).WithoutAnnotations(Simplifier.Annotation); } } + return parenthesizedLambda; + } + private static readonly Func s_simplifyBlock = SimplifyBlock; + + private static SyntaxNode SimplifyBlock( + BlockSyntax node, + SemanticModel semanticModel, + OptionSet optionSet, + CancellationToken cancellationToken) + { + if (node.Statements.Count == 1 && + CanHaveEmbeddedStatement(node.Parent) && + !optionSet.GetOption(CSharpCodeStyleOptions.PreferBraces).Value) + { + return node.Statements[0]; + } + return node; } + + private static bool CanHaveEmbeddedStatement(SyntaxNode node) + { + if (node != null) + { + switch (node.Kind()) + { + case SyntaxKind.IfStatement: + case SyntaxKind.ElseClause: + case SyntaxKind.ForStatement: + case SyntaxKind.ForEachStatement: + case SyntaxKind.ForEachVariableStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.UsingStatement: + case SyntaxKind.LockStatement: + return true; + } + } + + return false; + } } } \ No newline at end of file From 841044b0dfb54d9a1af317bd87cb5710150e6001 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Wed, 10 May 2017 22:47:56 -0700 Subject: [PATCH 122/284] Share pattern-matching temps when they are the same type. (#19378) Fixes #19280 --- .../Portable/Binder/PatternSwitchBinder.cs | 10 +- .../Binder/SubsumptionDiagnosticBuilder.cs | 9 +- .../CSharp/Portable/BoundTree/DecisionTree.cs | 30 - .../Portable/BoundTree/DecisionTreeBuilder.cs | 50 +- .../LocalRewriter_PatternSwitchStatement.cs | 19 +- .../CSharp/Test/Emit/CodeGen/SwitchTests.cs | 1169 +++++++++++------ .../CSharp/Test/Emit/PDB/PDBTests.cs | 292 ++-- .../Test/ExpressionCompiler/LocalsTests.cs | 250 ++-- 8 files changed, 1092 insertions(+), 737 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs index aa86e537ce024..b5551b2f52be0 100644 --- a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs @@ -64,7 +64,7 @@ internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatement BoundPatternSwitchLabel defaultLabel; bool isComplete; ImmutableArray switchSections = - BindPatternSwitchSections(boundSwitchExpression, node.Sections, originalBinder, out defaultLabel, out isComplete, diagnostics); + BindPatternSwitchSections(originalBinder, out defaultLabel, out isComplete, diagnostics); var locals = GetDeclaredLocalsForScope(node); var functions = GetDeclaredLocalFunctionsForScope(node); return new BoundPatternSwitchStatement( @@ -101,8 +101,6 @@ internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabel /// the decision tree. /// private ImmutableArray BindPatternSwitchSections( - BoundExpression boundSwitchExpression, - SyntaxList sections, Binder originalBinder, out BoundPatternSwitchLabel defaultLabel, out bool isComplete, @@ -115,11 +113,11 @@ private ImmutableArray BindPatternSwitchSections( // Bind match sections var boundPatternSwitchSectionsBuilder = ArrayBuilder.GetInstance(); - SubsumptionDiagnosticBuilder subsumption = new SubsumptionDiagnosticBuilder(ContainingMemberOrLambda, this.Conversions, boundSwitchExpression); - foreach (var sectionSyntax in sections) + SubsumptionDiagnosticBuilder subsumption = new SubsumptionDiagnosticBuilder(ContainingMemberOrLambda, SwitchSyntax, this.Conversions, SwitchGoverningExpression); + foreach (var sectionSyntax in SwitchSyntax.Sections) { boundPatternSwitchSectionsBuilder.Add(BindPatternSwitchSection( - boundSwitchExpression, sectionSyntax, originalBinder, ref defaultLabel, ref someValueMatched, subsumption, diagnostics)); + SwitchGoverningExpression, sectionSyntax, originalBinder, ref defaultLabel, ref someValueMatched, subsumption, diagnostics)); } isComplete = defaultLabel != null || subsumption.IsComplete || someValueMatched; diff --git a/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs b/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs index f1695a0bda40c..4496196c67e24 100644 --- a/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs @@ -20,11 +20,12 @@ internal class SubsumptionDiagnosticBuilder : DecisionTreeBuilder private readonly DecisionTree _subsumptionTree; internal SubsumptionDiagnosticBuilder(Symbol enclosingSymbol, - Conversions conversions, - BoundExpression expression) - : base(enclosingSymbol, conversions) + SyntaxNode syntax, + Conversions conversions, + BoundExpression expression) + : base(enclosingSymbol, syntax, conversions) { - _subsumptionTree = DecisionTree.Create(expression, expression.Type, enclosingSymbol); + _subsumptionTree = CreateEmptyDecisionTree(expression); } /// diff --git a/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs b/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs index e030bc16cb558..c6d95ca94f611 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs @@ -78,36 +78,6 @@ public DecisionTree(BoundExpression expression, TypeSymbol type, LocalSymbol tem Debug.Assert(this.Type != null); } - /// - /// Create a fresh decision tree for the given input expression of the given type. - /// - public static DecisionTree Create(BoundExpression expression, TypeSymbol type, Symbol enclosingSymbol) - { - Debug.Assert(expression.Type == type); - LocalSymbol temp = null; - if (expression.ConstantValue == null) - { - // Unless it is a constant, the decision tree acts on a copy of the input expression. - // We create a temp to represent that copy. Lowering will assign into this temp. - temp = new SynthesizedLocal(enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, expression.Syntax, false, RefKind.None); - expression = new BoundLocal(expression.Syntax, temp, null, type); - } - - if (type.CanContainNull() || type.SpecialType == SpecialType.None) - { - // We need the ByType decision tree to separate null from non-null values. - // Note that, for the purpose of the decision tree (and subsumption), we - // ignore the fact that the input may be a constant, and therefore always - // or never null. - return new ByType(expression, type, temp); - } - else - { - // If it is a (e.g. builtin) value type, we can switch on its (constant) values. - return new ByValue(expression, type, temp); - } - } - /// /// A decision tree node that branches based on (1) whether the input value is null, (2) the runtime /// type of the input expression, and finally (3) a default decision tree if nothing in the previous diff --git a/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs b/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs index 6e72bb9566008..703dffcf48a03 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs @@ -26,17 +26,63 @@ internal abstract class DecisionTreeBuilder protected readonly Symbol _enclosingSymbol; protected readonly Conversions _conversions; protected HashSet _useSiteDiagnostics = new HashSet(); + private Dictionary localByType = new Dictionary(); protected DecisionTreeBuilder( Symbol enclosingSymbol, + SyntaxNode syntax, Conversions conversions) { this._enclosingSymbol = enclosingSymbol; + this.Syntax = syntax; this._conversions = conversions; } protected SyntaxNode Syntax { private get; set; } + private LocalSymbol PatternMatchingTemp(TypeSymbol type) + { + LocalSymbol temp; + if (!localByType.TryGetValue(type, out temp)) + { + temp = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax); + localByType.Add(type, temp); + } + + return temp; + } + + /// + /// Create a fresh decision tree for the given input expression of the given type. + /// + protected DecisionTree CreateEmptyDecisionTree(BoundExpression expression) + { + var type = expression.Type; + + LocalSymbol temp = null; + if (expression.ConstantValue == null) + { + // Unless it is a constant, the decision tree acts on a copy of the input expression. + // We create a temp to represent that copy. Lowering will assign into this temp. + temp = PatternMatchingTemp(type); + expression = new BoundLocal(expression.Syntax, temp, null, type); + } + + if (type.CanContainNull() || type.SpecialType == SpecialType.None) + { + // We need the ByType decision tree to separate null from non-null values. + // Note that, for the purpose of the decision tree (and subsumption), we + // ignore the fact that the input may be a constant, and therefore always + // or never null. + return new DecisionTree.ByType(expression, type, temp); + } + else + { + // If it is a (e.g. builtin) value type, we can switch on its (constant) values. + return new DecisionTree.ByValue(expression, type, temp); + } + } + protected DecisionTree AddToDecisionTree(DecisionTree decisionTree, SyntaxNode sectionSyntax, BoundPatternSwitchLabel label) { var pattern = label.Pattern; @@ -237,7 +283,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern if (forType == null) { var type = value.Value.Type; - var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); + var localSymbol = PatternMatchingTemp(type); var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); byType.TypeAndDecision.Add(new KeyValuePair(value.Value.Type, forType)); @@ -338,7 +384,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci if (result == null) { - var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); + var localSymbol = PatternMatchingTemp(type); var expression = new BoundLocal(Syntax, localSymbol, null, type); result = makeDecision(expression, type); Debug.Assert(result.Temp == null); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs index f54cbd709d3bb..c8f353222bbab 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs @@ -38,7 +38,7 @@ private class PatternSwitchLocalRewriter : DecisionTreeBuilder private ArrayBuilder _loweredDecisionTree = ArrayBuilder.GetInstance(); private PatternSwitchLocalRewriter(LocalRewriter localRewriter, BoundPatternSwitchStatement node) - : base(localRewriter._factory.CurrentMethod, localRewriter._factory.Compilation.Conversions) + : base(localRewriter._factory.CurrentMethod, node.Syntax, localRewriter._factory.Compilation.Conversions) { this._localRewriter = localRewriter; this._factory = localRewriter._factory; @@ -59,15 +59,9 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) var expression = _localRewriter.VisitExpression(node.Expression); var result = ArrayBuilder.GetInstance(); - // if the expression is "too complex", we copy it to a temp. - LocalSymbol initialTemp = null; if (expression.ConstantValue == null) { - initialTemp = _factory.SynthesizedLocal(expression.Type, expression.Syntax); - result.Add(_factory.Assignment(_factory.Local(initialTemp), expression)); - expression = _factory.Local(initialTemp); - - // EnC: We need to insert a hidden sequence point to handle function remapping in case + // EnC: We need to insert a hidden sequence point to handle function remapping in case // the containing method is edited while methods invoked in the expression are being executed. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { @@ -85,7 +79,6 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) } // at this point the end of result is unreachable. - _declaredTemps.AddOptional(initialTemp); _declaredTemps.AddRange(node.InnerLocals); // output the sections of code @@ -148,7 +141,7 @@ private DecisionTree LowerToDecisionTree( BoundExpression loweredExpression, BoundPatternSwitchStatement node) { - var loweredDecisionTree = DecisionTree.Create(loweredExpression, loweredExpression.Type, _enclosingSymbol); + var loweredDecisionTree = CreateEmptyDecisionTree(loweredExpression); BoundPatternSwitchLabel defaultLabel = null; SyntaxNode defaultSection = null; foreach (var section in node.SwitchSections) @@ -223,15 +216,11 @@ private void LowerDecisionTree(BoundExpression expression, DecisionTree decision _loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, convertedExpression)); } + // If the temp is not yet in the declared temp set, add it now if (_declaredTempSet.Add(decisionTree.Temp)) { _declaredTemps.Add(decisionTree.Temp); } - else - { - // we should only attempt to declare each temp once. - throw ExceptionUtilities.Unreachable; - } } switch (decisionTree.Kind) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs index 39c1c7f56c87b..3472ef5b26ed6 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs @@ -7735,46 +7735,43 @@ .locals init (int? V_0, expectedOutput: "null1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 69 (0x45) + // Code size 65 (0x41) .maxstack 1 .locals init (int? V_0, int V_1, - int? V_2, - int V_3, //i - int? V_4, - int? V_5) + int V_2, //i + int? V_3, + int? V_4) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloca.s V_0 - IL_000b: call ""bool int?.HasValue.get"" - IL_0010: brtrue.s IL_0014 - IL_0012: br.s IL_0037 - IL_0014: ldloc.0 - IL_0015: stloc.s V_5 - IL_0017: ldloca.s V_5 - IL_0019: call ""int int?.GetValueOrDefault()"" - IL_001e: stloc.1 - IL_001f: ldloca.s V_5 - IL_0021: call ""bool int?.HasValue.get"" - IL_0026: brfalse.s IL_002a + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloca.s V_0 + IL_0007: call ""bool int?.HasValue.get"" + IL_000c: brtrue.s IL_0010 + IL_000e: br.s IL_0033 + IL_0010: ldloc.0 + IL_0011: stloc.s V_4 + IL_0013: ldloca.s V_4 + IL_0015: call ""int int?.GetValueOrDefault()"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_4 + IL_001d: call ""bool int?.HasValue.get"" + IL_0022: brfalse.s IL_0026 + IL_0024: br.s IL_0026 + IL_0026: ldloc.1 + IL_0027: stloc.2 IL_0028: br.s IL_002a - IL_002a: ldloc.1 - IL_002b: stloc.3 - IL_002c: br.s IL_002e - IL_002e: ldloc.3 - IL_002f: call ""void System.Console.Write(int)"" - IL_0034: nop - IL_0035: br.s IL_0044 - IL_0037: ldstr ""null"" - IL_003c: call ""void System.Console.Write(string)"" - IL_0041: nop - IL_0042: br.s IL_0044 - IL_0044: ret + IL_002a: ldloc.2 + IL_002b: call ""void System.Console.Write(int)"" + IL_0030: nop + IL_0031: br.s IL_0040 + IL_0033: ldstr ""null"" + IL_0038: call ""void System.Console.Write(string)"" + IL_003d: nop + IL_003e: br.s IL_0040 + IL_0040: ret }" ); } @@ -7821,32 +7818,29 @@ .locals init (System.IComparable V_0) expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 32 (0x20) + // Code size 28 (0x1c) .maxstack 1 .locals init (int V_0, System.IComparable V_1, - int V_2, - System.IComparable V_3, //i - int V_4) + System.IComparable V_2, //i + int V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: box ""int"" - IL_000f: stloc.1 + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: box ""int"" + IL_000b: stloc.1 + IL_000c: br.s IL_000e + IL_000e: ldloc.1 + IL_000f: stloc.2 IL_0010: br.s IL_0012 - IL_0012: ldloc.1 - IL_0013: stloc.3 - IL_0014: br.s IL_0016 - IL_0016: ldloc.3 - IL_0017: call ""void System.Console.Write(object)"" - IL_001c: nop - IL_001d: br.s IL_001f - IL_001f: ret + IL_0012: ldloc.2 + IL_0013: call ""void System.Console.Write(object)"" + IL_0018: nop + IL_0019: br.s IL_001b + IL_001b: ret }" ); } @@ -7900,37 +7894,34 @@ .locals init (int? V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 45 (0x2d) + // Code size 41 (0x29) .maxstack 1 .locals init (int? V_0, System.IComparable V_1, - int? V_2, - System.IComparable V_3, //i - int? V_4) + System.IComparable V_2, //i + int? V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloca.s V_0 - IL_000b: call ""bool int?.HasValue.get"" - IL_0010: brtrue.s IL_0014 - IL_0012: br.s IL_001d - IL_0014: ldloc.0 - IL_0015: box ""int?"" - IL_001a: stloc.1 - IL_001b: br.s IL_001f - IL_001d: br.s IL_002c - IL_001f: ldloc.1 - IL_0020: stloc.3 - IL_0021: br.s IL_0023 - IL_0023: ldloc.3 - IL_0024: call ""void System.Console.Write(object)"" - IL_0029: nop - IL_002a: br.s IL_002c - IL_002c: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloca.s V_0 + IL_0007: call ""bool int?.HasValue.get"" + IL_000c: brtrue.s IL_0010 + IL_000e: br.s IL_0019 + IL_0010: ldloc.0 + IL_0011: box ""int?"" + IL_0016: stloc.1 + IL_0017: br.s IL_001b + IL_0019: br.s IL_0028 + IL_001b: ldloc.1 + IL_001c: stloc.2 + IL_001d: br.s IL_001f + IL_001f: ldloc.2 + IL_0020: call ""void System.Console.Write(object)"" + IL_0025: nop + IL_0026: br.s IL_0028 + IL_0028: ret }" ); } @@ -7992,44 +7983,41 @@ .locals init (object V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 62 (0x3e) + // Code size 58 (0x3a) .maxstack 1 .locals init (object V_0, int V_1, - object V_2, - int V_3, //i - object V_4, - int? V_5) + int V_2, //i + object V_3, + int? V_4) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_002e - IL_000e: ldloc.0 - IL_000f: isinst ""int?"" - IL_0014: unbox.any ""int?"" - IL_0019: stloc.s V_5 - IL_001b: ldloca.s V_5 - IL_001d: call ""int int?.GetValueOrDefault()"" - IL_0022: stloc.1 - IL_0023: ldloca.s V_5 - IL_0025: call ""bool int?.HasValue.get"" - IL_002a: brfalse.s IL_002e - IL_002c: br.s IL_0030 - IL_002e: br.s IL_003d - IL_0030: ldloc.1 - IL_0031: stloc.3 - IL_0032: br.s IL_0034 - IL_0034: ldloc.3 - IL_0035: call ""void System.Console.Write(int)"" - IL_003a: nop - IL_003b: br.s IL_003d - IL_003d: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: brtrue.s IL_000a + IL_0008: br.s IL_002a + IL_000a: ldloc.0 + IL_000b: isinst ""int?"" + IL_0010: unbox.any ""int?"" + IL_0015: stloc.s V_4 + IL_0017: ldloca.s V_4 + IL_0019: call ""int int?.GetValueOrDefault()"" + IL_001e: stloc.1 + IL_001f: ldloca.s V_4 + IL_0021: call ""bool int?.HasValue.get"" + IL_0026: brfalse.s IL_002a + IL_0028: br.s IL_002c + IL_002a: br.s IL_0039 + IL_002c: ldloc.1 + IL_002d: stloc.2 + IL_002e: br.s IL_0030 + IL_0030: ldloc.2 + IL_0031: call ""void System.Console.Write(int)"" + IL_0036: nop + IL_0037: br.s IL_0039 + IL_0039: ret }" ); } @@ -8101,52 +8089,49 @@ .locals init (object V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 75 (0x4b) + // Code size 71 (0x47) .maxstack 2 .locals init (object V_0, T V_1, - object V_2, - T V_3, //i + T V_2, //i + object V_3, object V_4, - object V_5, - T V_6) + T V_5) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0036 - IL_000e: ldloc.0 - IL_000f: stloc.s V_5 - IL_0011: ldloc.s V_5 - IL_0013: isinst ""T"" - IL_0018: ldnull - IL_0019: cgt.un - IL_001b: dup - IL_001c: brtrue.s IL_002a - IL_001e: ldloca.s V_6 - IL_0020: initobj ""T"" - IL_0026: ldloc.s V_6 - IL_0028: br.s IL_0031 - IL_002a: ldloc.s V_5 - IL_002c: unbox.any ""T"" - IL_0031: stloc.1 - IL_0032: brfalse.s IL_0036 - IL_0034: br.s IL_0038 - IL_0036: br.s IL_004a - IL_0038: ldloc.1 - IL_0039: stloc.3 - IL_003a: br.s IL_003c - IL_003c: ldloc.3 - IL_003d: box ""T"" - IL_0042: call ""void System.Console.Write(object)"" - IL_0047: nop - IL_0048: br.s IL_004a - IL_004a: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: brtrue.s IL_000a + IL_0008: br.s IL_0032 + IL_000a: ldloc.0 + IL_000b: stloc.s V_4 + IL_000d: ldloc.s V_4 + IL_000f: isinst ""T"" + IL_0014: ldnull + IL_0015: cgt.un + IL_0017: dup + IL_0018: brtrue.s IL_0026 + IL_001a: ldloca.s V_5 + IL_001c: initobj ""T"" + IL_0022: ldloc.s V_5 + IL_0024: br.s IL_002d + IL_0026: ldloc.s V_4 + IL_0028: unbox.any ""T"" + IL_002d: stloc.1 + IL_002e: brfalse.s IL_0032 + IL_0030: br.s IL_0034 + IL_0032: br.s IL_0046 + IL_0034: ldloc.1 + IL_0035: stloc.2 + IL_0036: br.s IL_0038 + IL_0038: ldloc.2 + IL_0039: box ""T"" + IL_003e: call ""void System.Console.Write(object)"" + IL_0043: nop + IL_0044: br.s IL_0046 + IL_0046: ret }" ); } @@ -8218,52 +8203,49 @@ .locals init (System.IComparable V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 75 (0x4b) + // Code size 71 (0x47) .maxstack 2 .locals init (System.IComparable V_0, T V_1, - System.IComparable V_2, - T V_3, //i - System.IComparable V_4, - object V_5, - T V_6) + T V_2, //i + System.IComparable V_3, + object V_4, + T V_5) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0036 - IL_000e: ldloc.0 - IL_000f: stloc.s V_5 - IL_0011: ldloc.s V_5 - IL_0013: isinst ""T"" - IL_0018: ldnull - IL_0019: cgt.un - IL_001b: dup - IL_001c: brtrue.s IL_002a - IL_001e: ldloca.s V_6 - IL_0020: initobj ""T"" - IL_0026: ldloc.s V_6 - IL_0028: br.s IL_0031 - IL_002a: ldloc.s V_5 - IL_002c: unbox.any ""T"" - IL_0031: stloc.1 - IL_0032: brfalse.s IL_0036 - IL_0034: br.s IL_0038 - IL_0036: br.s IL_004a - IL_0038: ldloc.1 - IL_0039: stloc.3 - IL_003a: br.s IL_003c - IL_003c: ldloc.3 - IL_003d: box ""T"" - IL_0042: call ""void System.Console.Write(object)"" - IL_0047: nop - IL_0048: br.s IL_004a - IL_004a: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: brtrue.s IL_000a + IL_0008: br.s IL_0032 + IL_000a: ldloc.0 + IL_000b: stloc.s V_4 + IL_000d: ldloc.s V_4 + IL_000f: isinst ""T"" + IL_0014: ldnull + IL_0015: cgt.un + IL_0017: dup + IL_0018: brtrue.s IL_0026 + IL_001a: ldloca.s V_5 + IL_001c: initobj ""T"" + IL_0022: ldloc.s V_5 + IL_0024: br.s IL_002d + IL_0026: ldloc.s V_4 + IL_0028: unbox.any ""T"" + IL_002d: stloc.1 + IL_002e: brfalse.s IL_0032 + IL_0030: br.s IL_0034 + IL_0032: br.s IL_0046 + IL_0034: ldloc.1 + IL_0035: stloc.2 + IL_0036: br.s IL_0038 + IL_0038: ldloc.2 + IL_0039: box ""T"" + IL_003e: call ""void System.Console.Write(object)"" + IL_0043: nop + IL_0044: br.s IL_0046 + IL_0046: ret }" ); } @@ -8337,54 +8319,51 @@ .locals init (U V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 85 (0x55) + // Code size 81 (0x51) .maxstack 2 .locals init (U V_0, T V_1, - U V_2, - T V_3, //i - U V_4, - object V_5, - T V_6) + T V_2, //i + U V_3, + object V_4, + T V_5) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: box ""U"" - IL_000f: brtrue.s IL_0013 - IL_0011: br.s IL_0040 - IL_0013: ldloc.0 - IL_0014: box ""U"" - IL_0019: stloc.s V_5 - IL_001b: ldloc.s V_5 - IL_001d: isinst ""T"" - IL_0022: ldnull - IL_0023: cgt.un - IL_0025: dup - IL_0026: brtrue.s IL_0034 - IL_0028: ldloca.s V_6 - IL_002a: initobj ""T"" - IL_0030: ldloc.s V_6 - IL_0032: br.s IL_003b - IL_0034: ldloc.s V_5 - IL_0036: unbox.any ""T"" - IL_003b: stloc.1 - IL_003c: brfalse.s IL_0040 - IL_003e: br.s IL_0042 - IL_0040: br.s IL_0054 - IL_0042: ldloc.1 - IL_0043: stloc.3 - IL_0044: br.s IL_0046 - IL_0046: ldloc.3 - IL_0047: box ""T"" - IL_004c: call ""void System.Console.Write(object)"" - IL_0051: nop - IL_0052: br.s IL_0054 - IL_0054: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: box ""U"" + IL_000b: brtrue.s IL_000f + IL_000d: br.s IL_003c + IL_000f: ldloc.0 + IL_0010: box ""U"" + IL_0015: stloc.s V_4 + IL_0017: ldloc.s V_4 + IL_0019: isinst ""T"" + IL_001e: ldnull + IL_001f: cgt.un + IL_0021: dup + IL_0022: brtrue.s IL_0030 + IL_0024: ldloca.s V_5 + IL_0026: initobj ""T"" + IL_002c: ldloc.s V_5 + IL_002e: br.s IL_0037 + IL_0030: ldloc.s V_4 + IL_0032: unbox.any ""T"" + IL_0037: stloc.1 + IL_0038: brfalse.s IL_003c + IL_003a: br.s IL_003e + IL_003c: br.s IL_0050 + IL_003e: ldloc.1 + IL_003f: stloc.2 + IL_0040: br.s IL_0042 + IL_0042: ldloc.2 + IL_0043: box ""T"" + IL_0048: call ""void System.Console.Write(object)"" + IL_004d: nop + IL_004e: br.s IL_0050 + IL_0050: ret }" ); } @@ -8598,57 +8577,54 @@ .locals init (T V_0, //t ); compVerifier.VerifyIL("Program.M2", @"{ - // Code size 82 (0x52) + // Code size 78 (0x4e) .maxstack 2 .locals init (System.ValueType V_0, T V_1, - System.ValueType V_2, - T V_3, //t - System.ValueType V_4, - object V_5, - T V_6, - T V_7) + T V_2, //t + System.ValueType V_3, + object V_4, + T V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0036 - IL_000e: ldloc.0 - IL_000f: stloc.s V_5 - IL_0011: ldloc.s V_5 - IL_0013: isinst ""T"" - IL_0018: ldnull - IL_0019: cgt.un - IL_001b: dup - IL_001c: brtrue.s IL_002a - IL_001e: ldloca.s V_6 - IL_0020: initobj ""T"" - IL_0026: ldloc.s V_6 - IL_0028: br.s IL_0031 - IL_002a: ldloc.s V_5 - IL_002c: unbox.any ""T"" - IL_0031: stloc.1 - IL_0032: brfalse.s IL_0036 - IL_0034: br.s IL_0038 - IL_0036: br.s IL_0041 - IL_0038: ldloc.1 - IL_0039: stloc.3 - IL_003a: br.s IL_003c - IL_003c: ldloc.3 - IL_003d: stloc.s V_7 - IL_003f: br.s IL_004f - IL_0041: ldloca.s V_6 - IL_0043: initobj ""T"" - IL_0049: ldloc.s V_6 - IL_004b: stloc.s V_7 - IL_004d: br.s IL_004f - IL_004f: ldloc.s V_7 - IL_0051: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: brtrue.s IL_000a + IL_0008: br.s IL_0032 + IL_000a: ldloc.0 + IL_000b: stloc.s V_4 + IL_000d: ldloc.s V_4 + IL_000f: isinst ""T"" + IL_0014: ldnull + IL_0015: cgt.un + IL_0017: dup + IL_0018: brtrue.s IL_0026 + IL_001a: ldloca.s V_5 + IL_001c: initobj ""T"" + IL_0022: ldloc.s V_5 + IL_0024: br.s IL_002d + IL_0026: ldloc.s V_4 + IL_0028: unbox.any ""T"" + IL_002d: stloc.1 + IL_002e: brfalse.s IL_0032 + IL_0030: br.s IL_0034 + IL_0032: br.s IL_003d + IL_0034: ldloc.1 + IL_0035: stloc.2 + IL_0036: br.s IL_0038 + IL_0038: ldloc.2 + IL_0039: stloc.s V_6 + IL_003b: br.s IL_004b + IL_003d: ldloca.s V_5 + IL_003f: initobj ""T"" + IL_0045: ldloc.s V_5 + IL_0047: stloc.s V_6 + IL_0049: br.s IL_004b + IL_004b: ldloc.s V_6 + IL_004d: ret }" ); } @@ -8780,50 +8756,47 @@ .locals init (int V_0, //t ); compVerifier.VerifyIL("Program.M2", @"{ - // Code size 75 (0x4b) + // Code size 71 (0x47) .maxstack 1 .locals init (T V_0, int V_1, - T V_2, - int V_3, //t - T V_4, - int? V_5, - int V_6) + int V_2, //t + T V_3, + int? V_4, + int V_5) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloc.2 - IL_0004: stloc.s V_4 - IL_0006: ldloc.s V_4 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: box ""T"" - IL_000f: brtrue.s IL_0013 - IL_0011: br.s IL_0038 - IL_0013: ldloc.0 - IL_0014: box ""T"" - IL_0019: isinst ""int?"" - IL_001e: unbox.any ""int?"" - IL_0023: stloc.s V_5 - IL_0025: ldloca.s V_5 - IL_0027: call ""int int?.GetValueOrDefault()"" - IL_002c: stloc.1 - IL_002d: ldloca.s V_5 - IL_002f: call ""bool int?.HasValue.get"" - IL_0034: brfalse.s IL_0038 - IL_0036: br.s IL_003a - IL_0038: br.s IL_0043 - IL_003a: ldloc.1 - IL_003b: stloc.3 - IL_003c: br.s IL_003e - IL_003e: ldloc.3 - IL_003f: stloc.s V_6 - IL_0041: br.s IL_0048 - IL_0043: ldc.i4.0 - IL_0044: stloc.s V_6 - IL_0046: br.s IL_0048 - IL_0048: ldloc.s V_6 - IL_004a: ret + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: box ""T"" + IL_000b: brtrue.s IL_000f + IL_000d: br.s IL_0034 + IL_000f: ldloc.0 + IL_0010: box ""T"" + IL_0015: isinst ""int?"" + IL_001a: unbox.any ""int?"" + IL_001f: stloc.s V_4 + IL_0021: ldloca.s V_4 + IL_0023: call ""int int?.GetValueOrDefault()"" + IL_0028: stloc.1 + IL_0029: ldloca.s V_4 + IL_002b: call ""bool int?.HasValue.get"" + IL_0030: brfalse.s IL_0034 + IL_0032: br.s IL_0036 + IL_0034: br.s IL_003f + IL_0036: ldloc.1 + IL_0037: stloc.2 + IL_0038: br.s IL_003a + IL_003a: ldloc.2 + IL_003b: stloc.s V_5 + IL_003d: br.s IL_0044 + IL_003f: ldc.i4.0 + IL_0040: stloc.s V_5 + IL_0042: br.s IL_0044 + IL_0044: ldloc.s V_5 + IL_0046: ret }" ); } @@ -9125,81 +9098,497 @@ public static string M2(object o) compVerifier.VerifyDiagnostics(); compVerifier.VerifyIL("Program.M2", @"{ - // Code size 154 (0x9a) + // Code size 150 (0x96) .maxstack 2 .locals init (object V_0, Generic.Color V_1, Generic.Color V_2, - object V_3, - Generic.Color V_4, //c - object V_5, - Generic.Color? V_6, - Generic.Color? V_7, - int V_8, - string V_9) + Generic.Color V_3, //c + object V_4, + Generic.Color? V_5, + Generic.Color? V_6, + int V_7, + string V_8) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.3 - IL_0003: ldloc.3 - IL_0004: stloc.s V_5 - IL_0006: ldloc.s V_5 - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0060 - IL_000e: ldloc.0 - IL_000f: isinst ""Generic.Color?"" - IL_0014: unbox.any ""Generic.Color?"" - IL_0019: stloc.s V_6 - IL_001b: ldloca.s V_6 - IL_001d: call ""Generic.Color Generic.Color?.GetValueOrDefault()"" - IL_0022: stloc.1 - IL_0023: ldloca.s V_6 - IL_0025: call ""bool Generic.Color?.HasValue.get"" - IL_002a: brfalse.s IL_002e - IL_002c: br.s IL_0062 - IL_002e: ldloc.0 - IL_002f: isinst ""Generic.Color?"" - IL_0034: unbox.any ""Generic.Color?"" - IL_0039: stloc.s V_7 - IL_003b: ldloca.s V_7 - IL_003d: call ""Generic.Color Generic.Color?.GetValueOrDefault()"" - IL_0042: stloc.2 + IL_0002: stloc.s V_4 + IL_0004: ldloc.s V_4 + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brtrue.s IL_000c + IL_000a: br.s IL_005e + IL_000c: ldloc.0 + IL_000d: isinst ""Generic.Color?"" + IL_0012: unbox.any ""Generic.Color?"" + IL_0017: stloc.s V_5 + IL_0019: ldloca.s V_5 + IL_001b: call ""Generic.Color Generic.Color?.GetValueOrDefault()"" + IL_0020: stloc.1 + IL_0021: ldloca.s V_5 + IL_0023: call ""bool Generic.Color?.HasValue.get"" + IL_0028: brfalse.s IL_002c + IL_002a: br.s IL_0060 + IL_002c: ldloc.0 + IL_002d: isinst ""Generic.Color?"" + IL_0032: unbox.any ""Generic.Color?"" + IL_0037: stloc.s V_6 + IL_0039: ldloca.s V_6 + IL_003b: call ""Generic.Color Generic.Color?.GetValueOrDefault()"" + IL_0040: stloc.2 + IL_0041: ldloca.s V_6 + IL_0043: call ""bool Generic.Color?.HasValue.get"" + IL_0048: brfalse.s IL_005e + IL_004a: ldloc.2 + IL_004b: stloc.s V_7 + IL_004d: ldloc.s V_7 + IL_004f: brfalse.s IL_005a + IL_0051: br.s IL_0053 + IL_0053: ldloc.s V_7 + IL_0055: ldc.i4.1 + IL_0056: beq.s IL_005c + IL_0058: br.s IL_005e + IL_005a: br.s IL_0078 + IL_005c: br.s IL_0081 + IL_005e: br.s IL_008a + IL_0060: ldloc.1 + IL_0061: stloc.3 + IL_0062: br.s IL_0064 + IL_0064: ldstr ""Generic.Color."" + IL_0069: ldloc.3 + IL_006a: box ""Generic.Color"" + IL_006f: call ""string string.Concat(object, object)"" + IL_0074: stloc.s V_8 + IL_0076: br.s IL_0093 + IL_0078: ldstr ""Generic.Color.Red"" + IL_007d: stloc.s V_8 + IL_007f: br.s IL_0093 + IL_0081: ldstr ""Generic.Color.Blue"" + IL_0086: stloc.s V_8 + IL_0088: br.s IL_0093 + IL_008a: ldstr ""None"" + IL_008f: stloc.s V_8 + IL_0091: br.s IL_0093 + IL_0093: ldloc.s V_8 + IL_0095: ret +}" + ); + } + + [Fact, WorkItem(19280, "https://github.com/dotnet/roslyn/issues/19280")] + public void ShareLikeKindedTemps_01() + { + var source = @"using System; + +public class Program +{ + public static void Main() + { + } + static bool b = false; + public static void M(object o) + { + switch (o) + { + case int i when b: break; + case var _ when b: break; + case int i when b: break; + case var _ when b: break; + case int i when b: break; + case var _ when b: break; + case int i when b: break; + case var _ when b: break; + } + } +}"; + var compVerifier = CompileAndVerify(source, + options: TestOptions.ReleaseDll.WithOutputKind(OutputKind.ConsoleApplication), + expectedOutput: ""); + compVerifier.VerifyIL("Program.M", +@"{ + // Code size 203 (0xcb) + .maxstack 1 + .locals init (object V_0, + int V_1, + int? V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: brfalse IL_0094 + IL_0008: ldloc.0 + IL_0009: isinst ""int?"" + IL_000e: unbox.any ""int?"" + IL_0013: stloc.2 + IL_0014: ldloca.s V_2 + IL_0016: call ""int int?.GetValueOrDefault()"" + IL_001b: stloc.1 + IL_001c: ldloca.s V_2 + IL_001e: call ""bool int?.HasValue.get"" + IL_0023: brfalse.s IL_0094 + IL_0025: br.s IL_008d + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_00a4 + IL_002a: ldloc.0 + IL_002b: isinst ""int?"" + IL_0030: unbox.any ""int?"" + IL_0035: stloc.2 + IL_0036: ldloca.s V_2 + IL_0038: call ""int int?.GetValueOrDefault()"" + IL_003d: stloc.1 + IL_003e: ldloca.s V_2 + IL_0040: call ""bool int?.HasValue.get"" + IL_0045: brfalse.s IL_00a4 + IL_0047: br.s IL_009d + IL_0049: ldloc.0 + IL_004a: brfalse.s IL_00b4 + IL_004c: ldloc.0 + IL_004d: isinst ""int?"" + IL_0052: unbox.any ""int?"" + IL_0057: stloc.2 + IL_0058: ldloca.s V_2 + IL_005a: call ""int int?.GetValueOrDefault()"" + IL_005f: stloc.1 + IL_0060: ldloca.s V_2 + IL_0062: call ""bool int?.HasValue.get"" + IL_0067: brfalse.s IL_00b4 + IL_0069: br.s IL_00ad + IL_006b: ldloc.0 + IL_006c: brfalse.s IL_00c4 + IL_006e: ldloc.0 + IL_006f: isinst ""int?"" + IL_0074: unbox.any ""int?"" + IL_0079: stloc.2 + IL_007a: ldloca.s V_2 + IL_007c: call ""int int?.GetValueOrDefault()"" + IL_0081: stloc.1 + IL_0082: ldloca.s V_2 + IL_0084: call ""bool int?.HasValue.get"" + IL_0089: brfalse.s IL_00c4 + IL_008b: br.s IL_00bd + IL_008d: ldsfld ""bool Program.b"" + IL_0092: brtrue.s IL_00ca + IL_0094: ldsfld ""bool Program.b"" + IL_0099: brtrue.s IL_00ca + IL_009b: br.s IL_0027 + IL_009d: ldsfld ""bool Program.b"" + IL_00a2: brtrue.s IL_00ca + IL_00a4: ldsfld ""bool Program.b"" + IL_00a9: brtrue.s IL_00ca + IL_00ab: br.s IL_0049 + IL_00ad: ldsfld ""bool Program.b"" + IL_00b2: brtrue.s IL_00ca + IL_00b4: ldsfld ""bool Program.b"" + IL_00b9: brtrue.s IL_00ca + IL_00bb: br.s IL_006b + IL_00bd: ldsfld ""bool Program.b"" + IL_00c2: brtrue.s IL_00ca + IL_00c4: ldsfld ""bool Program.b"" + IL_00c9: pop + IL_00ca: ret +}" + ); + compVerifier = CompileAndVerify(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + expectedOutput: ""); + compVerifier.VerifyIL("Program.M", +@"{ + // Code size 276 (0x114) + .maxstack 1 + .locals init (object V_0, + int V_1, + int V_2, //i + int V_3, //i + int V_4, //i + int V_5, //i + object V_6, + int? V_7) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.s V_6 + IL_0004: ldloc.s V_6 + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brtrue.s IL_000c + IL_000a: br.s IL_002c + IL_000c: ldloc.0 + IL_000d: isinst ""int?"" + IL_0012: unbox.any ""int?"" + IL_0017: stloc.s V_7 + IL_0019: ldloca.s V_7 + IL_001b: call ""int int?.GetValueOrDefault()"" + IL_0020: stloc.1 + IL_0021: ldloca.s V_7 + IL_0023: call ""bool int?.HasValue.get"" + IL_0028: brfalse.s IL_002c + IL_002a: br.s IL_00a8 + IL_002c: br IL_00b8 + IL_0031: ldloc.0 + IL_0032: brtrue.s IL_0036 + IL_0034: br.s IL_0056 + IL_0036: ldloc.0 + IL_0037: isinst ""int?"" + IL_003c: unbox.any ""int?"" + IL_0041: stloc.s V_7 IL_0043: ldloca.s V_7 - IL_0045: call ""bool Generic.Color?.HasValue.get"" - IL_004a: brfalse.s IL_0060 - IL_004c: ldloc.2 - IL_004d: stloc.s V_8 - IL_004f: ldloc.s V_8 - IL_0051: brfalse.s IL_005c - IL_0053: br.s IL_0055 - IL_0055: ldloc.s V_8 - IL_0057: ldc.i4.1 - IL_0058: beq.s IL_005e - IL_005a: br.s IL_0060 - IL_005c: br.s IL_007c - IL_005e: br.s IL_0085 - IL_0060: br.s IL_008e - IL_0062: ldloc.1 - IL_0063: stloc.s V_4 - IL_0065: br.s IL_0067 - IL_0067: ldstr ""Generic.Color."" - IL_006c: ldloc.s V_4 - IL_006e: box ""Generic.Color"" - IL_0073: call ""string string.Concat(object, object)"" - IL_0078: stloc.s V_9 - IL_007a: br.s IL_0097 - IL_007c: ldstr ""Generic.Color.Red"" - IL_0081: stloc.s V_9 - IL_0083: br.s IL_0097 - IL_0085: ldstr ""Generic.Color.Blue"" - IL_008a: stloc.s V_9 - IL_008c: br.s IL_0097 - IL_008e: ldstr ""None"" - IL_0093: stloc.s V_9 - IL_0095: br.s IL_0097 - IL_0097: ldloc.s V_9 - IL_0099: ret + IL_0045: call ""int int?.GetValueOrDefault()"" + IL_004a: stloc.1 + IL_004b: ldloca.s V_7 + IL_004d: call ""bool int?.HasValue.get"" + IL_0052: brfalse.s IL_0056 + IL_0054: br.s IL_00c6 + IL_0056: br.s IL_00d3 + IL_0058: ldloc.0 + IL_0059: brtrue.s IL_005d + IL_005b: br.s IL_007d + IL_005d: ldloc.0 + IL_005e: isinst ""int?"" + IL_0063: unbox.any ""int?"" + IL_0068: stloc.s V_7 + IL_006a: ldloca.s V_7 + IL_006c: call ""int int?.GetValueOrDefault()"" + IL_0071: stloc.1 + IL_0072: ldloca.s V_7 + IL_0074: call ""bool int?.HasValue.get"" + IL_0079: brfalse.s IL_007d + IL_007b: br.s IL_00e1 + IL_007d: br.s IL_00ef + IL_007f: ldloc.0 + IL_0080: brtrue.s IL_0084 + IL_0082: br.s IL_00a4 + IL_0084: ldloc.0 + IL_0085: isinst ""int?"" + IL_008a: unbox.any ""int?"" + IL_008f: stloc.s V_7 + IL_0091: ldloca.s V_7 + IL_0093: call ""int int?.GetValueOrDefault()"" + IL_0098: stloc.1 + IL_0099: ldloca.s V_7 + IL_009b: call ""bool int?.HasValue.get"" + IL_00a0: brfalse.s IL_00a4 + IL_00a2: br.s IL_00fa + IL_00a4: br.s IL_0108 + IL_00a6: br.s IL_0113 + IL_00a8: ldloc.1 + IL_00a9: stloc.2 + IL_00aa: ldsfld ""bool Program.b"" + IL_00af: brtrue.s IL_00b6 + IL_00b1: br IL_002c + IL_00b6: br.s IL_0113 + IL_00b8: ldsfld ""bool Program.b"" + IL_00bd: brtrue.s IL_00c4 + IL_00bf: br IL_0031 + IL_00c4: br.s IL_0113 + IL_00c6: ldloc.1 + IL_00c7: stloc.3 + IL_00c8: ldsfld ""bool Program.b"" + IL_00cd: brtrue.s IL_00d1 + IL_00cf: br.s IL_0056 + IL_00d1: br.s IL_0113 + IL_00d3: ldsfld ""bool Program.b"" + IL_00d8: brtrue.s IL_00df + IL_00da: br IL_0058 + IL_00df: br.s IL_0113 + IL_00e1: ldloc.1 + IL_00e2: stloc.s V_4 + IL_00e4: ldsfld ""bool Program.b"" + IL_00e9: brtrue.s IL_00ed + IL_00eb: br.s IL_007d + IL_00ed: br.s IL_0113 + IL_00ef: ldsfld ""bool Program.b"" + IL_00f4: brtrue.s IL_00f8 + IL_00f6: br.s IL_007f + IL_00f8: br.s IL_0113 + IL_00fa: ldloc.1 + IL_00fb: stloc.s V_5 + IL_00fd: ldsfld ""bool Program.b"" + IL_0102: brtrue.s IL_0106 + IL_0104: br.s IL_00a4 + IL_0106: br.s IL_0113 + IL_0108: ldsfld ""bool Program.b"" + IL_010d: brtrue.s IL_0111 + IL_010f: br.s IL_00a6 + IL_0111: br.s IL_0113 + IL_0113: ret +}" + ); + compVerifier.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + [Fact, WorkItem(19280, "https://github.com/dotnet/roslyn/issues/19280")] + public void TestSignificanceOfDynamicVersusObjectAndTupleNamesInUniquenessOfPatternMatchingTemps() + { + var source = +@"using System; +public class Generic +{ +} +class Program +{ + public static void Main(string[] args) + { + var g = new Generic(); + M2(g, true, false, false); + M2(g, false, true, false); + M2(g, false, false, true); + } + public static void M2(object o, bool b1, bool b2, bool b3) + { + switch (o) + { + case Generic g when b1: Console.Write(""a""); break; + case var _ when b2: Console.Write(""b""); break; + case Generic g when b3: Console.Write(""c""); break; + } + } +} +"; + var compilation = CreateStandardCompilation(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }) + .VerifyDiagnostics(); + var compVerifier = CompileAndVerify(compilation, expectedOutput: "abc"); + compVerifier.VerifyIL("Program.M2", +@"{ + // Code size 105 (0x69) + .maxstack 2 + .locals init (object V_0, + Generic V_1, + Generic V_2, + Generic V_3, //g + Generic V_4, //g + object V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.s V_5 + IL_0004: ldloc.s V_5 + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brtrue.s IL_000c + IL_000a: br.s IL_0018 + IL_000c: ldloc.0 + IL_000d: isinst ""Generic"" + IL_0012: dup + IL_0013: stloc.1 + IL_0014: brfalse.s IL_0018 + IL_0016: br.s IL_002d + IL_0018: br.s IL_0041 + IL_001a: ldloc.0 + IL_001b: brtrue.s IL_001f + IL_001d: br.s IL_002b + IL_001f: ldloc.0 + IL_0020: isinst ""Generic"" + IL_0025: dup + IL_0026: stloc.2 + IL_0027: brfalse.s IL_002b + IL_0029: br.s IL_0053 + IL_002b: br.s IL_0068 + IL_002d: ldloc.1 + IL_002e: stloc.3 + IL_002f: ldarg.1 + IL_0030: brtrue.s IL_0034 + IL_0032: br.s IL_0018 + IL_0034: ldstr ""a"" + IL_0039: call ""void System.Console.Write(string)"" + IL_003e: nop + IL_003f: br.s IL_0068 + IL_0041: ldarg.2 + IL_0042: brtrue.s IL_0046 + IL_0044: br.s IL_001a + IL_0046: ldstr ""b"" + IL_004b: call ""void System.Console.Write(string)"" + IL_0050: nop + IL_0051: br.s IL_0068 + IL_0053: ldloc.2 + IL_0054: stloc.s V_4 + IL_0056: ldarg.3 + IL_0057: brtrue.s IL_005b + IL_0059: br.s IL_002b + IL_005b: ldstr ""c"" + IL_0060: call ""void System.Console.Write(string)"" + IL_0065: nop + IL_0066: br.s IL_0068 + IL_0068: ret }" ); } diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index fd713ce68d083..2d38589676392 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -2850,7 +2850,6 @@ class Student : Person { public double GPA; } - @@ -2861,32 +2860,31 @@ class Student : Person { public double GPA; } - - - - + + + - - + + - - + + -" -); +"); } [Fact] @@ -2943,7 +2941,6 @@ class Student : Person { public double GPA; } - @@ -2961,27 +2958,26 @@ class Student : Person { public double GPA; }