未验证 提交 81e9db82 编写于 作者: F Fred Silberberg 提交者: GitHub

Merge pull request #33178 from 333fred/rpatterns-33018

Make IPropertySubpattern Internal
...@@ -1996,6 +1996,7 @@ private IOperation CreateBoundDiscardPatternOperation(BoundDiscardPattern boundN ...@@ -1996,6 +1996,7 @@ private IOperation CreateBoundDiscardPatternOperation(BoundDiscardPattern boundN
boundNode.Syntax, boundNode.Syntax,
isImplicit: boundNode.WasCompilerGenerated); isImplicit: boundNode.WasCompilerGenerated);
} }
private IOperation CreateUsingLocalDeclarationsOperation(BoundUsingLocalDeclarations boundNode) private IOperation CreateUsingLocalDeclarationsOperation(BoundUsingLocalDeclarations boundNode)
{ {
//TODO: Implement UsingLocalDeclaration operations correctly. //TODO: Implement UsingLocalDeclaration operations correctly.
...@@ -2007,5 +2008,40 @@ private IOperation CreateUsingLocalDeclarationsOperation(BoundUsingLocalDeclarat ...@@ -2007,5 +2008,40 @@ private IOperation CreateUsingLocalDeclarationsOperation(BoundUsingLocalDeclarat
getChildren: () => ImmutableArray.Create<IOperation>(CreateBoundMultipleLocalDeclarationsOperation((BoundMultipleLocalDeclarations)boundNode)), getChildren: () => ImmutableArray.Create<IOperation>(CreateBoundMultipleLocalDeclarationsOperation((BoundMultipleLocalDeclarations)boundNode)),
isImplicit: false); isImplicit: false);
} }
internal IPropertySubpatternOperation CreatePropertySubpattern(BoundSubpattern subpattern, ITypeSymbol matchedType)
{
SyntaxNode syntax = subpattern.Syntax;
return new CSharpLazyPropertySubpatternOperation(this, subpattern, matchedType, syntax, _semanticModel);
}
internal IOperation CreatePropertySubpatternMember(Symbol symbol, ITypeSymbol matchedType, SyntaxNode syntax)
{
var nameSyntax = (syntax is SubpatternSyntax subpatSyntax ? subpatSyntax.NameColon?.Name : null) ?? syntax;
bool isImplicit = nameSyntax == syntax;
switch (symbol)
{
case FieldSymbol field:
{
var constantValue = field.ConstantValue is null ? default(Optional<object>) : new Optional<object>(field.ConstantValue);
var receiver = new InstanceReferenceOperation(
InstanceReferenceKind.PatternInput, _semanticModel, nameSyntax, matchedType, constantValue, isImplicit: true);
return new FieldReferenceOperation(
field, isDeclaration: false, receiver, _semanticModel, nameSyntax, field.Type.TypeSymbol, constantValue, isImplicit: isImplicit);
}
case PropertySymbol property:
{
var receiver = new InstanceReferenceOperation(
InstanceReferenceKind.PatternInput, _semanticModel, nameSyntax, matchedType, constantValue: default, isImplicit: true);
return new PropertyReferenceOperation(
property, receiver, ImmutableArray<IArgumentOperation>.Empty, _semanticModel, nameSyntax, property.Type.TypeSymbol,
constantValue: default, isImplicit: isImplicit);
}
default:
// We should expose the symbol in this case somehow:
// https://github.com/dotnet/roslyn/issues/33175
return OperationFactory.CreateInvalidOperation(_semanticModel, nameSyntax, ImmutableArray<IOperation>.Empty, isImplicit);
}
}
} }
} }
...@@ -14,7 +14,7 @@ internal sealed partial class CSharpOperationFactory ...@@ -14,7 +14,7 @@ internal sealed partial class CSharpOperationFactory
{ {
private static readonly IConvertibleConversion s_boxedIdentityConversion = Conversion.Identity; private static readonly IConvertibleConversion s_boxedIdentityConversion = Conversion.Identity;
private static Optional<object> ConvertToOptional(ConstantValue value) internal static Optional<object> ConvertToOptional(ConstantValue value)
{ {
return value != null && !value.IsBad ? new Optional<object>(value.Value) : default(Optional<object>); return value != null && !value.IsBad ? new Optional<object>(value.Value) : default(Optional<object>);
} }
......
...@@ -1492,7 +1492,10 @@ internal sealed partial class CSharpLazyRecursivePatternOperation : LazyRecursiv ...@@ -1492,7 +1492,10 @@ internal sealed partial class CSharpLazyRecursivePatternOperation : LazyRecursiv
private readonly CSharpOperationFactory _operationFactory; private readonly CSharpOperationFactory _operationFactory;
private readonly BoundRecursivePattern _boundRecursivePattern; private readonly BoundRecursivePattern _boundRecursivePattern;
public CSharpLazyRecursivePatternOperation(CSharpOperationFactory operationFactory, BoundRecursivePattern boundRecursivePattern, SemanticModel semanticModel) public CSharpLazyRecursivePatternOperation(
CSharpOperationFactory operationFactory,
BoundRecursivePattern boundRecursivePattern,
SemanticModel semanticModel)
: base(inputType: boundRecursivePattern.InputType, : base(inputType: boundRecursivePattern.InputType,
matchedType: boundRecursivePattern.DeclaredType?.Type ?? boundRecursivePattern.InputType.StrippedType(), matchedType: boundRecursivePattern.DeclaredType?.Type ?? boundRecursivePattern.InputType.StrippedType(),
deconstructSymbol: boundRecursivePattern.DeconstructMethod, deconstructSymbol: boundRecursivePattern.DeconstructMethod,
...@@ -1501,8 +1504,8 @@ public CSharpLazyRecursivePatternOperation(CSharpOperationFactory operationFacto ...@@ -1501,8 +1504,8 @@ public CSharpLazyRecursivePatternOperation(CSharpOperationFactory operationFacto
syntax: boundRecursivePattern.Syntax, syntax: boundRecursivePattern.Syntax,
isImplicit: boundRecursivePattern.WasCompilerGenerated) isImplicit: boundRecursivePattern.WasCompilerGenerated)
{ {
this._operationFactory = operationFactory; _operationFactory = operationFactory;
this._boundRecursivePattern = boundRecursivePattern; _boundRecursivePattern = boundRecursivePattern;
} }
public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns() public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns()
...@@ -1510,15 +1513,44 @@ public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpattern ...@@ -1510,15 +1513,44 @@ public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpattern
return _boundRecursivePattern.Deconstruction.IsDefault ? ImmutableArray<IPatternOperation>.Empty : return _boundRecursivePattern.Deconstruction.IsDefault ? ImmutableArray<IPatternOperation>.Empty :
_boundRecursivePattern.Deconstruction.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p.Pattern), _operationFactory); _boundRecursivePattern.Deconstruction.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p.Pattern), _operationFactory);
} }
public override ImmutableArray<(ISymbol, IPatternOperation)> CreatePropertySubpatterns() public override ImmutableArray<IPropertySubpatternOperation> CreatePropertySubpatterns()
{
return _boundRecursivePattern.Properties.IsDefault ? ImmutableArray<IPropertySubpatternOperation>.Empty :
_boundRecursivePattern.Properties.SelectAsArray((p, recursivePattern) => recursivePattern._operationFactory.CreatePropertySubpattern(p, recursivePattern.MatchedType), this);
}
}
internal sealed partial class CSharpLazyPropertySubpatternOperation : LazyPropertySubpatternOperation
{
private readonly BoundSubpattern _subpattern;
private readonly CSharpOperationFactory _operationFactory;
private readonly ITypeSymbol _matchedType;
public CSharpLazyPropertySubpatternOperation(
CSharpOperationFactory operationFactory,
BoundSubpattern subpattern,
ITypeSymbol matchedType,
SyntaxNode syntax,
SemanticModel semanticModel)
: base(semanticModel, syntax, isImplicit: false)
{
_subpattern = subpattern;
_operationFactory = operationFactory;
_matchedType = matchedType;
}
public override IOperation CreateMember()
{
return _operationFactory.CreatePropertySubpatternMember(_subpattern.Symbol, _matchedType, Syntax);
}
public override IPatternOperation CreatePattern()
{ {
return _boundRecursivePattern.Properties.IsDefault ? ImmutableArray<(ISymbol, IPatternOperation)>.Empty : return (IPatternOperation)_operationFactory.Create(_subpattern.Pattern);
_boundRecursivePattern.Properties.SelectAsArray((p, fac) => ((ISymbol)p.Symbol, (IPatternOperation)fac.Create(p.Pattern)), _operationFactory);
} }
} }
/// <summary> /// <summary>
/// Represents a C# recursive pattern. /// Represents a C# recursive pattern using ITuple.
/// </summary> /// </summary>
internal sealed partial class CSharpLazyITuplePatternOperation : LazyRecursivePatternOperation internal sealed partial class CSharpLazyITuplePatternOperation : LazyRecursivePatternOperation
{ {
...@@ -1534,8 +1566,8 @@ public CSharpLazyITuplePatternOperation(CSharpOperationFactory operationFactory, ...@@ -1534,8 +1566,8 @@ public CSharpLazyITuplePatternOperation(CSharpOperationFactory operationFactory,
syntax: boundITuplePattern.Syntax, syntax: boundITuplePattern.Syntax,
isImplicit: boundITuplePattern.WasCompilerGenerated) isImplicit: boundITuplePattern.WasCompilerGenerated)
{ {
this._operationFactory = operationFactory; _operationFactory = operationFactory;
this._boundITuplePattern = boundITuplePattern; _boundITuplePattern = boundITuplePattern;
} }
public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns() public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns()
...@@ -1543,9 +1575,9 @@ public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpattern ...@@ -1543,9 +1575,9 @@ public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpattern
return _boundITuplePattern.Subpatterns.IsDefault ? ImmutableArray<IPatternOperation>.Empty : return _boundITuplePattern.Subpatterns.IsDefault ? ImmutableArray<IPatternOperation>.Empty :
_boundITuplePattern.Subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p.Pattern), _operationFactory); _boundITuplePattern.Subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p.Pattern), _operationFactory);
} }
public override ImmutableArray<(ISymbol, IPatternOperation)> CreatePropertySubpatterns() public override ImmutableArray<IPropertySubpatternOperation> CreatePropertySubpatterns()
{ {
return ImmutableArray<(ISymbol, IPatternOperation)>.Empty; return ImmutableArray<IPropertySubpatternOperation>.Empty;
} }
} }
...@@ -1603,8 +1635,8 @@ internal sealed class CSharpLazySwitchExpressionOperation : LazySwitchExpression ...@@ -1603,8 +1635,8 @@ internal sealed class CSharpLazySwitchExpressionOperation : LazySwitchExpression
public CSharpLazySwitchExpressionOperation(CSharpOperationFactory operationFactory, BoundSwitchExpression boundSwitchExpression, SemanticModel semanticModel) public CSharpLazySwitchExpressionOperation(CSharpOperationFactory operationFactory, BoundSwitchExpression boundSwitchExpression, SemanticModel semanticModel)
: base(boundSwitchExpression.Type, semanticModel, boundSwitchExpression.Syntax, boundSwitchExpression.WasCompilerGenerated) : base(boundSwitchExpression.Type, semanticModel, boundSwitchExpression.Syntax, boundSwitchExpression.WasCompilerGenerated)
{ {
this._operationFactory = operationFactory; _operationFactory = operationFactory;
this._switchExpression = boundSwitchExpression; _switchExpression = boundSwitchExpression;
} }
protected override IOperation CreateValue() protected override IOperation CreateValue()
...@@ -1625,8 +1657,8 @@ internal sealed class CSharpLazySwitchExpressionArmOperation : LazySwitchExpress ...@@ -1625,8 +1657,8 @@ internal sealed class CSharpLazySwitchExpressionArmOperation : LazySwitchExpress
public CSharpLazySwitchExpressionArmOperation(CSharpOperationFactory operationFactory, BoundSwitchExpressionArm boundSwitchExpressionArm, SemanticModel semanticModel) public CSharpLazySwitchExpressionArmOperation(CSharpOperationFactory operationFactory, BoundSwitchExpressionArm boundSwitchExpressionArm, SemanticModel semanticModel)
: base(boundSwitchExpressionArm.Locals.Cast<CSharp.Symbols.LocalSymbol, ILocalSymbol>(), semanticModel, boundSwitchExpressionArm.Syntax, boundSwitchExpressionArm.WasCompilerGenerated) : base(boundSwitchExpressionArm.Locals.Cast<CSharp.Symbols.LocalSymbol, ILocalSymbol>(), semanticModel, boundSwitchExpressionArm.Syntax, boundSwitchExpressionArm.WasCompilerGenerated)
{ {
this._operationFactory = operationFactory; _operationFactory = operationFactory;
this._switchExpressionArm = boundSwitchExpressionArm; _switchExpressionArm = boundSwitchExpressionArm;
} }
protected override IOperation CreateGuard() protected override IOperation CreateGuard()
......
...@@ -6547,20 +6547,30 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation ...@@ -6547,20 +6547,30 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation
operation.Syntax, IsImplicit(operation)); operation.Syntax, IsImplicit(operation));
} }
public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, int? argument) internal override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, int? argument)
{ {
return new RecursivePatternOperation( return new RecursivePatternOperation(
inputType: operation.InputType, inputType: operation.InputType,
matchedType: operation.MatchedType, matchedType: operation.MatchedType,
operation.DeconstructSymbol, operation.DeconstructSymbol,
operation.DeconstructionSubpatterns.SelectAsArray(p => (IPatternOperation)Visit(p)), operation.DeconstructionSubpatterns.SelectAsArray(p => (IPatternOperation)Visit(p)),
operation.PropertySubpatterns.SelectAsArray(p => (p.Item1, (IPatternOperation)Visit(p.Item2))), operation.PropertySubpatterns.SelectAsArray(p => (IPropertySubpatternOperation)Visit(p)),
operation.DeclaredSymbol, operation.DeclaredSymbol,
semanticModel: null, semanticModel: null,
operation.Syntax, operation.Syntax,
IsImplicit(operation)); IsImplicit(operation));
} }
internal override IOperation VisitPropertySubpattern(IPropertySubpatternOperation operation, int? argument)
{
return new PropertySubpatternOperation(
semanticModel: null,
operation.Syntax,
IsImplicit(operation),
Visit(operation.Member),
(IPatternOperation)Visit(operation.Pattern));
}
public override IOperation VisitDelegateCreation(IDelegateCreationOperation operation, int? captureIdForResult) public override IOperation VisitDelegateCreation(IDelegateCreationOperation operation, int? captureIdForResult)
{ {
return new DelegateCreationOperation(Visit(operation.Target), semanticModel: null, return new DelegateCreationOperation(Visit(operation.Target), semanticModel: null,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Operations
{
/// <summary>
/// Represents an element of a property subpattern, which identifies a member to be matched and the
/// pattern to match it against.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
internal interface IPropertySubpatternOperation : IOperation
{
/// <summary>
/// The member being matched in a property subpattern. This can be a <see cref="IMemberReferenceOperation"/>
/// in non-error cases, or an <see cref="IInvalidOperation"/> in error cases.
/// </summary>
// The symbol should be exposed for error cases somehow:
// https://github.com/dotnet/roslyn/issues/33175
IOperation Member { get; }
/// <summary>
/// The pattern to which the member is matched in a property subpattern.
/// </summary>
IPatternOperation Pattern { get; }
}
}
...@@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Operations ...@@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Operations
/// This interface is reserved for implementation by its associated APIs. We reserve the right to /// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future. /// change it in the future.
/// </remarks> /// </remarks>
public interface IRecursivePatternOperation : IPatternOperation internal interface IRecursivePatternOperation : IPatternOperation
{ {
/// <summary> /// <summary>
/// The type accepted for the recursive pattern. /// The type accepted for the recursive pattern.
...@@ -33,7 +33,7 @@ public interface IRecursivePatternOperation : IPatternOperation ...@@ -33,7 +33,7 @@ public interface IRecursivePatternOperation : IPatternOperation
/// <summary> /// <summary>
/// This contains the (symbol, property) pairs within a property subpattern. /// This contains the (symbol, property) pairs within a property subpattern.
/// </summary> /// </summary>
ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } ImmutableArray<IPropertySubpatternOperation> PropertySubpatterns { get; }
/// <summary> /// <summary>
/// Symbol declared by the pattern. /// Symbol declared by the pattern.
......
...@@ -17,5 +17,9 @@ public enum InstanceReferenceKind ...@@ -17,5 +17,9 @@ public enum InstanceReferenceKind
/// anonymous type creation initializer, or to the object being referred to in a VB With statement. /// anonymous type creation initializer, or to the object being referred to in a VB With statement.
/// </summary> /// </summary>
ImplicitReceiver, ImplicitReceiver,
/// <summary>
/// Reference to the value being matching in a property subpattern.
/// </summary>
PatternInput,
} }
} }
...@@ -535,7 +535,7 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation ...@@ -535,7 +535,7 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation
((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit);
} }
public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, object argument) internal override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, object argument)
{ {
return new RecursivePatternOperation( return new RecursivePatternOperation(
operation.InputType, operation.InputType,
...@@ -549,6 +549,16 @@ public override IOperation VisitRecursivePattern(IRecursivePatternOperation oper ...@@ -549,6 +549,16 @@ public override IOperation VisitRecursivePattern(IRecursivePatternOperation oper
operation.IsImplicit); operation.IsImplicit);
} }
internal override IOperation VisitPropertySubpattern(IPropertySubpatternOperation operation, object argument)
{
return new PropertySubpatternOperation(
semanticModel: ((Operation)operation).OwningSemanticModel,
operation.Syntax,
operation.IsImplicit,
Visit(operation.Member),
Visit(operation.Pattern));
}
public override IOperation VisitPatternCaseClause(IPatternCaseClauseOperation operation, object argument) public override IOperation VisitPatternCaseClause(IPatternCaseClauseOperation operation, object argument)
{ {
return new PatternCaseClauseOperation(operation.Label, Visit(operation.Pattern), Visit(operation.Guard), ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); return new PatternCaseClauseOperation(operation.Label, Visit(operation.Pattern), Visit(operation.Guard), ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit);
......
...@@ -7965,7 +7965,8 @@ internal abstract partial class BaseRecursivePatternOperation : Operation, IRecu ...@@ -7965,7 +7965,8 @@ internal abstract partial class BaseRecursivePatternOperation : Operation, IRecu
ITypeSymbol inputType, ITypeSymbol inputType,
ITypeSymbol matchedType, ITypeSymbol matchedType,
ISymbol deconstructSymbol, ISymbol deconstructSymbol,
ISymbol declaredSymbol, SemanticModel semanticModel, ISymbol declaredSymbol,
SemanticModel semanticModel,
SyntaxNode syntax, SyntaxNode syntax,
bool isImplicit) bool isImplicit)
: base(OperationKind.RecursivePattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) : base(OperationKind.RecursivePattern, semanticModel, syntax, type: default, constantValue: default, isImplicit)
...@@ -7979,7 +7980,7 @@ internal abstract partial class BaseRecursivePatternOperation : Operation, IRecu ...@@ -7979,7 +7980,7 @@ internal abstract partial class BaseRecursivePatternOperation : Operation, IRecu
public ITypeSymbol MatchedType { get; } public ITypeSymbol MatchedType { get; }
public ISymbol DeconstructSymbol { get; } public ISymbol DeconstructSymbol { get; }
public abstract ImmutableArray<IPatternOperation> DeconstructionSubpatterns { get; } public abstract ImmutableArray<IPatternOperation> DeconstructionSubpatterns { get; }
public abstract ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } public abstract ImmutableArray<IPropertySubpatternOperation> PropertySubpatterns { get; }
public ISymbol DeclaredSymbol { get; } public ISymbol DeclaredSymbol { get; }
public override IEnumerable<IOperation> Children public override IEnumerable<IOperation> Children
{ {
...@@ -7992,7 +7993,7 @@ public override IEnumerable<IOperation> Children ...@@ -7992,7 +7993,7 @@ public override IEnumerable<IOperation> Children
foreach (var p in PropertySubpatterns) foreach (var p in PropertySubpatterns)
{ {
yield return p.Item2; yield return p;
} }
} }
} }
...@@ -8017,7 +8018,7 @@ internal sealed partial class RecursivePatternOperation : BaseRecursivePatternOp ...@@ -8017,7 +8018,7 @@ internal sealed partial class RecursivePatternOperation : BaseRecursivePatternOp
ITypeSymbol matchedType, ITypeSymbol matchedType,
ISymbol deconstructSymbol, ISymbol deconstructSymbol,
ImmutableArray<IPatternOperation> deconstructionSubpatterns, ImmutableArray<IPatternOperation> deconstructionSubpatterns,
ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns, ImmutableArray<IPropertySubpatternOperation> propertySubpatterns,
ISymbol declaredSymbol, SemanticModel semanticModel, ISymbol declaredSymbol, SemanticModel semanticModel,
SyntaxNode syntax, SyntaxNode syntax,
bool isImplicit) bool isImplicit)
...@@ -8025,32 +8026,30 @@ internal sealed partial class RecursivePatternOperation : BaseRecursivePatternOp ...@@ -8025,32 +8026,30 @@ internal sealed partial class RecursivePatternOperation : BaseRecursivePatternOp
{ {
SetParentOperation(deconstructionSubpatterns, this); SetParentOperation(deconstructionSubpatterns, this);
DeconstructionSubpatterns = deconstructionSubpatterns; DeconstructionSubpatterns = deconstructionSubpatterns;
foreach (var p in propertySubpatterns) SetParentOperation(propertySubpatterns, this);
{
SetParentOperation(p.Item2, this);
}
PropertySubpatterns = propertySubpatterns; PropertySubpatterns = propertySubpatterns;
} }
public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns { get; } public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns { get; }
public override ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } public override ImmutableArray<IPropertySubpatternOperation> PropertySubpatterns { get; }
} }
internal abstract partial class LazyRecursivePatternOperation : BaseRecursivePatternOperation internal abstract partial class LazyRecursivePatternOperation : BaseRecursivePatternOperation
{ {
private ImmutableArray<IPatternOperation> _lazyDeconstructionSubpatterns; private ImmutableArray<IPatternOperation> _lazyDeconstructionSubpatterns;
private ImmutableArray<(ISymbol, IPatternOperation)> _lazyPropertySubpatterns; private ImmutableArray<IPropertySubpatternOperation> _lazyPropertySubpatterns;
public LazyRecursivePatternOperation( public LazyRecursivePatternOperation(
ITypeSymbol inputType, ITypeSymbol inputType,
ITypeSymbol matchedType, ITypeSymbol matchedType,
ISymbol deconstructSymbol, ISymbol deconstructSymbol,
ISymbol declaredSymbol, SemanticModel semanticModel, ISymbol declaredSymbol,
SemanticModel semanticModel,
SyntaxNode syntax, SyntaxNode syntax,
bool isImplicit) bool isImplicit)
: base(inputType, matchedType, deconstructSymbol, declaredSymbol, semanticModel, syntax, isImplicit) : base(inputType, matchedType, deconstructSymbol, declaredSymbol, semanticModel, syntax, isImplicit)
{ {
} }
public abstract ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns(); public abstract ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns();
public abstract ImmutableArray<(ISymbol, IPatternOperation)> CreatePropertySubpatterns(); public abstract ImmutableArray<IPropertySubpatternOperation> CreatePropertySubpatterns();
public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns
{ {
get get
...@@ -8069,7 +8068,7 @@ public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns ...@@ -8069,7 +8068,7 @@ public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns
return _lazyDeconstructionSubpatterns; return _lazyDeconstructionSubpatterns;
} }
} }
public override ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns public override ImmutableArray<IPropertySubpatternOperation> PropertySubpatterns
{ {
get get
{ {
...@@ -8078,7 +8077,7 @@ public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns ...@@ -8078,7 +8077,7 @@ public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns
var propertySubpatterns = CreatePropertySubpatterns(); var propertySubpatterns = CreatePropertySubpatterns();
foreach (var propertySubpattern in propertySubpatterns) foreach (var propertySubpattern in propertySubpatterns)
{ {
SetParentOperation(propertySubpattern.Item2, this); SetParentOperation(propertySubpattern, this);
} }
ImmutableInterlocked.InterlockedInitialize(ref _lazyPropertySubpatterns, propertySubpatterns); ImmutableInterlocked.InterlockedInitialize(ref _lazyPropertySubpatterns, propertySubpatterns);
...@@ -8089,6 +8088,100 @@ public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns ...@@ -8089,6 +8088,100 @@ public override ImmutableArray<IPatternOperation> DeconstructionSubpatterns
} }
} }
internal abstract partial class BasePropertySubpatternOperation : Operation, IPropertySubpatternOperation
{
public BasePropertySubpatternOperation(
SemanticModel semanticModel,
SyntaxNode syntax,
bool isImplicit)
: base(OperationKind.None, semanticModel, syntax, type: default, constantValue: default, isImplicit)
{
}
public abstract IOperation Member { get; }
public abstract IPatternOperation Pattern { get; }
public override IEnumerable<IOperation> Children
{
get
{
if (Member != null)
yield return Member;
if (Pattern != null)
yield return Pattern;
}
}
public override void Accept(OperationVisitor visitor)
{
visitor.VisitPropertySubpattern(this);
}
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitPropertySubpattern(this, argument);
}
}
internal sealed partial class PropertySubpatternOperation : BasePropertySubpatternOperation
{
public PropertySubpatternOperation(
SemanticModel semanticModel,
SyntaxNode syntax,
bool isImplicit,
IOperation member,
IPatternOperation pattern)
: base(semanticModel, syntax, isImplicit)
{
SetParentOperation(member, this);
Member = member;
SetParentOperation(pattern, this);
Pattern = pattern;
}
public override IOperation Member { get; }
public override IPatternOperation Pattern { get; }
}
internal abstract partial class LazyPropertySubpatternOperation : BasePropertySubpatternOperation
{
private IOperation _lazyMember = s_unset;
private IPatternOperation _lazyPattern = s_unsetPattern;
public LazyPropertySubpatternOperation(
SemanticModel semanticModel,
SyntaxNode syntax,
bool isImplicit)
: base(semanticModel, syntax, isImplicit)
{
}
public abstract IOperation CreateMember();
public abstract IPatternOperation CreatePattern();
public override IOperation Member
{
get
{
if (_lazyMember == s_unset)
{
var member = CreateMember();
SetParentOperation(member, this);
Interlocked.CompareExchange(ref _lazyMember, member, s_unset);
}
return _lazyMember;
}
}
public override IPatternOperation Pattern
{
get
{
if (_lazyPattern == s_unsetPattern)
{
var pattern = CreatePattern();
SetParentOperation(pattern, this);
Interlocked.CompareExchange(ref _lazyPattern, pattern, s_unsetPattern);
}
return _lazyPattern;
}
}
}
/// <summary> /// <summary>
/// Represents a C# pattern case clause. /// Represents a C# pattern case clause.
/// </summary> /// </summary>
......
...@@ -505,7 +505,12 @@ public virtual void VisitDeclarationPattern(IDeclarationPatternOperation operati ...@@ -505,7 +505,12 @@ public virtual void VisitDeclarationPattern(IDeclarationPatternOperation operati
DefaultVisit(operation); DefaultVisit(operation);
} }
public virtual void VisitRecursivePattern(IRecursivePatternOperation operation) internal virtual void VisitRecursivePattern(IRecursivePatternOperation operation)
{
DefaultVisit(operation);
}
internal virtual void VisitPropertySubpattern(IPropertySubpatternOperation operation)
{ {
DefaultVisit(operation); DefaultVisit(operation);
} }
...@@ -1113,7 +1118,12 @@ public virtual TResult VisitDeclarationPattern(IDeclarationPatternOperation oper ...@@ -1113,7 +1118,12 @@ public virtual TResult VisitDeclarationPattern(IDeclarationPatternOperation oper
return DefaultVisit(operation, argument); return DefaultVisit(operation, argument);
} }
public virtual TResult VisitRecursivePattern(IRecursivePatternOperation operation, TArgument argument) internal virtual TResult VisitRecursivePattern(IRecursivePatternOperation operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
internal virtual TResult VisitPropertySubpattern(IPropertySubpatternOperation operation, TArgument argument)
{ {
return DefaultVisit(operation, argument); return DefaultVisit(operation, argument);
} }
......
...@@ -16,15 +16,10 @@ Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.MatchedType.get - ...@@ -16,15 +16,10 @@ Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.MatchedType.get -
Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.MatchesNull.get -> bool Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.MatchesNull.get -> bool
Microsoft.CodeAnalysis.Operations.IDiscardPatternOperation Microsoft.CodeAnalysis.Operations.IDiscardPatternOperation
Microsoft.CodeAnalysis.Operations.IPatternOperation.InputType.get -> Microsoft.CodeAnalysis.ITypeSymbol Microsoft.CodeAnalysis.Operations.IPatternOperation.InputType.get -> Microsoft.CodeAnalysis.ITypeSymbol
Microsoft.CodeAnalysis.Operations.InstanceReferenceKind.PatternInput = 2 -> Microsoft.CodeAnalysis.Operations.InstanceReferenceKind
Microsoft.CodeAnalysis.SymbolDisplayPartKind.ConstantName = 30 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind Microsoft.CodeAnalysis.SymbolDisplayPartKind.ConstantName = 30 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind
Microsoft.CodeAnalysis.SymbolDisplayPartKind.EnumMemberName = 28 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind Microsoft.CodeAnalysis.SymbolDisplayPartKind.EnumMemberName = 28 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind
Microsoft.CodeAnalysis.SymbolDisplayPartKind.ExtensionMethodName = 29 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind Microsoft.CodeAnalysis.SymbolDisplayPartKind.ExtensionMethodName = 29 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeclaredSymbol.get -> Microsoft.CodeAnalysis.ISymbol
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructSymbol.get -> Microsoft.CodeAnalysis.ISymbol
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructionSubpatterns.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Operations.IPatternOperation>
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.MatchedType.get -> Microsoft.CodeAnalysis.ITypeSymbol
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.PropertySubpatterns.get -> System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.ISymbol, Microsoft.CodeAnalysis.Operations.IPatternOperation)>
Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation
Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation.Guard.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation.Guard.get -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation.Locals.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ILocalSymbol> Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation.Locals.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ILocalSymbol>
...@@ -212,7 +207,6 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitIsNull(Microsoft ...@@ -212,7 +207,6 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitIsNull(Microsoft
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRangeOperation(Microsoft.CodeAnalysis.Operations.IRangeOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRangeOperation(Microsoft.CodeAnalysis.Operations.IRangeOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitReDim(Microsoft.CodeAnalysis.Operations.IReDimOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitReDim(Microsoft.CodeAnalysis.Operations.IReDimOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitReDimClause(Microsoft.CodeAnalysis.Operations.IReDimClauseOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitReDimClause(Microsoft.CodeAnalysis.Operations.IReDimClauseOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitStaticLocalInitializationSemaphore(Microsoft.CodeAnalysis.FlowAnalysis.IStaticLocalInitializationSemaphoreOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitStaticLocalInitializationSemaphore(Microsoft.CodeAnalysis.FlowAnalysis.IStaticLocalInitializationSemaphoreOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitSwitchExpression(Microsoft.CodeAnalysis.Operations.ISwitchExpressionOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitSwitchExpression(Microsoft.CodeAnalysis.Operations.ISwitchExpressionOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitSwitchExpressionArm(Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitSwitchExpressionArm(Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation operation) -> void
...@@ -226,7 +220,6 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.V ...@@ -226,7 +220,6 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.V
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitRangeOperation(Microsoft.CodeAnalysis.Operations.IRangeOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitRangeOperation(Microsoft.CodeAnalysis.Operations.IRangeOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitReDim(Microsoft.CodeAnalysis.Operations.IReDimOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitReDim(Microsoft.CodeAnalysis.Operations.IReDimOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitReDimClause(Microsoft.CodeAnalysis.Operations.IReDimClauseOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitReDimClause(Microsoft.CodeAnalysis.Operations.IReDimClauseOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitStaticLocalInitializationSemaphore(Microsoft.CodeAnalysis.FlowAnalysis.IStaticLocalInitializationSemaphoreOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitStaticLocalInitializationSemaphore(Microsoft.CodeAnalysis.FlowAnalysis.IStaticLocalInitializationSemaphoreOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitSwitchExpression(Microsoft.CodeAnalysis.Operations.ISwitchExpressionOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitSwitchExpression(Microsoft.CodeAnalysis.Operations.ISwitchExpressionOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitSwitchExpressionArm(Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitSwitchExpressionArm(Microsoft.CodeAnalysis.Operations.ISwitchExpressionArmOperation operation, TArgument argument) -> TResult
...@@ -1728,14 +1728,16 @@ private static bool CanBeInControlFlowGraph(IOperation n) ...@@ -1728,14 +1728,16 @@ private static bool CanBeInControlFlowGraph(IOperation n)
case OperationKind.InstanceReference: case OperationKind.InstanceReference:
// Implicit instance receivers, except for anonymous type creations, are expected to have been removed when dealing with creations. // Implicit instance receivers, except for anonymous type creations, are expected to have been removed when dealing with creations.
return ((IInstanceReferenceOperation)n).ReferenceKind == InstanceReferenceKind.ContainingTypeInstance || var instanceReference = (IInstanceReferenceOperation)n;
((IInstanceReferenceOperation)n).ReferenceKind == InstanceReferenceKind.ImplicitReceiver && return instanceReference.ReferenceKind == InstanceReferenceKind.ContainingTypeInstance ||
n.Type.IsAnonymousType && instanceReference.ReferenceKind == InstanceReferenceKind.PatternInput ||
n.Parent is IPropertyReferenceOperation propertyReference && (instanceReference.ReferenceKind == InstanceReferenceKind.ImplicitReceiver &&
propertyReference.Instance == n && n.Type.IsAnonymousType &&
propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && n.Parent is IPropertyReferenceOperation propertyReference &&
simpleAssignment.Target == propertyReference && propertyReference.Instance == n &&
simpleAssignment.Parent.Kind == OperationKind.AnonymousObjectCreation; propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment &&
simpleAssignment.Target == propertyReference &&
simpleAssignment.Parent.Kind == OperationKind.AnonymousObjectCreation);
case OperationKind.None: case OperationKind.None:
return !(n is IPlaceholderOperation); return !(n is IPlaceholderOperation);
......
...@@ -1762,7 +1762,7 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat ...@@ -1762,7 +1762,7 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat
LogNewLine(); LogNewLine();
} }
public override void VisitRecursivePattern(IRecursivePatternOperation operation) internal override void VisitRecursivePattern(IRecursivePatternOperation operation)
{ {
LogString(nameof(IRecursivePatternOperation)); LogString(nameof(IRecursivePatternOperation));
LogPatternProperties(operation); LogPatternProperties(operation);
...@@ -1773,11 +1773,17 @@ public override void VisitRecursivePattern(IRecursivePatternOperation operation) ...@@ -1773,11 +1773,17 @@ public override void VisitRecursivePattern(IRecursivePatternOperation operation)
LogNewLine(); LogNewLine();
VisitArray(operation.DeconstructionSubpatterns, $"{nameof(operation.DeconstructionSubpatterns)} ", true, true); VisitArray(operation.DeconstructionSubpatterns, $"{nameof(operation.DeconstructionSubpatterns)} ", true, true);
VisitArrayCommon(operation.PropertySubpatterns, $"{nameof(operation.PropertySubpatterns)} ", true, true, subpat => VisitArray(operation.PropertySubpatterns, $"{nameof(operation.PropertySubpatterns)} ", true, true);
{ }
LogSymbol(subpat.Item1, "MatchedSymbol");
Visit(subpat.Item2, ", Pattern"); internal override void VisitPropertySubpattern(IPropertySubpatternOperation operation)
}); {
LogString(nameof(IPropertySubpatternOperation));
LogCommonProperties(operation);
LogNewLine();
Visit(operation.Member, $"{nameof(operation.Member)}");
Visit(operation.Pattern, $"{nameof(operation.Pattern)}");
} }
public override void VisitIsPattern(IIsPatternOperation operation) public override void VisitIsPattern(IIsPatternOperation operation)
......
...@@ -1151,7 +1151,7 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat ...@@ -1151,7 +1151,7 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat
Assert.Empty(operation.Children); Assert.Empty(operation.Children);
} }
public override void VisitRecursivePattern(IRecursivePatternOperation operation) internal override void VisitRecursivePattern(IRecursivePatternOperation operation)
{ {
Assert.Equal(OperationKind.RecursivePattern, operation.Kind); Assert.Equal(OperationKind.RecursivePattern, operation.Kind);
VisitPatternCommon(operation); VisitPatternCommon(operation);
...@@ -1186,25 +1186,38 @@ public override void VisitRecursivePattern(IRecursivePatternOperation operation) ...@@ -1186,25 +1186,38 @@ public override void VisitRecursivePattern(IRecursivePatternOperation operation)
foreach (var subpat in operation.PropertySubpatterns) foreach (var subpat in operation.PropertySubpatterns)
{ {
var (symbol, pattern) = subpat; Assert.True(subpat is IPropertySubpatternOperation);
switch (symbol)
{
case null: // error case
break;
case IFieldSymbol field:
case IPropertySymbol prop:
case IErrorTypeSymbol error:
break;
default:
Assert.True(false, $"Unexpected symbol {symbol}");
break;
}
} }
IEnumerable<IOperation> children = operation.DeconstructionSubpatterns.Cast<IOperation>(); IEnumerable<IOperation> children = operation.DeconstructionSubpatterns.Cast<IOperation>();
children = children.Concat(operation.PropertySubpatterns.Select(x => x.Item2)); children = children.Concat(operation.PropertySubpatterns);
AssertEx.Equal(children, operation.Children);
}
internal override void VisitPropertySubpattern(IPropertySubpatternOperation operation)
{
Assert.NotNull(operation.Pattern);
var children = new IOperation[] { operation.Member, operation.Pattern };
AssertEx.Equal(children, operation.Children); AssertEx.Equal(children, operation.Children);
if (operation.Member.Kind == OperationKind.Invalid)
{
return;
}
Assert.True(operation.Member is IMemberReferenceOperation);
var member = (IMemberReferenceOperation)operation.Member;
switch (member.Member)
{
case IFieldSymbol field:
case IPropertySymbol prop:
case IErrorTypeSymbol error:
break;
case var symbol:
Assert.True(false, $"Unexpected symbol {symbol}");
break;
}
} }
public override void VisitSwitchExpression(ISwitchExpressionOperation operation) public override void VisitSwitchExpression(ISwitchExpressionOperation operation)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Diagnostics; using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.CodeAnalysis namespace Microsoft.CodeAnalysis
...@@ -57,7 +58,7 @@ public static ValueUsageInfo GetValueUsageInfo(this IOperation operation) ...@@ -57,7 +58,7 @@ public static ValueUsageInfo GetValueUsageInfo(this IOperation operation)
// //
return ValueUsageInfo.Write; return ValueUsageInfo.Write;
case IRecursivePatternOperation _: case IOperation iop when iop.GetType().GetInterfaces().Any(i => i.Name == "IRecursivePatternOperation"):
// A declaration pattern within a recursive pattern is a // A declaration pattern within a recursive pattern is a
// write for the declared local. // write for the declared local.
// For example, 'x' is defined and assigned the value from 'obj' below: // For example, 'x' is defined and assigned the value from 'obj' below:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册