提交 8058ba4e 编写于 作者: S Shyam N 提交者: GitHub

Merge pull request #19282 from shyamnamboodiripad/FixPartialMethodInstrumentation

Fix partial method instrumentation
......@@ -2719,7 +2719,17 @@ private void EmitMethodDefIndexExpression(BoundMethodDefIndex node)
Debug.Assert(node.Method.IsDefinition);
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);
_builder.EmitOpCode(ILOpCode.Ldtoken);
EmitSymbolToken(node.Method, node.Syntax, null, encodeAsRawDefinitionToken: true);
// For partial methods, we emit pseudo token based on the symbol for the partial
// definition part as opposed to the symbol for the partial implementation part.
// We will need to resolve the symbol associated with each pseudo token in order
// to compute the real method definition tokens later. For partial methods, this
// resolution can only succeed if the associated symbol is the symbol for the
// partial definition and not the symbol for the partial implementation (see
// MethodSymbol.ResolvedMethodImpl()).
var symbol = node.Method.PartialDefinitionPart ?? node.Method;
EmitSymbolToken(symbol, node.Syntax, null, encodeAsRawDefinitionToken: true);
}
private void EmitMaximumMethodDefIndexExpression(BoundMaximumMethodDefIndex node)
......
......@@ -2380,7 +2380,7 @@ int P2
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C.P1.get");
AssertNotInstrumented(verifier, "C.P1.set");
AssertNotInstrumented(verifier, "C.<get_P1>g__L11_0");
......@@ -2526,7 +2526,7 @@ class C
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C.P1.get");
AssertNotInstrumented(verifier, "C.P1.set");
AssertNotInstrumented(verifier, "C.E1.add");
......@@ -2612,6 +2612,178 @@ class D
AssertInstrumented(verifier, "D.M");
}
[Fact]
public void TestPartialMethodsWithImplementation()
{
var source = @"
using System;
public partial class Class1<T>
{
partial void Method1<U>(int x);
public void Method2(int x)
{
Console.WriteLine($""Method2: x = {x}"");
Method1<T>(x);
}
}
public partial class Class1<T>
{
partial void Method1<U>(int x)
{
Console.WriteLine($""Method1: x = {x}"");
if (x > 0)
{
Console.WriteLine(""Method1: x > 0"");
Method1<U>(0);
}
else if (x < 0)
{
Console.WriteLine(""Method1: x < 0"");
}
}
}
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
Console.WriteLine(""Test"");
var c = new Class1<int>();
c.Method2(1);
}
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(1, 1, "partial void Method1<U>(int x)")
.True(@"Console.WriteLine($""Method1: x = {x}"");")
.True(@"Console.WriteLine(""Method1: x > 0"");")
.True("Method1<U>(0);")
.False(@"Console.WriteLine(""Method1: x < 0"");")
.True("x < 0)")
.True("x > 0)");
checker.Method(2, 1, "public void Method2(int x)")
.True(@"Console.WriteLine($""Method2: x = {x}"");")
.True("Method1<T>(x);");
checker.Method(3, 1, ".ctor()", expectBodySpan: false);
checker.Method(4, 1, "public static void Main(string[] args)")
.True("Test();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();");
checker.Method(5, 1, "static void Test()")
.True(@"Console.WriteLine(""Test"");")
.True("var c = new Class1<int>();")
.True("c.Method2(1);");
checker.Method(8, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
var expectedOutput = @"Test
Method2: x = 1
Method1: x = 1
Method1: x > 0
Method1: x = 0
" + checker.ExpectedOutput;
var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestPartialMethodsWithoutImplementation()
{
var source = @"
using System;
public partial class Class1<T>
{
partial void Method1<U>(int x);
public void Method2(int x)
{
Console.WriteLine($""Method2: x = {x}"");
Method1<T>(x);
}
}
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
Console.WriteLine(""Test"");
var c = new Class1<int>();
c.Method2(1);
}
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(1, 1, "public void Method2(int x)")
.True(@"Console.WriteLine($""Method2: x = {x}"");");
checker.Method(2, 1, ".ctor()", expectBodySpan: false);
checker.Method(3, 1, "public static void Main(string[] args)")
.True("Test();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();");
checker.Method(4, 1, "static void Test()")
.True(@"Console.WriteLine(""Test"");")
.True("var c = new Class1<int>();")
.True("c.Method2(1);");
checker.Method(7, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
var expectedOutput = @"Test
Method2: x = 1
" + checker.ExpectedOutput;
var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
}
private static void AssertNotInstrumented(CompilationVerifier verifier, string qualifiedMethodName)
=> AssertInstrumented(verifier, qualifiedMethodName, expected: false);
......
......@@ -95,7 +95,7 @@ internal abstract partial class MetadataWriter
this.Context = context;
this.messageProvider = messageProvider;
_cancellationToken = cancellationToken;
this.metadata = metadata;
_debugMetadataOpt = debugMetadataOpt;
_dynamicAnalysisDataWriterOpt = dynamicAnalysisDataWriterOpt;
......@@ -322,7 +322,7 @@ private bool IsMinimalDelta
/// The greatest index given to any method definition.
/// </summary>
protected abstract int GreatestMethodDefIndex { get; }
/// <summary>
/// Return true and full metadata handle of the type reference
/// if the reference is available in the current generation.
......@@ -423,9 +423,9 @@ private bool IsMinimalDelta
// Shared builder (reference equals heaps) if we are embedding Portable PDB into the metadata stream.
// Null otherwise.
protected readonly MetadataBuilder _debugMetadataOpt;
internal bool EmitStandaloneDebugMetadata => _debugMetadataOpt != null && metadata != _debugMetadataOpt;
private readonly DynamicAnalysisDataWriter _dynamicAnalysisDataWriterOpt;
private readonly Dictionary<ICustomAttribute, BlobHandle> _customAttributeSignatureIndex = new Dictionary<ICustomAttribute, BlobHandle>();
......@@ -611,8 +611,8 @@ private ImmutableArray<IParameterDefinition> GetParametersToEmitCore(IMethodDefi
// No explicit param row is needed if param has no flags (other than optionally IN),
// no name and no references to the param row, such as CustomAttribute, Constant, or FieldMarshal
if (parDef.Name != String.Empty ||
parDef.HasDefaultValue || parDef.IsOptional || parDef.IsOut || parDef.IsMarshalledExplicitly ||
if (parDef.Name != String.Empty ||
parDef.HasDefaultValue || parDef.IsOptional || parDef.IsOut || parDef.IsMarshalledExplicitly ||
IteratorHelper.EnumerableIsNotEmpty(parDef.GetAttributes(Context)))
{
if (builder != null)
......@@ -675,7 +675,7 @@ private void CreateInitialAssemblyRefIndex()
private void CreateInitialFileRefIndex()
{
Debug.Assert(!_tableIndicesAreComplete);
foreach (IFileReference fileRef in module.GetFiles(Context))
{
string key = fileRef.FileName;
......@@ -1153,7 +1153,7 @@ private BlobHandle GetMethodSignatureHandleAndBlob(IMethodReference methodRefere
var builder = PooledBlobBuilder.GetInstance();
var encoder = new BlobEncoder(builder).MethodSignature(
new SignatureHeader((byte)methodReference.CallingConvention).CallingConvention,
new SignatureHeader((byte)methodReference.CallingConvention).CallingConvention,
methodReference.GenericParameterCount,
isInstanceMethod: (methodReference.CallingConvention & CallingConvention.HasThis) != 0);
......@@ -1724,10 +1724,10 @@ public void WriteMetadataAndIL(PdbWriter nativePdbWriterOpt, Stream metadataStre
BuildMetadataAndIL(
nativePdbWriterOpt,
ilBuilder,
ilBuilder,
mappedFieldDataBuilder,
managedResourceDataBuilder,
out Blob mvidFixup,
out Blob mvidFixup,
out Blob mvidStringFixup);
var typeSystemRowCounts = metadata.GetRowCounts();
......@@ -1791,14 +1791,14 @@ public void WriteMetadataAndIL(PdbWriter nativePdbWriterOpt, Stream metadataStre
_tableIndicesAreComplete = true;
ReportReferencesToAddedSymbols();
BlobBuilder dynamicAnalysisDataOpt = null;
if (_dynamicAnalysisDataWriterOpt != null)
{
dynamicAnalysisDataOpt = new BlobBuilder();
_dynamicAnalysisDataWriterOpt.SerializeMetadataTables(dynamicAnalysisDataOpt);
}
PopulateTypeSystemTables(methodBodyOffsets, mappedFieldDataBuilder, managedResourceDataBuilder, dynamicAnalysisDataOpt, out mvidFixup);
}
......@@ -1948,7 +1948,7 @@ private void PopulateAssemblyTableRows()
name: GetStringHandleForPathAndCheckLength(module.Name, module),
culture: metadata.GetOrAddString(sourceAssembly.Identity.CultureName));
}
private void PopulateCustomAttributeTableRows(ImmutableArray<IGenericParameter> sortedGenericParameters)
{
if (this.IsFullMetadata)
......@@ -2278,9 +2278,9 @@ private void PopulateFieldMarshalTableRows()
var marshallingInformation = parDef.MarshallingInformation;
BlobHandle descriptor = (marshallingInformation != null)
? GetMarshallingDescriptorHandle(marshallingInformation)
: GetMarshallingDescriptorHandle(parDef.MarshallingDescriptor);
BlobHandle descriptor = (marshallingInformation != null)
? GetMarshallingDescriptorHandle(marshallingInformation)
: GetMarshallingDescriptorHandle(parDef.MarshallingDescriptor);
metadata.AddMarshallingDescriptor(
parent: GetParameterHandle(parDef),
......@@ -2450,7 +2450,7 @@ private void PopulateInterfaceImplTableRows()
}
}
}
private void PopulateManifestResourceTableRows(BlobBuilder resourceDataWriter, BlobBuilder dynamicAnalysisDataOpt)
{
if (dynamicAnalysisDataOpt != null)
......@@ -2462,7 +2462,7 @@ private void PopulateManifestResourceTableRows(BlobBuilder resourceDataWriter, B
offset: GetManagedResourceOffset(dynamicAnalysisDataOpt, resourceDataWriter)
);
}
foreach (var resource in this.module.GetResources(Context))
{
EntityHandle implementation;
......@@ -2497,11 +2497,11 @@ private void PopulateMemberRefTableRows()
{
metadata.AddMemberReference(
parent: GetMemberReferenceParent(memberRef),
name: GetStringHandleForNameAndCheckLength(memberRef.Name, memberRef),
name: GetStringHandleForNameAndCheckLength(memberRef.Name, memberRef),
signature: GetMemberReferenceSignatureHandle(memberRef));
}
}
private void PopulateMethodImplTableRows()
{
metadata.SetCapacity(TableIndex.MethodImpl, methodImplList.Count);
......@@ -2514,7 +2514,7 @@ private void PopulateMethodImplTableRows()
methodDeclaration: GetMethodDefinitionOrReferenceHandle(methodImplementation.ImplementedMethod));
}
}
private void PopulateMethodSpecTableRows()
{
var methodSpecs = this.GetMethodSpecs();
......@@ -2652,7 +2652,7 @@ private void PopulateModuleTableRow(out Blob mvidFixup)
encId: metadata.GetOrAddGuid(EncId),
encBaseId: metadata.GetOrAddGuid(EncBaseId));
}
private void PopulateParamTableRows()
{
var parameterDefs = this.GetParameterDefs();
......@@ -2680,7 +2680,7 @@ private void PopulatePropertyTableRows()
signature: GetPropertySignatureHandle(propertyDef));
}
}
private void PopulateTypeDefTableRows()
{
var typeDefs = this.GetTypeDefs();
......@@ -2863,7 +2863,7 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr
}
_dynamicAnalysisDataWriterOpt?.SerializeMethodDynamicAnalysisData(body);
bodyOffsets[methodRid - 1] = bodyOffset;
methodRid++;
......@@ -2891,11 +2891,11 @@ private int SerializeMethodBody(MethodBodyStreamEncoder encoder, IMethodBody met
}
var encodedBody = encoder.AddMethodBody(
codeSize: methodBody.IL.Length,
maxStack: methodBody.MaxStack,
exceptionRegionCount: exceptionRegions.Length,
codeSize: methodBody.IL.Length,
maxStack: methodBody.MaxStack,
exceptionRegionCount: exceptionRegions.Length,
hasSmallExceptionRegions: MayUseSmallExceptionHeaders(exceptionRegions),
localVariablesSignature: localSignatureHandleOpt,
localVariablesSignature: localSignatureHandleOpt,
attributes: (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : 0));
// Don't do small body method caching during deterministic builds until this issue is fixed
......@@ -3083,7 +3083,7 @@ private ReservedBlob<UserStringHandle> ReserveUserString(int length)
internal const uint LiteralGreatestMethodDefinitionToken = 0x40000000;
internal const uint SourceDocumentIndex = 0x20000000;
internal const uint ModuleVersionIdStringToken = 0x80000000;
private void WriteInstructions(Blob finalIL, ImmutableArray<byte> generatedIL, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup)
{
// write the raw body first and then patch tokens:
......@@ -3119,7 +3119,9 @@ private void WriteInstructions(Blob finalIL, ImmutableArray<byte> generatedIL, r
switch ((uint)tokenMask)
{
case LiteralMethodDefinitionToken:
token = MetadataTokens.GetToken(ResolveEntityHandleFromPseudoToken(pseudoToken & 0x00ffffff)) & 0x00ffffff;
// Crash the compiler if pseudo token fails to resolve to a MethodDefinitionHandle.
var handle = (MethodDefinitionHandle)ResolveEntityHandleFromPseudoToken(pseudoToken & 0x00ffffff);
token = MetadataTokens.GetToken(handle) & 0x00ffffff;
break;
case LiteralGreatestMethodDefinitionToken:
token = GreatestMethodDefIndex;
......@@ -3137,7 +3139,7 @@ private void WriteInstructions(Blob finalIL, ImmutableArray<byte> generatedIL, r
offset += 4;
break;
}
case OperandType.InlineString:
{
writer.Offset = offset;
......@@ -3221,7 +3223,7 @@ private void SerializeMethodBodyExceptionHandlerTable(ExceptionRegionEncoder enc
region.TryLength,
region.HandlerStartOffset,
region.HandlerLength,
(exceptionType != null) ? GetTypeHandle(exceptionType) : default(EntityHandle),
(exceptionType != null) ? GetTypeHandle(exceptionType) : default(EntityHandle),
region.FilterDecisionStartOffset);
}
}
......@@ -3380,7 +3382,7 @@ private void SerializeMetadataExpression(LiteralEncoder encoder, IMetadataExpres
{
CustomAttributeElementTypeEncoder typeEncoder;
encoder.TaggedScalar(out typeEncoder, out scalarEncoder);
// special case null argument assigned to Object parameter - treat as null string
if (c != null &&
c.Value == null &&
......@@ -3405,7 +3407,7 @@ private void SerializeMetadataExpression(LiteralEncoder encoder, IMetadataExpres
scalarEncoder.NullArray();
return;
}
Debug.Assert(!module.IsPlatformType(c.Type, PlatformType.SystemType) || c.Value == null);
scalarEncoder.Constant(c.Value);
}
......@@ -3599,7 +3601,7 @@ private void SerializePermissionSet(IEnumerable<ICustomAttribute> permissionSet,
private void SerializeReturnValueAndParameters(MethodSignatureEncoder encoder, ISignature signature, ImmutableArray<IParameterTypeInformation> varargParameters)
{
var declaredParameters = signature.GetParameters(Context);
var returnType = signature.GetType(Context);
var returnType = signature.GetType(Context);
ReturnTypeEncoder returnTypeEncoder;
ParametersEncoder parametersEncoder;
......
' 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
Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports System.Linq
Imports System.Reflection.Metadata
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Friend Partial Class CodeGenerator
Partial Friend Class CodeGenerator
Private _recursionDepth As Integer
Private Class EmitCancelledException
......@@ -2205,7 +2200,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Debug.Assert(node.Method.IsDefinition)
Debug.Assert(node.Type.SpecialType = SpecialType.System_Int32)
_builder.EmitOpCode(ILOpCode.Ldtoken)
EmitSymbolToken(node.Method, node.Syntax, encodeAsRawDefinitionToken:=True)
' For partial methods, we emit pseudo token based on the symbol for the partial
' definition part as opposed to the symbol for the partial implementation part.
' We will need to resolve the symbol associated with each pseudo token in order
' to compute the real method definition tokens later. For partial methods, this
' resolution can only succeed if the associated symbol is the symbol for the
' partial definition and not the symbol for the partial implementation (see
' MethodSymbol.ResolvedMethodImpl()).
Dim symbol = If(node.Method.PartialDefinitionPart, node.Method)
EmitSymbolToken(symbol, node.Syntax, encodeAsRawDefinitionToken:=True)
End Sub
Private Sub EmitMaximumMethodDefIndexExpression(node As BoundMaximumMethodDefIndex)
......
......@@ -7,8 +7,6 @@ Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.CodeAnalysis.Test.Utilities.VBInstrumentationChecker
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests
Imports Roslyn.Test.Utilities
Imports Xunit
Namespace Microsoft.CodeAnalysis.VisualBasic.DynamicAnalysis.UnitTests
......@@ -2368,6 +2366,173 @@ End Class
AssertInstrumented(verifier, "D.M")
End Sub
<Fact>
Public Sub TestPartialMethodsWithImplementation()
Dim testSource = <file name="c.vb">
<![CDATA[
Imports System
Partial Class Class1
Private Partial Sub Method1(x as Integer)
End Sub
Public Sub Method2(x as Integer)
Console.WriteLine("Method2: x = {0}", x)
Method1(x)
End Sub
End Class
Partial Class Class1
Private Sub Method1(x as Integer)
Console.WriteLine("Method1: x = {0}", x)
If x > 0
Console.WriteLine("Method1: x > 0")
Method1(0)
ElseIf x < 0
Console.WriteLine("Method1: x < 0")
End If
End Sub
End Class
Module Program
Public Sub Main()
Test()
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()
End Sub
Sub Test()
Console.WriteLine("Test")
Dim c = new Class1()
c.Method2(1)
End Sub
End Module
]]>
</file>
Dim source = <compilation>
<%= testSource %>
<%= InstrumentationHelperSource %>
</compilation>
Dim checker = New VBInstrumentationChecker()
checker.Method(1, 1, "New", expectBodySpan:=False)
checker.Method(2, 1, "Private Sub Method1(x as Integer)").
True("Console.WriteLine(""Method1: x = {0}"", x)").
True("Console.WriteLine(""Method1: x > 0"")").
True("Method1(0)").
False("Console.WriteLine(""Method1: x < 0"")").
True("x < 0").
True("x > 0")
checker.Method(3, 1, "Public Sub Method2(x as Integer)").
True("Console.WriteLine(""Method2: x = {0}"", x)").
True("Method1(x)")
checker.Method(4, 1, "Public Sub Main()").
True("Test()").
True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()")
checker.Method(5, 1, "Sub Test()").
True("Console.WriteLine(""Test"")").
True("new Class1()").
True("c.Method2(1)")
checker.Method(8, 1).
True().
False().
True().
True().
True().
True().
True().
True().
True().
True().
True()
Dim expectedOutput = "Test
Method2: x = 1
Method1: x = 1
Method1: x > 0
Method1: x = 0
" + XCDataToString(checker.ExpectedOutput)
Dim verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.ReleaseExe)
checker.CompleteCheck(verifier.Compilation, testSource)
verifier.VerifyDiagnostics()
verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.DebugExe)
checker.CompleteCheck(verifier.Compilation, testSource)
verifier.VerifyDiagnostics()
End Sub
<Fact>
Public Sub TestPartialMethodsWithoutImplementation()
Dim testSource = <file name="c.vb">
<![CDATA[
Imports System
Partial Class Class1
Private Partial Sub Method1(x as Integer)
End Sub
Public Sub Method2(x as Integer)
Console.WriteLine("Method2: x = {0}", x)
Method1(x)
End Sub
End Class
Module Program
Public Sub Main()
Test()
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()
End Sub
Sub Test()
Console.WriteLine("Test")
Dim c = new Class1()
c.Method2(1)
End Sub
End Module
]]>
</file>
Dim source = <compilation>
<%= testSource %>
<%= InstrumentationHelperSource %>
</compilation>
Dim checker = New VBInstrumentationChecker()
checker.Method(1, 1, "New", expectBodySpan:=False)
checker.Method(2, 1, "Public Sub Method2(x as Integer)").
True("Console.WriteLine(""Method2: x = {0}"", x)")
checker.Method(3, 1, "Public Sub Main()").
True("Test()").
True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()")
checker.Method(4, 1, "Sub Test()").
True("Console.WriteLine(""Test"")").
True("new Class1()").
True("c.Method2(1)")
checker.Method(7, 1).
True().
False().
True().
True().
True().
True().
True().
True().
True().
True().
True()
Dim expectedOutput = "Test
Method2: x = 1
" + XCDataToString(checker.ExpectedOutput)
Dim verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.ReleaseExe)
checker.CompleteCheck(verifier.Compilation, testSource)
verifier.VerifyDiagnostics()
verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.DebugExe)
checker.CompleteCheck(verifier.Compilation, testSource)
verifier.VerifyDiagnostics()
End Sub
Private Shared Sub AssertNotInstrumented(verifier As CompilationVerifier, qualifiedMethodName As String)
AssertInstrumented(verifier, qualifiedMethodName, expected:=False)
End Sub
......@@ -2394,6 +2559,14 @@ End Class
emitOptions:=EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)))
End Function
Private Overloads Function CompileAndVerify(source As XElement, Optional expectedOutput As String = Nothing, Optional options As VisualBasicCompilationOptions = Nothing) As CompilationVerifier
Return CompileAndVerify(source,
LatestVbReferences,
expectedOutput,
options:=If(options, TestOptions.ReleaseExe).WithDeterministic(True),
emitOptions:=EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)))
End Function
Private Overloads Function CompileAndVerify(source As String, Optional expectedOutput As String = Nothing, Optional options As VisualBasicCompilationOptions = Nothing) As CompilationVerifier
Return CompileAndVerify(source,
LatestVbReferences,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册