提交 22ea8679 编写于 作者: C CyrusNajmabadi

Merge branch 'master' into completionInEnumInitializer

......@@ -197,8 +197,26 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
//case ConversionKind.ImplicitConstant:
//case ConversionKind.ImplicitNumeric:
default:
Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
return true;
if (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter())
{
LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion();
if (requiredVersion > Compilation.LanguageVersion)
{
Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
operandType, patternType,
Compilation.LanguageVersion.ToDisplayString(),
new CSharpRequiredLanguageVersion(requiredVersion));
return true;
}
// permit pattern-matching when one of the types is an open type in C# 7.1.
break;
}
else
{
Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
return true;
}
}
}
......
......@@ -52,7 +52,8 @@ internal bool AddLabel(BoundPatternSwitchLabel label, DiagnosticBag diagnostics,
// For purposes of subsumption, we do not take into consideration the value
// of the input expression. Therefore we consider null possible if the type permits.
Syntax = label.Syntax;
var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: true);
var inputCouldBeNull = _subsumptionTree.Type.CanContainNull();
var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: inputCouldBeNull);
if (subsumedErrorCode != 0 && subsumedErrorCode != ErrorCode.ERR_NoImplicitConvCast)
{
if (!label.HasErrors)
......
......@@ -93,7 +93,7 @@ public static DecisionTree Create(BoundExpression expression, TypeSymbol type, S
expression = new BoundLocal(expression.Syntax, temp, null, type);
}
if (expression.Type.CanContainNull())
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
......@@ -104,8 +104,6 @@ public static DecisionTree Create(BoundExpression expression, TypeSymbol type, S
else
{
// If it is a (e.g. builtin) value type, we can switch on its (constant) values.
// If it isn't a builtin, in practice we will only use the Default part of the
// ByValue.
return new ByValue(expression, type, temp);
}
}
......
......@@ -130,7 +130,7 @@ private DecisionTree AddByValue(DecisionTree.Guarded guarded, BoundConstantPatte
private DecisionTree AddByValue(DecisionTree.ByValue byValue, BoundConstantPattern value, DecisionMaker makeDecision)
{
Debug.Assert(value.Value.Type == byValue.Type);
Debug.Assert(value.Value.Type.Equals(byValue.Type, TypeCompareKind.IgnoreDynamicAndTupleNames));
if (byValue.Default != null)
{
return AddByValue(byValue.Default, value, makeDecision);
......@@ -221,7 +221,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern
var kvp = byType.TypeAndDecision[i];
var matchedType = kvp.Key;
var decision = kvp.Value;
if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf())
if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
forType = decision;
break;
......@@ -260,21 +260,30 @@ private DecisionTree AddByType(DecisionTree decision, TypeSymbol type, DecisionM
case DecisionTree.DecisionKind.ByValue:
{
var byValue = (DecisionTree.ByValue)decision;
DecisionTree result;
if (byValue.Default == null)
{
byValue.Default = makeDecision(byValue.Expression, byValue.Type);
if (byValue.Default.MatchIsComplete)
if (byValue.Type.Equals(type, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
byValue.MatchIsComplete = true;
result = byValue.Default = makeDecision(byValue.Expression, byValue.Type);
}
else
{
byValue.Default = new DecisionTree.ByType(byValue.Expression, byValue.Type, null);
result = AddByType(byValue.Default, type, makeDecision);
}
return byValue.Default;
}
else
{
Debug.Assert(byValue.Default.Type == type);
return Add(byValue.Default, makeDecision);
result = AddByType(byValue.Default, type, makeDecision);
}
if (byValue.Default.MatchIsComplete)
{
byValue.MatchIsComplete = true;
}
return result;
}
case DecisionTree.DecisionKind.Guarded:
return AddByType((DecisionTree.Guarded)decision, type, makeDecision);
......@@ -321,7 +330,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci
if (byType.TypeAndDecision.Count != 0)
{
var lastTypeAndDecision = byType.TypeAndDecision.Last();
if (lastTypeAndDecision.Key.TupleUnderlyingTypeOrSelf() == type.TupleUnderlyingTypeOrSelf())
if (lastTypeAndDecision.Key.Equals(type, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
result = Add(lastTypeAndDecision.Value, makeDecision);
}
......@@ -533,7 +542,7 @@ private DecisionTree Add(DecisionTree.ByType byType, DecisionMaker makeDecision)
TypeSymbol patternType,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
if (expressionType == patternType)
if ((object)expressionType == (object)patternType)
{
return true;
}
......
......@@ -7604,7 +7604,16 @@ internal class CSharpResources {
}
/// <summary>
/// Looks up a localized string similar to An expression of type {0} cannot be handled by a pattern of type {1}..
/// Looks up a localized string similar to An expression of type &apos;{0}&apos; cannot be handled by a pattern of type &apos;{1}&apos; in C# {2}. Please use language version {3} or greater..
/// </summary>
internal static string ERR_PatternWrongGenericTypeInVersion {
get {
return ResourceManager.GetString("ERR_PatternWrongGenericTypeInVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An expression of type &apos;{0}&apos; cannot be handled by a pattern of type &apos;{1}&apos;..
/// </summary>
internal static string ERR_PatternWrongType {
get {
......
......@@ -4891,7 +4891,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>The switch case has already been handled by a previous case.</value>
</data>
<data name="ERR_PatternWrongType" xml:space="preserve">
<value>An expression of type {0} cannot be handled by a pattern of type {1}.</value>
<value>An expression of type '{0}' cannot be handled by a pattern of type '{1}'.</value>
</data>
<data name="WRN_AttributeIgnoredWhenPublicSigning" xml:space="preserve">
<value>Attribute '{0}' is ignored when public signing is specified.</value>
......@@ -5070,4 +5070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_VoidInTuple" xml:space="preserve">
<value>A tuple may not contain a value of type 'void'.</value>
</data>
<data name="ERR_PatternWrongGenericTypeInVersion" xml:space="preserve">
<value>An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater.</value>
</data>
</root>
......@@ -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)
......
......@@ -1476,8 +1476,13 @@ internal enum ErrorCode
WRN_Experimental = 8305,
ERR_TupleInferredNamesNotAvailable = 8306,
#region diagnostics for C# 7.1
ERR_BadDynamicMethodArgDefaultLiteral = 9000,
ERR_DefaultLiteralNotValid = 9001,
WRN_DefaultInSwitch = 9002,
ERR_PatternWrongGenericTypeInVersion = 9003,
#endregion diagnostics for C# 7.1
}
}
......@@ -129,6 +129,7 @@ internal enum MessageID
IDS_ThrowExpression = MessageBase + 12717,
IDS_FeatureDefaultLiteral = MessageBase + 12718,
IDS_FeatureInferredTupleNames = MessageBase + 12719,
IDS_FeatureGenericPatternMatching = MessageBase + 12720,
}
// Message IDs may refer to strings that need to be localized.
......@@ -188,6 +189,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
// C# 7.1 features.
case MessageID.IDS_FeatureDefaultLiteral:
case MessageID.IDS_FeatureInferredTupleNames:
case MessageID.IDS_FeatureGenericPatternMatching:
return LanguageVersion.CSharp7_1;
// C# 7 features.
......
......@@ -177,7 +177,7 @@ private void LowerPatternSwitch(BoundExpression loweredExpression, BoundPatternS
}
}
if (defaultLabel != null)
if (defaultLabel != null && !loweredDecisionTree.MatchIsComplete)
{
Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded(loweredExpression, loweredExpression.Type, default(ImmutableArray<KeyValuePair<BoundExpression, BoundExpression>>), defaultSection, null, defaultLabel));
}
......@@ -219,7 +219,8 @@ private void LowerDecisionTree(BoundExpression expression, DecisionTree decision
// Store the input expression into a temp
if (decisionTree.Expression != expression)
{
_loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, expression));
var convertedExpression = _factory.Convert(decisionTree.Expression.Type, expression);
_loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, convertedExpression));
}
if (_declaredTempSet.Add(decisionTree.Temp))
......
......@@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Immutable;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -80,7 +81,7 @@ private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern lowered
return result;
}
Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type == loweredPattern.Variable.GetTypeOrReturnType());
Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type.Equals(loweredPattern.Variable.GetTypeOrReturnType(), TypeCompareKind.IgnoreDynamicAndTupleNames));
var assignment = _factory.AssignmentExpression(loweredPattern.VariableAccess, loweredInput);
return _factory.MakeSequence(assignment, result);
......@@ -93,10 +94,10 @@ private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern lowered
return _factory.Sequence(ImmutableArray.Create(temp),
sideEffects: ImmutableArray<BoundExpression>.Empty,
result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: true));
result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: loweredInput.Type.CanContainNull()));
}
return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: true);
return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: loweredInput.Type.CanContainNull());
}
/// <summary>
......@@ -141,13 +142,36 @@ private BoundExpression CompareWithConstant(BoundExpression input, BoundExpressi
);
}
private bool MatchIsIrrefutable(TypeSymbol sourceType, TypeSymbol targetType, bool requiredNullTest)
{
// use site diagnostics will already have been reported during binding.
HashSet<DiagnosticInfo> ignoredDiagnostics = null;
switch (_compilation.Conversions.ClassifyBuiltInConversion(sourceType, targetType, ref ignoredDiagnostics).Kind)
{
case ConversionKind.Boxing:
case ConversionKind.ImplicitReference:
case ConversionKind.Identity:
return true;
default:
return false;
}
}
BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression loweredInput, BoundExpression loweredTarget, bool requiresNullTest)
{
var type = loweredTarget.Type;
requiresNullTest = requiresNullTest && loweredInput.Type.CanContainNull();
// The type here is not a Nullable<T> instance type, as that would have led to the semantic error:
// ERR_PatternNullableType: It is not legal to use nullable type '{0}' in a pattern; use the underlying type '{1}' instead.
Debug.Assert(!type.IsNullableType());
// It is possible that the input value is already of the correct type, in which case the pattern
// is irrefutable, and we can just do the assignment and return true (or perform the null test).
if (MatchIsIrrefutable(loweredInput.Type, loweredTarget.Type, requiresNullTest))
{
var convertedInput = _factory.Convert(loweredTarget.Type, loweredInput);
var assignment = _factory.AssignmentExpression(loweredTarget, convertedInput);
return requiresNullTest
? _factory.ObjectNotEqual(assignment, _factory.Null(type))
: _factory.MakeSequence(assignment, _factory.Literal(true));
}
// a pattern match of the form "expression is Type identifier" is equivalent to
// an invocation of one of these helpers:
......@@ -158,31 +182,15 @@ BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression lowe
// t = e as T;
// return t != null;
// }
if (loweredInput.Type == type)
{
// CONSIDER: this can be done whenever input.Type is a subtype of type for improved code
var assignment = _factory.AssignmentExpression(loweredTarget, loweredInput);
return requiresNullTest
? _factory.ObjectNotEqual(assignment, _factory.Null(type))
: _factory.MakeSequence(assignment, _factory.Literal(true));
}
else
{
return _factory.ObjectNotEqual(
_factory.AssignmentExpression(loweredTarget, _factory.As(loweredInput, type)),
_factory.Null(type));
}
return _factory.ObjectNotEqual(
_factory.AssignmentExpression(loweredTarget, _factory.As(loweredInput, type)),
_factory.Null(type));
}
else if (type.IsValueType)
{
// It is possible that the input value is already of the correct type, in which case the pattern
// is irrefutable, and we can just do the assignment and return true.
if (loweredInput.Type == type)
{
return _factory.MakeSequence(
_factory.AssignmentExpression(loweredTarget, loweredInput),
_factory.Literal(true));
}
// The type here is not a Nullable<T> instance type, as that would have led to the semantic error:
// ERR_PatternNullableType: It is not legal to use nullable type '{0}' in a pattern; use the underlying type '{1}' instead.
Debug.Assert(!type.IsNullableType());
// It may be possible to improve this code by only assigning t when returning
// true (avoid returning a new default value)
......@@ -207,18 +215,28 @@ BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression lowe
Debug.Assert(type.IsTypeParameter());
// bool Is<T>(this object i, out T o)
// {
// // inefficient because it performs the type test twice.
// bool s = i is T;
// if (s) o = (T)i;
// // inefficient because it performs the type test twice, and also because it boxes the input.
// bool s;
// o = (s = i is T) ? (T)i : default(T);
// return s;
// }
return _factory.Conditional(_factory.Is(loweredInput, type),
_factory.MakeSequence(_factory.AssignmentExpression(
loweredTarget,
_factory.Convert(type, loweredInput)),
_factory.Literal(true)),
_factory.Literal(false),
_factory.SpecialType(SpecialType.System_Boolean));
// Because a cast involving a type parameter is not necessarily a valid conversion (or, if it is, it might not
// be of a kind appropriate for pattern-matching), we use `object` as an intermediate type for the input expression.
var tmpType = _factory.SpecialType(SpecialType.System_Object);
var s = _factory.SynthesizedLocal(_factory.SpecialType(SpecialType.System_Boolean), syntax);
var i = _factory.SynthesizedLocal(tmpType, syntax); // we copy the input to avoid double evaluation
return _factory.Sequence(
ImmutableArray.Create(s, i),
ImmutableArray.Create<BoundExpression>(
_factory.AssignmentExpression(_factory.Local(i), _factory.Convert(tmpType, loweredInput)),
_factory.AssignmentExpression(loweredTarget, _factory.Conditional(
_factory.AssignmentExpression(_factory.Local(s), _factory.Is(_factory.Local(i), type)),
_factory.Convert(type, _factory.Local(i)),
_factory.Default(type), type))
),
_factory.Local(s)
);
}
}
}
......
......@@ -1102,6 +1102,11 @@ private MethodSymbol GetFieldFromHandleMethod(NamedTypeSymbol fieldContainer)
public BoundExpression Convert(TypeSymbol type, BoundExpression arg)
{
if (type == arg.Type)
{
return arg;
}
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion c = Compilation.Conversions.ClassifyConversionFromExpression(arg, type, ref useSiteDiagnostics);
Debug.Assert(c.Exists);
......
......@@ -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);
......
......@@ -6472,19 +6472,18 @@ static void M(object o)
var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll);
CompileAndVerify(c).VerifyIL("Program.M",
@"{
// Code size 210 (0xd2)
// Code size 202 (0xca)
.maxstack 2
.locals init (object V_0,
int V_1,
object V_2,
object V_3,
int? V_4,
int V_5,
object V_6,
int V_7,
object V_5,
int V_6,
object V_7,
object V_8,
object V_9,
object V_10)
object V_9)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.2
......@@ -6494,7 +6493,7 @@ .maxstack 2
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brtrue.s IL_000c
IL_000a: br.s IL_005e
IL_000a: br.s IL_005a
IL_000c: ldloc.0
IL_000d: isinst ""int?""
IL_0012: unbox.any ""int?""
......@@ -6504,88 +6503,83 @@ .maxstack 2
IL_0020: stloc.1
IL_0021: ldloca.s V_4
IL_0023: call ""bool int?.HasValue.get""
IL_0028: brfalse.s IL_005e
IL_0028: brfalse.s IL_005a
IL_002a: ldloc.1
IL_002b: stloc.s V_5
IL_002d: ldloc.s V_5
IL_002f: ldc.i4.1
IL_0030: sub
IL_0031: switch (
IL_004c,
IL_0054,
IL_005a,
IL_0052,
IL_0058)
IL_004a: br.s IL_005e
IL_004c: br.s IL_0060
IL_004e: br.s IL_006c
IL_0050: br.s IL_007a
IL_0052: br.s IL_006a
IL_0054: br.s IL_0065
IL_0056: br.s IL_005e
IL_0058: br.s IL_0076
IL_005a: br.s IL_0071
IL_005c: br.s IL_005e
IL_005e: br.s IL_0078
IL_0060: ldarg.0
IL_0061: brfalse.s IL_006a
IL_0063: br.s IL_004e
IL_0065: ldarg.0
IL_0066: brfalse.s IL_006a
IL_0068: br.s IL_0056
IL_006a: br.s IL_007c
IL_006c: ldarg.0
IL_006d: brtrue.s IL_0076
IL_006f: br.s IL_0050
IL_0071: ldarg.0
IL_0072: brtrue.s IL_0076
IL_0074: br.s IL_005c
IL_0076: br.s IL_007c
IL_0078: br.s IL_007c
IL_007a: br.s IL_007c
IL_007c: ldarg.0
IL_007d: stloc.2
IL_007e: ldloc.2
IL_007f: stloc.s V_8
IL_0081: ldloc.s V_8
IL_0083: stloc.s V_6
IL_0085: ldloc.s V_6
IL_0087: brtrue.s IL_008b
IL_0089: br.s IL_00b8
IL_008b: ldloc.s V_6
IL_008d: isinst ""int?""
IL_0092: unbox.any ""int?""
IL_0097: stloc.s V_4
IL_0099: ldloca.s V_4
IL_009b: call ""int int?.GetValueOrDefault()""
IL_00a0: stloc.s V_7
IL_00a2: ldloca.s V_4
IL_00a4: call ""bool int?.HasValue.get""
IL_00a9: brfalse.s IL_00b8
IL_00ab: ldloc.s V_7
IL_00ad: stloc.s V_5
IL_00af: ldloc.s V_5
IL_00b1: ldc.i4.1
IL_00b2: beq.s IL_00b6
IL_00b4: br.s IL_00b8
IL_00b6: br.s IL_00ba
IL_00b8: br.s IL_00bc
IL_00ba: br.s IL_00be
IL_00bc: br.s IL_00be
IL_00be: ldarg.0
IL_00bf: stloc.2
IL_00c0: ldloc.2
IL_00c1: stloc.s V_10
IL_00c3: ldloc.s V_10
IL_00c5: stloc.s V_9
IL_00c7: ldloc.s V_9
IL_00c9: brtrue.s IL_00cd
IL_00cb: br.s IL_00cd
IL_00cd: br.s IL_00cf
IL_00cf: br.s IL_00d1
IL_00d1: ret
}
");
IL_002b: ldc.i4.1
IL_002c: sub
IL_002d: switch (
IL_0048,
IL_0050,
IL_0056,
IL_004e,
IL_0054)
IL_0046: br.s IL_005a
IL_0048: br.s IL_005c
IL_004a: br.s IL_0068
IL_004c: br.s IL_0076
IL_004e: br.s IL_0066
IL_0050: br.s IL_0061
IL_0052: br.s IL_005a
IL_0054: br.s IL_0072
IL_0056: br.s IL_006d
IL_0058: br.s IL_005a
IL_005a: br.s IL_0074
IL_005c: ldarg.0
IL_005d: brfalse.s IL_0066
IL_005f: br.s IL_004a
IL_0061: ldarg.0
IL_0062: brfalse.s IL_0066
IL_0064: br.s IL_0052
IL_0066: br.s IL_0078
IL_0068: ldarg.0
IL_0069: brtrue.s IL_0072
IL_006b: br.s IL_004c
IL_006d: ldarg.0
IL_006e: brtrue.s IL_0072
IL_0070: br.s IL_0058
IL_0072: br.s IL_0078
IL_0074: br.s IL_0078
IL_0076: br.s IL_0078
IL_0078: ldarg.0
IL_0079: stloc.2
IL_007a: ldloc.2
IL_007b: stloc.s V_7
IL_007d: ldloc.s V_7
IL_007f: stloc.s V_5
IL_0081: ldloc.s V_5
IL_0083: brtrue.s IL_0087
IL_0085: br.s IL_00b0
IL_0087: ldloc.s V_5
IL_0089: isinst ""int?""
IL_008e: unbox.any ""int?""
IL_0093: stloc.s V_4
IL_0095: ldloca.s V_4
IL_0097: call ""int int?.GetValueOrDefault()""
IL_009c: stloc.s V_6
IL_009e: ldloca.s V_4
IL_00a0: call ""bool int?.HasValue.get""
IL_00a5: brfalse.s IL_00b0
IL_00a7: ldloc.s V_6
IL_00a9: ldc.i4.1
IL_00aa: beq.s IL_00ae
IL_00ac: br.s IL_00b0
IL_00ae: br.s IL_00b2
IL_00b0: br.s IL_00b4
IL_00b2: br.s IL_00b6
IL_00b4: br.s IL_00b6
IL_00b6: ldarg.0
IL_00b7: stloc.2
IL_00b8: ldloc.2
IL_00b9: stloc.s V_9
IL_00bb: ldloc.s V_9
IL_00bd: stloc.s V_8
IL_00bf: ldloc.s V_8
IL_00c1: brtrue.s IL_00c5
IL_00c3: br.s IL_00c5
IL_00c5: br.s IL_00c7
IL_00c7: br.s IL_00c9
IL_00c9: ret
}");
c.VerifyPdb(
@"<symbols>
<methods>
......@@ -6600,7 +6594,6 @@ .maxstack 2
<slot kind=""temp"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""413"" />
<slot kind=""1"" offset=""378"" />
......@@ -6612,22 +6605,22 @@ .maxstack 2
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""19"" />
<entry offset=""0x5"" hidden=""true"" />
<entry offset=""0x60"" startLine=""7"" startColumn=""20"" endLine=""7"" endColumn=""34"" />
<entry offset=""0x65"" startLine=""9"" startColumn=""20"" endLine=""9"" endColumn=""34"" />
<entry offset=""0x6a"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""23"" />
<entry offset=""0x6c"" startLine=""11"" startColumn=""20"" endLine=""11"" endColumn=""34"" />
<entry offset=""0x71"" startLine=""13"" startColumn=""20"" endLine=""13"" endColumn=""34"" />
<entry offset=""0x76"" startLine=""14"" startColumn=""17"" endLine=""14"" endColumn=""23"" />
<entry offset=""0x78"" startLine=""16"" startColumn=""17"" endLine=""16"" endColumn=""23"" />
<entry offset=""0x7a"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" />
<entry offset=""0x7c"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" />
<entry offset=""0x81"" hidden=""true"" />
<entry offset=""0xba"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" />
<entry offset=""0xbc"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" />
<entry offset=""0xbe"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" />
<entry offset=""0xc3"" hidden=""true"" />
<entry offset=""0xcf"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" />
<entry offset=""0xd1"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" />
<entry offset=""0x5c"" startLine=""7"" startColumn=""20"" endLine=""7"" endColumn=""34"" />
<entry offset=""0x61"" startLine=""9"" startColumn=""20"" endLine=""9"" endColumn=""34"" />
<entry offset=""0x66"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""23"" />
<entry offset=""0x68"" startLine=""11"" startColumn=""20"" endLine=""11"" endColumn=""34"" />
<entry offset=""0x6d"" startLine=""13"" startColumn=""20"" endLine=""13"" endColumn=""34"" />
<entry offset=""0x72"" startLine=""14"" startColumn=""17"" endLine=""14"" endColumn=""23"" />
<entry offset=""0x74"" startLine=""16"" startColumn=""17"" endLine=""16"" endColumn=""23"" />
<entry offset=""0x76"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" />
<entry offset=""0x78"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" />
<entry offset=""0x7d"" hidden=""true"" />
<entry offset=""0xb2"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" />
<entry offset=""0xb4"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" />
<entry offset=""0xb6"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" />
<entry offset=""0xbb"" hidden=""true"" />
<entry offset=""0xc7"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" />
<entry offset=""0xc9"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" />
</sequencePoints>
</method>
</methods>
......
......@@ -5674,5 +5674,226 @@ public static void Main(string[] args)
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe);
var comp = CompileAndVerify(compilation, expectedOutput: "roslyn");
}
[Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")]
public void OpenTypeMatch_01()
{
var source =
@"using System;
public class Base { }
public class Derived : Base { }
public class Program
{
public static void Main(string[] args)
{
M(new Derived());
M(new Base());
}
public static void M<T>(T x) where T: Base
{
Console.Write(x is Derived b0);
switch (x)
{
case Derived b1:
Console.Write(1);
break;
default:
Console.Write(0);
break;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7);
compilation.VerifyDiagnostics(
// (13,28): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Derived b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived").WithArguments("T", "Derived", "7", "7.1").WithLocation(13, 28),
// (16,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived' in C# 7. Please use language version 7.1 or greater.
// case Derived b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived").WithArguments("T", "Derived", "7", "7.1").WithLocation(16, 18)
);
compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "True1False0");
}
[Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")]
public void OpenTypeMatch_02()
{
var source =
@"using System;
public class Base { }
public class Derived : Base { }
public class Program
{
public static void Main(string[] args)
{
M<Derived>(new Derived());
M<Derived>(new Base());
}
public static void M<T>(Base x)
{
Console.Write(x is T b0);
switch (x)
{
case T b1:
Console.Write(1);
break;
default:
Console.Write(0);
break;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7);
compilation.VerifyDiagnostics(
// (13,28): error CS9003: An expression of type 'Base' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is T b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("Base", "T", "7", "7.1").WithLocation(13, 28),
// (16,18): error CS9003: An expression of type 'Base' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater.
// case T b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("Base", "T", "7", "7.1")
);
compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "True1False0");
}
[Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")]
public void OpenTypeMatch_03()
{
var source =
@"using System;
public class Base { }
public class Derived<T> : Base { }
public class Program
{
public static void Main(string[] args)
{
M<Base>(new Derived<Base>());
M<Base>(new Base());
}
public static void M<T>(T x) where T: Base
{
Console.Write(x is Derived<T> b0);
switch (x)
{
case Derived<T> b1:
Console.Write(1);
break;
default:
Console.Write(0);
break;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7);
compilation.VerifyDiagnostics(
// (13,28): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived<T>' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Derived<T> b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived<T>").WithArguments("T", "Derived<T>", "7", "7.1").WithLocation(13, 28),
// (16,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived<T>' in C# 7. Please use language version 7.1 or greater.
// case Derived<T> b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived<T>").WithArguments("T", "Derived<T>", "7", "7.1").WithLocation(16, 18)
);
compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "True1False0");
}
[Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")]
public void OpenTypeMatch_04()
{
var source =
@"using System;
public class Base { }
class Container<T>
{
public class Derived : Base { }
}
public class Program
{
public static void Main(string[] args)
{
M<Base>(new Container<Base>.Derived());
M<Base>(new Base());
}
public static void M<T>(T x) where T: Base
{
Console.Write(x is Container<T>.Derived b0);
switch (x)
{
case Container<T>.Derived b1:
Console.Write(1);
break;
default:
Console.Write(0);
break;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7);
compilation.VerifyDiagnostics(
// (16,28): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Container<T>.Derived' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Container<T>.Derived b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived").WithArguments("T", "Container<T>.Derived", "7", "7.1").WithLocation(16, 28),
// (19,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Container<T>.Derived' in C# 7. Please use language version 7.1 or greater.
// case Container<T>.Derived b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived").WithArguments("T", "Container<T>.Derived", "7", "7.1").WithLocation(19, 18)
);
compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "True1False0");
}
[Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")]
public void OpenTypeMatch_05()
{
var source =
@"using System;
public class Base { }
class Container<T>
{
public class Derived : Base { }
}
public class Program
{
public static void Main(string[] args)
{
M<Base>(new Container<Base>.Derived[1]);
M<Base>(new Base[1]);
}
public static void M<T>(T[] x) where T: Base
{
Console.Write(x is Container<T>.Derived[] b0);
switch (x)
{
case Container<T>.Derived[] b1:
Console.Write(1);
break;
default:
Console.Write(0);
break;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7);
compilation.VerifyDiagnostics(
// (16,28): error CS9003: An expression of type 'T[]' cannot be handled by a pattern of type 'Container<T>.Derived[]' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Container<T>.Derived[] b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived[]").WithArguments("T[]", "Container<T>.Derived[]", "7", "7.1").WithLocation(16, 28),
// (19,18): error CS9003: An expression of type 'T[]' cannot be handled by a pattern of type 'Container<T>.Derived[]' in C# 7. Please use language version 7.1 or greater.
// case Container<T>.Derived[] b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived[]").WithArguments("T[]", "Container<T>.Derived[]", "7", "7.1").WithLocation(19, 18)
);
compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "True1False0");
}
}
}
......@@ -2987,5 +2987,120 @@ static void Main()
Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "case (string)null:").WithArguments("null").WithLocation(37, 13)
);
}
[Fact]
public void SubsumedCasesAreUnreachable_01()
{
var source =
@"
class Program
{
static void Main(string[] args)
{
switch (args.Length)
{
case 1:
break;
case System.IComparable c:
break;
case 2: // error: subsumed
break; // unreachable
case int n: // error: subsumed
break; // unreachable
case var i: // error: subsumed
break; // unreachable
default:
break; // ok; default case is always considered reachable
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics(
// (12,13): error CS8120: The switch case has already been handled by a previous case.
// case 2: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case 2:").WithLocation(12, 13),
// (14,18): error CS8120: The switch case has already been handled by a previous case.
// case int n: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "int n").WithLocation(14, 18),
// (16,18): error CS8120: The switch case has already been handled by a previous case.
// case var i: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "var i").WithLocation(16, 18),
// (13,17): warning CS0162: Unreachable code detected
// break; // unreachable
Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(13, 17),
// (15,17): warning CS0162: Unreachable code detected
// break; // unreachable
Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(15, 17),
// (17,17): warning CS0162: Unreachable code detected
// break; // unreachable
Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(17, 17)
);
}
[Fact]
public void SwitchTuple()
{
var source =
@"
class Program
{
static void Main(string[] args)
{
switch ((x: 1, y: 2))
{
case System.IComparable c:
break;
case System.ValueTuple<int, int> x: // error: subsumed
break; // unreachable
default:
break; // ok; default case is always considered reachable
}
}
}
";
var compilation = CreateStandardCompilation(
source, options: TestOptions.ReleaseExe, references: new[] { SystemRuntimeFacadeRef, ValueTupleRef });
compilation.VerifyDiagnostics(
// (10,18): error CS8120: The switch case has already been handled by a previous case.
// case System.ValueTuple<int, int> x: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "System.ValueTuple<int, int> x").WithLocation(10, 18),
// (11,17): warning CS0162: Unreachable code detected
// break; // unreachable
Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(11, 17)
);
}
[Fact]
public void ByValueThenByTypeTwice()
{
var source =
@"
class Program
{
static bool b = false;
static int i = 2;
static void Main(string[] args)
{
switch (i)
{
case 1:
break;
case System.IComparable c when b:
break;
case System.IFormattable f when b:
break;
default:
System.Console.WriteLine(nameof(Main));
break;
}
}
}
";
var compilation = CreateStandardCompilation(
source, options: TestOptions.ReleaseExe, references: new[] { SystemRuntimeFacadeRef, ValueTupleRef });
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput: "Main");
}
}
}
......@@ -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;
......
......@@ -14,6 +14,7 @@ public static class TestOptions
public static readonly CSharpParseOptions Script = new CSharpParseOptions(kind: SourceCodeKind.Script, documentationMode: DocumentationMode.None);
public static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None);
public static readonly CSharpParseOptions Regular6 = Regular.WithLanguageVersion(LanguageVersion.CSharp6);
public static readonly CSharpParseOptions Regular7 = Regular.WithLanguageVersion(LanguageVersion.CSharp7);
public static readonly CSharpParseOptions Regular7_1 = Regular.WithLanguageVersion(LanguageVersion.CSharp7_1);
public static readonly CSharpParseOptions RegularWithDocumentationComments = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Diagnose);
......
' 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,
......
......@@ -179,6 +179,25 @@ class Program
index: 1);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeProjectFromCSharp7ToCSharp7_1_B()
{
await TestLanguageVersionUpgradedAsync(
@"public class Base { }
public class Derived : Base { }
public class Program
{
public static void M<T>(T x) where T: Base
{
System.Console.Write(x is [|Derived|] b0);
}
}
",
LanguageVersion.CSharp7_1,
new CSharpParseOptions(LanguageVersion.CSharp7),
index: 1);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeAllProjectsToDefault()
{
......
......@@ -2349,6 +2349,56 @@ public int M2()
index: 1);
}
[WorkItem(18556, "https://github.com/dotnet/roslyn/issues/18556")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestImplementInterfaceThroughExplicitProperty()
{
await TestActionCountAsync(
@"interface IA
{
IB B { get; }
}
interface IB
{
int M();
}
class AB : IA, [|IB|]
{
IB IA.B => null;
}",
count: 3);
await TestWithAllCodeStyleOptionsOffAsync(
@"interface IA
{
IB B { get; }
}
interface IB
{
int M();
}
class AB : IA, [|IB|]
{
IB IA.B => null;
}",
@"interface IA
{
IB B { get; }
}
interface IB
{
int M();
}
class AB : IA, [|IB|]
{
IB IA.B => null;
public int M()
{
return ((IA)this).B.M();
}
}", index: 1);
}
[WorkItem(768799, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/768799")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestNoImplementThroughIndexer()
......
......@@ -25,9 +25,10 @@ internal class CSharpUpgradeProjectCodeFixProvider : AbstractUpgradeProjectCodeF
private const string CS8107 = nameof(CS8107); // error CS8059: Feature is not available in C# 7.0. Please use language version X or greater.
private const string CS8302 = nameof(CS8302); // error CS8302: Feature is not available in C# 7.1. Please use language version X or greater.
private const string CS8306 = nameof(CS8306); // error CS8306: ... Please use language version 7.1 or greater to access a un-named element by its inferred name.
private const string CS9003 = nameof(CS9003); // error CS9003: An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater.
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059, CS8107, CS8302, CS8306);
ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059, CS8107, CS8302, CS8306, CS9003);
public override string UpgradeThisProjectResource => CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0;
public override string UpgradeAllProjectsResource => CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0;
......
......@@ -159,8 +159,8 @@ protected override Task<Document> GetChangedDocumentAsync(CancellationToken canc
public Task<Document> GetUpdatedDocumentAsync(CancellationToken cancellationToken)
{
var unimplementedMembers = Explicitly
? State.UnimplementedExplicitMembers
var unimplementedMembers = Explicitly
? State.UnimplementedExplicitMembers
: State.UnimplementedMembers;
return GetUpdatedDocumentAsync(Document, unimplementedMembers, State.ClassOrStructType, State.ClassOrStructDecl, cancellationToken);
}
......@@ -441,14 +441,14 @@ private static bool IsUnexpressibleTypeParameter(ITypeParameterSymbol typeParame
return generateInvisibly ? accessor : null;
}
private SyntaxNode CreateThroughExpression(SyntaxGenerator factory)
private SyntaxNode CreateThroughExpression(SyntaxGenerator generator)
{
var through = ThroughMember.IsStatic
? GenerateName(factory, State.ClassOrStructType.IsGenericType)
: factory.ThisExpression();
? GenerateName(generator, State.ClassOrStructType.IsGenericType)
: generator.ThisExpression();
through = factory.MemberAccessExpression(
through, factory.IdentifierName(ThroughMember.Name));
through = generator.MemberAccessExpression(
through, generator.IdentifierName(ThroughMember.Name));
var throughMemberType = ThroughMember.GetMemberType();
if ((State.InterfaceTypes != null) && (throughMemberType != null))
......@@ -476,13 +476,32 @@ private SyntaxNode CreateThroughExpression(SyntaxGenerator factory)
// uncommon case and optimize for the common one - in other words, we only apply the cast
// in cases where we can unambiguously figure out which interface we are trying to implement.
var interfaceBeingImplemented = State.InterfaceTypes.SingleOrDefault();
if ((interfaceBeingImplemented != null) && (!throughMemberType.Equals(interfaceBeingImplemented)))
if (interfaceBeingImplemented != null)
{
through = factory.CastExpression(interfaceBeingImplemented,
through.WithAdditionalAnnotations(Simplifier.Annotation));
var facts = this.Document.GetLanguageService<ISyntaxFactsService>();
through = facts.Parenthesize(through);
if (!throughMemberType.Equals(interfaceBeingImplemented))
{
through = generator.CastExpression(interfaceBeingImplemented,
through.WithAdditionalAnnotations(Simplifier.Annotation));
}
else if (!ThroughMember.IsStatic &&
ThroughMember is IPropertySymbol throughMemberProperty &&
throughMemberProperty.ExplicitInterfaceImplementations.Any())
{
// If we are implementing through an explicitly implemented property, we need to cast 'this' to
// the explicitly implemented interface type before calling the member, as in:
// ((IA)this).Prop.Member();
//
var explicitlyImplementedProperty = throughMemberProperty.ExplicitInterfaceImplementations[0];
var explicitImplementationCast = generator.CastExpression(
explicitlyImplementedProperty.ContainingType,
generator.ThisExpression());
through = generator.MemberAccessExpression(explicitImplementationCast,
generator.IdentifierName(explicitlyImplementedProperty.Name));
through = through.WithAdditionalAnnotations(Simplifier.Annotation);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册