提交 02b55801 编写于 作者: N Neal Gafter

Merge pull request #847 from gafter/DeclarationDiagnostics

Detect indirect cyclic constructor chaining
......@@ -2687,7 +2687,7 @@ private static bool IsNegativeConstantForArraySize(BoundExpression expression)
((ConstructorInitializerSyntax)initializerArgumentListOpt.Parent).ThisOrBaseKeyword.GetLocation(),
constructor);
hasErrors = true; //will prevent recursive constructor from being emitted
hasErrors = true; // prevent recursive constructor from being emitted
}
else if (resultMember.HasUnsafeParameter())
{
......
......@@ -349,7 +349,7 @@ internal void Complete(CancellationToken cancellationToken)
private void Validate()
{
DiagnosticBag semanticDiagnostics = _compilation.SemanticDiagnostics;
DiagnosticBag semanticDiagnostics = _compilation.DeclarationDiagnostics;
if (UsingAliases != null)
{
......
......@@ -148,7 +148,7 @@ public void BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpre
{
case BinaryOperatorKind.Equal:
case BinaryOperatorKind.NotEqual:
TypeSymbol systemDelegateType = _binder.GetSpecialType(SpecialType.System_Delegate, _binder.Compilation.SemanticDiagnostics, left.Syntax);
TypeSymbol systemDelegateType = _binder.GetSpecialType(SpecialType.System_Delegate, _binder.Compilation.DeclarationDiagnostics, left.Syntax);
if (Conversions.ClassifyImplicitConversionFromExpression(left, systemDelegateType, ref useSiteDiagnostics).IsValid &&
Conversions.ClassifyImplicitConversionFromExpression(right, systemDelegateType, ref useSiteDiagnostics).IsValid)
......
......@@ -4759,6 +4759,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Constructor &apos;{0}&apos; cannot call itself through another constructor.
/// </summary>
internal static string ERR_IndirectRecursiveConstructorCall {
get {
return ResourceManager.GetString("ERR_IndirectRecursiveConstructorCall", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &apos;in&apos; expected.
/// </summary>
......
......@@ -1418,6 +1418,9 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_RecursiveConstructorCall" xml:space="preserve">
<value>Constructor '{0}' cannot call itself</value>
</data>
<data name="ERR_IndirectRecursiveConstructorCall" xml:space="preserve">
<value>Constructor '{0}' cannot call itself through another constructor</value>
</data>
<data name="ERR_ObjectCallingBaseConstructor" xml:space="preserve">
<value>'{0}' has no base class and cannot call a base constructor</value>
</data>
......
......@@ -1872,21 +1872,34 @@ internal override CommonMessageProvider MessageProvider
/// <summary>
/// The bag in which semantic analysis should deposit its diagnostics.
/// </summary>
internal DiagnosticBag SemanticDiagnostics
internal DiagnosticBag DeclarationDiagnostics
{
get
{
if (_lazySemanticDiagnostics == null)
// We should only be placing diagnostics in this bag until
// we are done gathering declaration diagnostics. Assert that is
// the case. But since we have bugs (see https://github.com/dotnet/roslyn/issues/846)
// we disable the assertion until they are fixed.
Debug.Assert(!_declarationDiagnosticsFrozen || true);
if (_lazyDeclarationDiagnostics == null)
{
var diagnostics = new DiagnosticBag();
Interlocked.CompareExchange(ref _lazySemanticDiagnostics, diagnostics, null);
Interlocked.CompareExchange(ref _lazyDeclarationDiagnostics, diagnostics, null);
}
return _lazySemanticDiagnostics;
return _lazyDeclarationDiagnostics;
}
}
private DiagnosticBag _lazySemanticDiagnostics;
private IEnumerable<Diagnostic> FreezeDeclarationDiagnostics()
{
_declarationDiagnosticsFrozen = true;
var result = _lazyDeclarationDiagnostics?.AsEnumerable() ?? Enumerable.Empty<Diagnostic>();
return result;
}
private DiagnosticBag _lazyDeclarationDiagnostics;
private bool _declarationDiagnosticsFrozen = false;
/// <summary>
/// A bag in which diagnostics that should be reported after code gen can be deposited.
......@@ -2136,7 +2149,7 @@ private ImmutableArray<Diagnostic> GetSourceDeclarationDiagnostics(SyntaxTree sy
Assembly.ForceComplete(location, cancellationToken);
var result = this.SemanticDiagnostics.AsEnumerable().Concat(
var result = this.FreezeDeclarationDiagnostics().Concat(
((SourceModuleSymbol)this.SourceModule).Diagnostics);
if (locationFilterOpt != null)
......
......@@ -1412,6 +1412,13 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
if (initializerInvocation != null)
{
var ctorCall = initializerInvocation as BoundCall;
if (ctorCall != null && !ctorCall.HasAnyErrors && ctorCall.Method != method && ctorCall.Method.ContainingType == method.ContainingType)
{
// Detect and report indirect cycles in the ctor-initializer call graph.
compilationState.ReportCtorInitializerCycles(method, ctorCall.Method, ctorCall.Syntax, diagnostics);
}
constructorInitializer = new BoundExpressionStatement(initializerInvocation.Syntax, initializerInvocation) { WasCompilerGenerated = true };
Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer.");
}
......
// 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.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
......@@ -68,6 +70,12 @@ internal MethodWithBody(MethodSymbol method, BoundStatement body, ImportChain im
public LambdaFrame staticLambdaFrame;
/// <summary>
/// A graph of method->method references for this(...) constructor initializers.
/// Used to detect and report initializer cycles.
/// </summary>
SmallDictionary<MethodSymbol, MethodSymbol> _constructorInitializers;
public TypeCompilationState(NamedTypeSymbol typeOpt, CSharpCompilation compilation, PEModuleBuilder moduleBuilderOpt)
{
this.Compilation = compilation;
......@@ -164,6 +172,58 @@ public void Free()
}
_wrappers = null;
_constructorInitializers = null;
}
/// <summary>
/// Report an error if adding the edge (method1, method2) to the ctor-initializer
/// graph would add a new cycle to that graph.
/// </summary>
/// <param name="method1">a calling ctor</param>
/// <param name="method2">the chained-to ctor</param>
/// <param name="syntax">where to report a cyclic error if needed</param>
/// <param name="diagnostics">a diagnostic bag for receiving the diagnostic</param>
internal void ReportCtorInitializerCycles(MethodSymbol method1, MethodSymbol method2, CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
{
// precondition and postcondition: the graph _constructorInitializers is acyclic.
// If adding the edge (method1, method2) would induce a cycle, we report an error
// and do not add it to the set of edges. If it would not induce a cycle we add
// it to the set of edges and return.
if (method1 == method2)
{
// direct recursion is diagnosed elsewhere
throw ExceptionUtilities.Unreachable;
}
if (_constructorInitializers == null)
{
_constructorInitializers = new SmallDictionary<MethodSymbol, MethodSymbol>();
_constructorInitializers.Add(method1, method2);
return;
}
MethodSymbol next = method2;
while (true)
{
if (_constructorInitializers.TryGetValue(next, out next))
{
Debug.Assert((object)next != null);
if (method1 == next)
{
// We found a (new) cycle containing the edge (method1, method2). Report an
// error and do not add the edge.
diagnostics.Add(ErrorCode.ERR_IndirectRecursiveConstructorCall, syntax.Location, method1);
return;
}
}
else
{
// we've reached the end of the path without finding a cycle. Add the new edge.
_constructorInitializers.Add(method1, method2);
return;
}
}
}
}
}
......@@ -540,7 +540,9 @@ internal enum ErrorCode
ERR_PartialMethodInExpressionTree = 765,
ERR_PartialMethodMustReturnVoid = 766,
ERR_ExplicitImplCollisionOnRefOut = 767,
// unused 768-799
ERR_IndirectRecursiveConstructorCall = 768,
// unused 769-799
//ERR_NoEmptyArrayRanges = 800,
//ERR_IntegerSpecifierOnOneDimArrays = 801,
//ERR_IntegerSpecifierMustBePositive = 802,
......
......@@ -817,7 +817,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
{
var diagnostics = DiagnosticBag.GetInstance();
ValidateAttributeSemantics(diagnostics);
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
var thisThreadCompleted = _state.NotePartComplete(CompletionPart.FinishAttributeChecks);
Debug.Assert(thisThreadCompleted);
diagnostics.Free();
......@@ -944,7 +944,7 @@ private void ReportDiagnosticsForAddedModules()
ReportNameCollisionDiagnosticsForAddedModules(this.GlobalNamespace, diagnostics);
_compilation.SemanticDiagnostics.AddRange(diagnostics);
_compilation.DeclarationDiagnostics.AddRange(diagnostics);
diagnostics.Free();
}
......@@ -1340,7 +1340,7 @@ private void LoadAndValidateNetModuleAttributes(ref CustomAttributesBag<CSharpAt
if (Interlocked.CompareExchange(ref lazyNetModuleAttributesBag, netModuleAttributesBag, null) == null)
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
......
......@@ -204,7 +204,7 @@ private ConstantValue DefaultSyntaxValue
var diagnostics = DiagnosticBag.GetInstance();
if (Interlocked.CompareExchange(ref _lazyDefaultSyntaxValue, MakeDefaultExpression(diagnostics), ConstantValue.Unset) == ConstantValue.Unset)
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
......
......@@ -733,7 +733,7 @@ private ConstantValue GetLazyConstantValue(bool earlyDecodingWellKnownAttributes
#if REPORT_ALL
Console.WriteLine("Thread {0}, Field {1}, StartsCycle {2}", Thread.CurrentThread.ManagedThreadId, this, startsCycle);
#endif
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
this.state.NotePartComplete(CompletionPart.ConstantValue);
}
}
......
......@@ -121,7 +121,7 @@ public sealed override int FixedSize
// Winner writes diagnostics.
if (Interlocked.CompareExchange(ref _fixedSize, size, FixedSizeNotInitialized) == FixedSizeNotInitialized)
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
if (state.NotePartComplete(CompletionPart.FixedSize))
{
// FixedSize is the last completion part for fields.
......
......@@ -413,7 +413,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
{
var diagnostics = DiagnosticBag.GetInstance();
CheckBase(diagnostics);
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
state.NotePartComplete(CompletionPart.FinishBaseType);
diagnostics.Free();
}
......@@ -425,7 +425,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
{
var diagnostics = DiagnosticBag.GetInstance();
CheckInterfaces(diagnostics);
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
state.NotePartComplete(CompletionPart.FinishInterfaces);
diagnostics.Free();
}
......@@ -469,7 +469,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
{
var diagnostics = DiagnosticBag.GetInstance();
AfterMembersChecks(diagnostics);
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
var thisThreadCompleted = state.NotePartComplete(CompletionPart.FinishMemberChecks);
Debug.Assert(thisThreadCompleted);
diagnostics.Free();
......@@ -1026,7 +1026,7 @@ public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(string name, int
var diagnostics = DiagnosticBag.GetInstance();
if (Interlocked.CompareExchange(ref _lazyTypeMembers, MakeTypeMembers(diagnostics), null) == null)
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
state.NotePartComplete(CompletionPart.TypeMembers);
}
......@@ -1264,7 +1264,7 @@ private MembersAndInitializers GetMembersAndInitializers()
return alreadyKnown;
}
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
diagnostics.Free();
return membersAndInitializers;
......@@ -1289,7 +1289,7 @@ private MembersAndInitializers GetMembersAndInitializers()
if (Interlocked.CompareExchange(ref _lazyMembersDictionary, membersDictionary, null) == null)
{
MergePartialMethods(_lazyMembersDictionary, diagnostics);
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
state.NotePartComplete(CompletionPart.Members);
}
......@@ -1733,7 +1733,7 @@ internal override bool KnownCircularStruct
if (Interlocked.CompareExchange(ref _lazyKnownCircularStruct, value, (int)ThreeState.Unknown) == (int)ThreeState.Unknown)
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
Debug.Assert(value == _lazyKnownCircularStruct);
......
......@@ -47,7 +47,7 @@ internal partial class SourceMemberContainerTypeSymbol
{
// Do not cancel from this point on. We've assigned the member, so we must add
// the diagnostics.
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
state.NotePartComplete(CompletionPart.SynthesizedExplicitImplementations);
}
......
......@@ -306,12 +306,12 @@ internal sealed override TypeSymbol GetFieldType(ConsList<FieldSymbol> fieldsBei
TypeChecks(type, fieldSyntax, declarator, diagnostics);
// CONSIDER: SourceEventFieldSymbol would like to suppress these diagnostics.
compilation.SemanticDiagnostics.AddRange(diagnostics);
compilation.DeclarationDiagnostics.AddRange(diagnostics);
bool isFirstDeclarator = fieldSyntax.Declaration.Variables[0] == declarator;
if (isFirstDeclarator)
{
compilation.SemanticDiagnostics.AddRange(diagnosticsForFirstDeclarator);
compilation.DeclarationDiagnostics.AddRange(diagnosticsForFirstDeclarator);
}
state.NotePartComplete(CompletionPart.Type);
......
......@@ -430,7 +430,7 @@ protected sealed override void LazyAsyncMethodChecks(CancellationToken cancellat
if (state.NotePartComplete(CompletionPart.StartAsyncMethodChecks))
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
if (state.NotePartComplete(CompletionPart.FinishAsyncMethodChecks) && IsPartialDefinition)
{
DeclaringCompilation.SymbolDeclaredEvent(this);
......@@ -481,7 +481,7 @@ private TypeParameterConstraintClause GetTypeParameterConstraintClause(int ordin
var diagnostics = DiagnosticBag.GetInstance();
if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(diagnostics)))
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
......
......@@ -295,7 +295,7 @@ protected void LazyMethodChecks()
try
{
MethodChecks(diagnostics);
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
finally
{
......
......@@ -220,7 +220,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
{
if (diagnostics != null)
{
_assemblySymbol.DeclaringCompilation.SemanticDiagnostics.AddRange(diagnostics);
_assemblySymbol.DeclaringCompilation.DeclarationDiagnostics.AddRange(diagnostics);
}
_state.NotePartComplete(CompletionPart.FinishValidatingReferencedAssemblies);
......
......@@ -236,7 +236,7 @@ private TypeParameterConstraintClause GetTypeParameterConstraintClause(int ordin
var diagnostics = DiagnosticBag.GetInstance();
if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(diagnostics)))
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
......@@ -371,7 +371,7 @@ public override ImmutableArray<TypeParameterSymbol> TypeParameters
var diagnostics = DiagnosticBag.GetInstance();
if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters, MakeTypeParameters(diagnostics)))
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
......
......@@ -43,7 +43,7 @@ internal sealed override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics
var acyclicBase = this.MakeAcyclicBaseType(diagnostics);
if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBaseType, acyclicBase, ErrorTypeSymbol.UnknownResultType), ErrorTypeSymbol.UnknownResultType))
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
......@@ -69,7 +69,7 @@ internal sealed override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiag
var acyclicInterfaces = MakeAcyclicInterfaces(basesBeingResolved, diagnostics);
if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyInterfaces, acyclicInterfaces, default(ImmutableArray<NamedTypeSymbol>)).IsDefault)
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
......@@ -192,7 +192,7 @@ private SingleTypeDeclaration FirstDeclarationWithExplicitBases()
DiagnosticBag diagnostics = DiagnosticBag.GetInstance();
if (Interlocked.CompareExchange(ref _lazyDeclaredBases, MakeDeclaredBases(basesBeingResolved, diagnostics), null) == null)
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
......
......@@ -27,7 +27,7 @@ public override NamedTypeSymbol EnumUnderlyingType
if ((object)Interlocked.CompareExchange(ref _lazyEnumUnderlyingType, this.GetEnumUnderlyingType(diagnostics), ErrorTypeSymbol.UnknownResultType) ==
(object)ErrorTypeSymbol.UnknownResultType)
{
AddSemanticDiagnostics(diagnostics);
AddDeclarationDiagnostics(diagnostics);
this.state.NotePartComplete(CompletionPart.EnumUnderlyingType);
}
diagnostics.Free();
......
......@@ -222,7 +222,7 @@ internal override NamespaceExtent Extent
{
// NOTE: the following is not cancellable. Once we've set the
// members, we *must* do the following to make sure we're in a consistent state.
this.DeclaringCompilation.SemanticDiagnostics.AddRange(diagnostics);
this.DeclaringCompilation.DeclarationDiagnostics.AddRange(diagnostics);
RegisterDeclaredCorTypes();
_state.NotePartComplete(CompletionPart.NameToMembersMap);
......
......@@ -386,7 +386,7 @@ public override TypeSymbol Type
var result = this.ComputeType(binder, syntax, diagnostics);
if ((object)Interlocked.CompareExchange(ref _lazyType, result, null) == null)
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
......@@ -559,7 +559,7 @@ public override ImmutableArray<ParameterSymbol> Parameters
var result = this.ComputeParameters(binder, syntax, diagnostics);
if (ImmutableInterlocked.InterlockedInitialize(ref _lazyParameters, result))
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
......@@ -1182,7 +1182,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
if (_state.NotePartComplete(CompletionPart.Type))
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
......@@ -1204,7 +1204,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
if (_state.NotePartComplete(CompletionPart.Parameters))
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
DeclaringCompilation.SymbolDeclaredEvent(this);
}
......
......@@ -216,7 +216,7 @@ private TypeParameterBounds GetBounds(ConsList<TypeParameterSymbol> inProgress)
if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset), TypeParameterBounds.Unset))
{
this.CheckConstraintTypeConstraints(diagnostics);
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
_state.NotePartComplete(CompletionPart.TypeParameterConstraints);
}
......
......@@ -742,13 +742,13 @@ internal string GetDebuggerDisplay()
return $"{this.Kind} {this.ToDisplayString(SymbolDisplayFormat.TestFormat)}";
}
internal void AddSemanticDiagnostics(DiagnosticBag diagnostics)
internal void AddDeclarationDiagnostics(DiagnosticBag diagnostics)
{
if (!diagnostics.IsEmptyWithoutResolution)
{
CSharpCompilation compilation = this.DeclaringCompilation;
Debug.Assert(compilation != null);
compilation.SemanticDiagnostics.AddRange(diagnostics);
compilation.DeclarationDiagnostics.AddRange(diagnostics);
}
}
......
......@@ -348,7 +348,7 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
bool lazyAttributesStoredOnThisThread = false;
if (lazyCustomAttributesBag.SetAttributes(boundAttributes))
{
this.AddSemanticDiagnostics(diagnostics);
this.AddDeclarationDiagnostics(diagnostics);
lazyAttributesStoredOnThisThread = true;
if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty;
}
......
......@@ -10055,6 +10055,32 @@ public class A : A<int>
Diagnostic(ErrorCode.ERR_RecursiveConstructorCall, "base").WithArguments("A.A()"));
}
[WorkItem(366, "https://github.com/dotnet/roslyn/issues/366")]
[Fact]
public void IndirectConstructorCycle()
{
var text = @"
public class A
{
public A() : this(1) {}
public A(int x) : this(string.Empty) {}
public A(string s) : this(1) {}
public A(long l) : this(double.MaxValue) {}
public A(double d) : this(char.MaxValue) {}
public A(char c) : this(long.MaxValue) {}
public A(short s) : this() {}
}
";
CreateCompilationWithMscorlib(text).VerifyDiagnostics(
// (6,24): error CS0768: Constructor 'A.A(string)' cannot call itself through another constructor
// public A(string s) : this(1) {}
Diagnostic(ErrorCode.ERR_IndirectRecursiveConstructorCall, ": this(1)").WithArguments("A.A(string)").WithLocation(6, 24),
// (9,22): error CS0768: Constructor 'A.A(char)' cannot call itself through another constructor
// public A(char c) : this(long.MaxValue) {}
Diagnostic(ErrorCode.ERR_IndirectRecursiveConstructorCall, ": this(long.MaxValue)").WithArguments("A.A(char)").WithLocation(9, 22)
);
}
[Fact]
public void CS0517ERR_ObjectCallingBaseConstructor()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册