diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs index 8f8c2f0b8f66..59e5a75488ce 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Operations; @@ -53,22 +54,18 @@ public bool IsSuppressMessageAttributeWithNamedArguments( CancellationToken cancellationToken, out ImmutableArray<(string name, IOperation value)> namedAttributeArguments) { - var attribute = model.GetOperation(attributeSyntax, cancellationToken); - if (attribute == null) + var operation = (model.GetOperation(attributeSyntax, cancellationToken) as IAttributeOperation)?.Operation; + if (operation is not IObjectCreationOperation { Initializer: { } initializerOperation }) { namedAttributeArguments = ImmutableArray<(string name, IOperation value)>.Empty; return false; } - // Workaround for https://github.com/dotnet/roslyn/issues/18198 - // Use 'IOperation.Children' to get named attribute arguments. - // Each named attribute argument is represented as an 'ISimpleAssignmentOperation' - // with a constant value assignment to an 'IPropertyReferenceOperation' in the operation tree. using var _ = ArrayBuilder<(string name, IOperation value)>.GetInstance(out var builder); - foreach (var childOperation in attribute.ChildOperations) + foreach (var initializer in initializerOperation.Initializers) { - if (childOperation is ISimpleAssignmentOperation simpleAssignment && - simpleAssignment.Target is IPropertyReferenceOperation propertyReference && + var simpleAssignment = (ISimpleAssignmentOperation)initializer; + if (simpleAssignment.Target is IPropertyReferenceOperation propertyReference && _suppressMessageAttributeType.Equals(propertyReference.Property.ContainingType)) { builder.Add((propertyReference.Property.Name, simpleAssignment.Value)); diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index 52fb5b1a6786..4ced79a25386 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -183,27 +183,14 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. - // 3. Operations with OperationKind.None which are not operation root nodes. Attributes - // generate operation blocks with root operation with OperationKind.None, and we don't want to bail out for them. - symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, + // 3. Operations with OperationKind.None. + symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, OperationKind.None, OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); - symbolStartContext.RegisterOperationAction(AnalyzeOperationNone, OperationKind.None); symbolStartContext.RegisterSymbolEndAction(symbolEndContext => OnSymbolEnd(symbolEndContext, hasUnsupportedOperation)); // Register custom language-specific actions, if any. _analyzer.HandleNamedTypeSymbolStart(symbolStartContext, onSymbolUsageFound); - - return; - - void AnalyzeOperationNone(OperationAnalysisContext context) - { - if (context.Operation.Kind == OperationKind.None && - context.Operation.Parent != null) - { - hasUnsupportedOperation = true; - } - } }, SymbolKind.NamedType); } diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs index 50d34cba2d25..17a689972a9d 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs @@ -402,9 +402,9 @@ private bool ShouldAnalyze(IOperation operationBlock, ISymbol owningSymbol, ref { switch (operationBlock.Kind) { - case OperationKind.None: + case OperationKind.Attribute: case OperationKind.ParameterInitializer: - // Skip blocks from attributes (which have OperationKind.None) and parameter initializers. + // Skip blocks from attributes and parameter initializers. // We don't have any unused values in such operation blocks. return false; diff --git a/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs index 26afb3c26636..3b5873996b3f 100644 --- a/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs @@ -49,7 +49,7 @@ internal Symbol AttributedMember /// /// Walk up to the nearest method/property/event. /// - private static Symbol GetAttributedMember(Symbol symbol) + internal static Symbol GetAttributedMember(Symbol symbol) { for (; (object)symbol != null; symbol = symbol.ContainingSymbol) { diff --git a/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs index c6b787d32340..595d77d4d393 100644 --- a/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs @@ -16,10 +16,12 @@ namespace Microsoft.CodeAnalysis.CSharp internal sealed class AttributeSemanticModel : MemberSemanticModel { private readonly AliasSymbol _aliasOpt; + private readonly Symbol? _attributeTarget; private AttributeSemanticModel( AttributeSyntax syntax, NamedTypeSymbol attributeType, + Symbol? attributeTarget, AliasSymbol aliasOpt, Binder rootBinder, SyntaxTreeSemanticModel? containingSemanticModelOpt = null, @@ -30,6 +32,7 @@ private AttributeSemanticModel( { Debug.Assert(syntax != null); _aliasOpt = aliasOpt; + _attributeTarget = attributeTarget; } /// @@ -38,7 +41,7 @@ private AttributeSemanticModel( public static AttributeSemanticModel Create(SyntaxTreeSemanticModel containingSemanticModel, AttributeSyntax syntax, NamedTypeSymbol attributeType, AliasSymbol aliasOpt, Symbol? attributeTarget, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt) { rootBinder = attributeTarget is null ? rootBinder : new ContextualAttributeBinder(rootBinder, attributeTarget); - return new AttributeSemanticModel(syntax, attributeType, aliasOpt, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); + return new AttributeSemanticModel(syntax, attributeType, attributeTarget, aliasOpt, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); } /// @@ -49,7 +52,22 @@ public static AttributeSemanticModel CreateSpeculative(SyntaxTreeSemanticModel p Debug.Assert(parentSemanticModel != null); Debug.Assert(rootBinder != null); Debug.Assert(rootBinder.IsSemanticModelBinder); - return new AttributeSemanticModel(syntax, attributeType, aliasOpt, rootBinder, parentSemanticModelOpt: parentSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition: position); + + var attributeTarget = GetAttributeTargetFromPosition(position, parentSemanticModel); + return new AttributeSemanticModel(syntax, attributeType, attributeTarget, aliasOpt, rootBinder, parentSemanticModelOpt: parentSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition: position); + } + + private static Symbol? GetAttributeTargetFromPosition(int position, SemanticModel model) + { + var attributedNode = model.SyntaxTree.GetRoot().FindToken(position).Parent; + attributedNode = attributedNode?.FirstAncestorOrSelf()?.Parent; + + if (attributedNode is not null) + { + return model.GetDeclaredSymbolForNode(attributedNode).GetSymbol(); + } + + return null; } private NamedTypeSymbol AttributeType @@ -89,8 +107,7 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDi if (node.Kind() == SyntaxKind.Attribute) { var attribute = (AttributeSyntax)node; - // note: we should find the attributed member before binding the attribute as part of https://github.com/dotnet/roslyn/issues/53618 - return binder.BindAttribute(attribute, AttributeType, attributedMember: null, diagnostics); + return binder.BindAttribute(attribute, AttributeType, attributedMember: ContextualAttributeBinder.GetAttributedMember(_attributeTarget), diagnostics); } else if (SyntaxFacts.IsAttributeName(node)) { diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 39a43fefda05..4865ca840825 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -280,8 +279,9 @@ public CSharpOperationFactory(SemanticModel semanticModel) return CreateBoundInterpolatedStringArgumentPlaceholder((BoundInterpolatedStringArgumentPlaceholder)boundNode); case BoundKind.InterpolatedStringHandlerPlaceholder: return CreateBoundInterpolatedStringHandlerPlaceholder((BoundInterpolatedStringHandlerPlaceholder)boundNode); - case BoundKind.Attribute: + return CreateBoundAttributeOperation((BoundAttribute)boundNode); + case BoundKind.ArgList: case BoundKind.ArgListOperator: case BoundKind.ConvertedStackAllocExpression: @@ -493,6 +493,27 @@ private IOperation CreateBoundUnconvertedAddressOfOperatorOperation(BoundUnconve boundUnconvertedAddressOf.WasCompilerGenerated); } + private IOperation CreateBoundAttributeOperation(BoundAttribute boundAttribute) + { + var isAttributeImplicit = boundAttribute.WasCompilerGenerated; + if (boundAttribute.Constructor is null) + { + var invalidOperation = OperationFactory.CreateInvalidOperation(_semanticModel, boundAttribute.Syntax, GetIOperationChildren(boundAttribute), isImplicit: true); + return new AttributeOperation(invalidOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit); + } + + ObjectOrCollectionInitializerOperation? initializer = null; + if (!boundAttribute.NamedArguments.IsEmpty) + { + var namedArguments = CreateFromArray(boundAttribute.NamedArguments); + initializer = new ObjectOrCollectionInitializerOperation(namedArguments, _semanticModel, boundAttribute.Syntax, boundAttribute.GetPublicTypeSymbol(), isImplicit: true); + Debug.Assert(initializer.Initializers.All(i => i is ISimpleAssignmentOperation)); + } + + var objectCreationOperation = new ObjectCreationOperation(boundAttribute.Constructor.GetPublicSymbol(), initializer, DeriveArguments(boundAttribute), _semanticModel, boundAttribute.Syntax, boundAttribute.GetPublicTypeSymbol(), boundAttribute.ConstantValue, isImplicit: true); + return new AttributeOperation(objectCreationOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit); + } + internal ImmutableArray CreateIgnoredDimensions(BoundNode declaration, SyntaxNode declarationSyntax) { switch (declaration.Kind) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs index fd1b944398b3..7ed9a5fcb260 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs @@ -37,7 +37,7 @@ internal IArgumentOperation CreateArgumentOperation(ArgumentKind kind, IParamete { // put argument syntax to argument operation IOperation value = Create(expression); - (SyntaxNode syntax, bool isImplicit) = expression.Syntax is { Parent: ArgumentSyntax parent } ? (parent, expression.WasCompilerGenerated) : (value.Syntax, true); + (SyntaxNode syntax, bool isImplicit) = expression.Syntax is { Parent: ArgumentSyntax or AttributeArgumentSyntax } ? (expression.Syntax.Parent, expression.WasCompilerGenerated) : (value.Syntax, true); return new ArgumentOperation( kind, parameter, @@ -238,6 +238,15 @@ internal ImmutableArray DeriveArguments(BoundNode containing objectCreation.Expanded, objectCreation.Syntax); } + case BoundKind.Attribute: + var attribute = (BoundAttribute)containingExpression; + Debug.Assert(attribute.Constructor is not null); + return DeriveArguments(attribute.Constructor, + attribute.ConstructorArguments, + attribute.ConstructorArgumentsToParamsOpt, + attribute.ConstructorDefaultArguments, + attribute.ConstructorExpanded, + attribute.Syntax); case BoundKind.Call: { var boundCall = (BoundCall)containingExpression; diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs index 6fc4dd25fc3f..edf8c2bc5431 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs @@ -714,17 +714,21 @@ void M0() { } Assert.Equal("M0", attrs.Single().ConstructorArguments.Single().Value); var operation = semanticModel.GetOperation(attrSyntax); - // note: this operation tree should contain a constant string "M0" instead of null. - // this should ideally be fixed as part of https://github.com/dotnet/roslyn/issues/53618. VerifyOperationTree(comp, operation, @" -IOperation: (OperationKind.None, Type: Attr) (Syntax: 'Attr()') - Children(1): - IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr()') +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "); } [Fact] - public void TestAttributeCallerInfoSemanticModel_Speculative() + public void TestAttributeCallerInfoSemanticModel_Method_Speculative() { var source = @" using System; @@ -752,12 +756,182 @@ void M0() { } Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, out var speculativeModel)); var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); - // note: this operation tree should contain a constant string "M0" instead of null. - // this should ideally be fixed as part of https://github.com/dotnet/roslyn/issues/53618. VerifyOperationTree(comp, speculativeOperation, @" -IOperation: (OperationKind.None, Type: Attr) (Syntax: 'Attr()') - Children(1): - IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr()') +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Method_Speculative2() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +class Attr : Attribute { public Attr([CallerMemberName] string s = null) { } } + +class C +{ + private const string World = ""World""; + + [Attr($""Hello {World}"")] + void M0() { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().Last(); + var interpolationSyntax = root.DescendantNodes().OfType().Single(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().Last(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(interpolationSyntax.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Parameter_Speculative() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +class Attr : Attribute { public Attr([CallerMemberName] string s = null) { } } + +class C +{ + void M0([Attr(""a"")] int x) { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().Last(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().Last(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Class_Speculative() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +class Attr : Attribute { public Attr([CallerMemberName] string s = null) { } } + +[Attr(""a"")] +class C +{ +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().Last(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().Last(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Speculative_AssemblyTarget() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +[assembly: Attr(""a"")] + +class Attr : Attribute { public Attr([CallerMemberName] string s = ""default_value"") { } } + +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().First(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().First(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = ""default_value""])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""default_value"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs index 1e264c3027a4..1a324bc58ca5 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs @@ -3544,9 +3544,15 @@ public void DirectlyBindArgument_Attribute() [assembly: /**/System.CLSCompliant(isCompliant: true)/**/] "; string expectedOperationTree = @" -IOperation: (OperationKind.None, Type: System.CLSCompliantAttribute) (Syntax: 'System.CLSC ... iant: true)') - Children(1): - ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'System.CLSC ... iant: true)') + IObjectCreationOperation (Constructor: System.CLSCompliantAttribute..ctor(System.Boolean isCompliant)) (OperationKind.ObjectCreation, Type: System.CLSCompliantAttribute, IsImplicit) (Syntax: 'System.CLSC ... iant: true)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: isCompliant) (OperationKind.Argument, Type: null) (Syntax: 'isCompliant: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.cs new file mode 100644 index 000000000000..d266ce865a97 --- /dev/null +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.cs @@ -0,0 +1,1302 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + [CompilerTrait(CompilerFeature.IOperation)] + public class IOperationTests_IAttributeOperation : SemanticModelTestBase + { + [Fact] + public void TestCallerInfoImplicitCall() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerLineNumber] int lineNumber = -1) + { + Console.WriteLine(lineNumber); + } +} + +[/**/My/**/] +class Test { } +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Int32 lineNumber = -1])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: lineNumber) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 13, IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + string expectedFlowGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (1) + IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Int32 lineNumber = -1])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: lineNumber) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 13, IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null + Next (Regular) Block[B2] +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact] + public void TestCallerMemberName_Class() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string callerName = """") + { + Console.WriteLine(callerName); + } +} + +[/**/My/**/] +class Test +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String callerName = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: callerName) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: """", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestCallerMemberName_Method() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string callerName = """") + { + Console.WriteLine(callerName); + } +} + +class Test +{ + [/**/My/**/] + public void M() { } +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String callerName = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: callerName) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestCallerMemberName_Parameter() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +public class C +{ + public void M([/**/My/**/] int x) + { + } +} + +internal class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string x = null) {} +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String x = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: x) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestNonExistingAttribute() + { + string source = @" +using System; + +[/**/My/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My') + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'My') + Children(0) +"; + var expectedDiagnostics = new[] + { + // (4,12): error CS0246: The type or namespace name 'MyAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [/**/My/**/] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "My").WithArguments("MyAttribute").WithLocation(4, 12), + // (4,12): error CS0246: The type or namespace name 'My' could not be found (are you missing a using directive or an assembly reference?) + // [/**/My/**/] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "My").WithArguments("My").WithLocation(4, 12), + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithoutArguments() + { + string source = @" +using System; + +class MyAttribute : Attribute { } + +[/**/My/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor()) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(0) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithExplicitArgument() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value) { } +} + +[/**/My(""Value"")/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(""Value"")') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.String value)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(""Value"")') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '""Value""') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""Value"") (Syntax: '""Value""') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithExplicitArgument_IncorrectTypePassed() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value) { } +} + +[/**/My(0)/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My(0)') + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'My(0)') + Children(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') +"; + var expectedDiagnostics = new[] + { + // (9,15): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // [/**/My(0)/**/] + Diagnostic(ErrorCode.ERR_BadArgType, "0").WithArguments("1", "int", "string").WithLocation(9, 15) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithExplicitArgumentOptionalParameter() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value = """") { } +} + +[/**/My(""Value"")/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(""Value"")') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String value = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(""Value"")') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '""Value""') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""Value"") (Syntax: '""Value""') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void TestAttributeWithOptionalParameterNotPassed(bool withParentheses) + { + string attribute = withParentheses ? "My()" : "My"; + + string attributeListSyntax = $"[/**/{attribute}/**/]"; + + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value = """") { } +} +" + attributeListSyntax + @" +class C +{ +} +"; + string expectedOperationTree = $@" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: '{attribute}') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String value = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: '{attribute}') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{attribute}') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: """", IsImplicit) (Syntax: '{attribute}') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithUnorderedArguments() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int a, int b) { } +} + +[/**/My(b: 1, a: 0)/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(b: 1, a: 0)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 a, System.Int32 b)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(b: 1, a: 0)') + Arguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'a: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithUnorderedArgumentsAndOptionalParameters() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int a, int b, int c = 2, int d = 3) { } +} + +[/**/My(b: 1, a: 0, d: 5)/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(b: 1, a: 0, d: 5)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 a, System.Int32 b, [System.Int32 c = 2], [System.Int32 d = 3])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(b: 1, a: 0, d: 5)') + Arguments(4): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'a: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: d) (OperationKind.Argument, Type: null) (Syntax: 'd: 5') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(b: 1, a: 0, d: 5)') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: 'My(b: 1, a: 0, d: 5)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestConversion() + { + string source = @" +using System; + + +[/**/My(0.0f)/**/] +class MyAttribute : Attribute +{ + public MyAttribute(double x) { } +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(0.0f)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Double x)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(0.0f)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '0.0f') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, Constant: 0, IsImplicit) (Syntax: '0.0f') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Single, Constant: 0) (Syntax: '0.0f') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void SwitchExpression_Attribute() + { + string source = @" +using System; +class Program +{ + [/**/My(1 switch { 1 => 1, _ => 2 })/**/] + public static void M1() { } +} +public class MyAttribute : Attribute +{ + public MyAttribute(int Value) { } +} +public class A +{ + public static implicit operator int(A a) => 4; +} +public class B +{ + public static implicit operator int(B b) => 2; +} +"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (5,19): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [/**/My(1 switch { 1 => 1, _ => 2 })/**/] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "1 switch { 1 => 1, _ => 2 }").WithLocation(5, 19), + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My(1 switch ... , _ => 2 })') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 Value)) (OperationKind.ObjectCreation, Type: MyAttribute, IsInvalid, IsImplicit) (Syntax: 'My(1 switch ... , _ => 2 })') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Value) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: '1 switch { ... 1, _ => 2 }') + ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32, IsInvalid) (Syntax: '1 switch { ... 1, _ => 2 }') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + Arms(2): + ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => 1') + Pattern: + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => 2') + Pattern: + IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void BadAttributeParameterType() + { + string source = @" +[/**/Boom/**/] +class Boom : System.Attribute +{ + public Boom(int? x = 0) { } + + static void Main() + { + typeof(Boom).GetCustomAttributes(true); + } +}"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (2,2): error CS0181: Attribute constructor parameter 'x' has type 'int?', which is not a valid attribute parameter type + // [/**/Boom/**/] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Boom").WithArguments("x", "int?").WithLocation(2, 12) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'Boom') + IObjectCreationOperation (Constructor: Boom..ctor([System.Int32? x = 0])) (OperationKind.ObjectCreation, Type: Boom, IsInvalid, IsImplicit) (Syntax: 'Boom') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: x) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: 'Boom') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'Boom') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'Boom') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void BadAttributeParameterType2() + { + string source = @" +[/**/Boom(null)/**/] +class Boom : System.Attribute +{ + public Boom(int? x = 0) { } + + static void Main() + { + typeof(Boom).GetCustomAttributes(true); + } +}"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (2,2): error CS0181: Attribute constructor parameter 'x' has type 'int?', which is not a valid attribute parameter type + // [/**/Boom/**/] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Boom").WithArguments("x", "int?").WithLocation(2, 12) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'Boom(null)') + IObjectCreationOperation (Constructor: Boom..ctor([System.Int32? x = 0])) (OperationKind.ObjectCreation, Type: Boom, IsInvalid, IsImplicit) (Syntax: 'Boom(null)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeWithExplicitNullArgument() + { + string source = @" +using System; + +[/**/My(null)/**/] +class MyAttribute : Attribute +{ + public MyAttribute(Type opt = null) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(null)') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Type opt = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(null)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: opt) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Type, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeWithDefaultNullArgument() + { + string source = @" +using System; + +[/**/My/**/] +class MyAttribute : Attribute +{ + public MyAttribute(Type opt = null) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Type opt = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: opt) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Type, Constant: null, IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeWithTypeOfArgument() + { + string source = @" +using System; + +[/**/My(typeof(MyAttribute))/**/] +class MyAttribute : Attribute +{ + public MyAttribute(Type opt = null) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(typeof(MyAttribute))') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Type opt = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(typeof(MyAttribute))') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: opt) (OperationKind.Argument, Type: null) (Syntax: 'typeof(MyAttribute)') + ITypeOfOperation (OperationKind.TypeOf, Type: System.Type) (Syntax: 'typeof(MyAttribute)') + TypeOperand: MyAttribute + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void InvalidValue() + { + string source = @" +using System.Security.Permissions; + +[/**/A/**/] +class A : CodeAccessSecurityAttribute +{ + public A(SecurityAction a = 0) : base(a) + { + } + +} +"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (4,12): error CS7049: Security attribute 'A' has an invalid SecurityAction value '0' + // [/**/A/**/] + Diagnostic(ErrorCode.ERR_SecurityAttributeInvalidAction, "A").WithArguments("A", "0").WithLocation(4, 12), + // (5,7): error CS0534: 'A' does not implement inherited abstract member 'SecurityAttribute.CreatePermission()' + // class A : CodeAccessSecurityAttribute + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "A").WithArguments("A", "System.Security.Permissions.SecurityAttribute.CreatePermission()").WithLocation(5, 7) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'A') + IObjectCreationOperation (Constructor: A..ctor([System.Security.Permissions.SecurityAction a = (System.Security.Permissions.SecurityAction)0])) (OperationKind.ObjectCreation, Type: A, IsInvalid, IsImplicit) (Syntax: 'A') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: a) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: 'A') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Security.Permissions.SecurityAction, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'A') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'A') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void InvalidAttributeParameterType() + { + string source = @" +using System; + +[/**/My/**/] +class MyAttribute : Attribute +{ + public MyAttribute(params int[][,] x) { } +} +"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (4,12): error CS0181: Attribute constructor parameter 'x' has type 'int[][*,*]', which is not a valid attribute parameter type + // [/**/My/**/] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "My").WithArguments("x", "int[][*,*]").WithLocation(4, 12) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor(params System.Int32[][,] x)) (OperationKind.ObjectCreation, Type: MyAttribute, IsInvalid, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: x) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: 'My') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Int32[][,], IsInvalid, IsImplicit) (Syntax: 'My') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'My') + Initializer: + IArrayInitializerOperation (0 elements) (OperationKind.ArrayInitializer, Type: null, IsInvalid, IsImplicit) (Syntax: 'My') + Element Values(0) + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Theory] + [InlineData("assembly")] + [InlineData("module")] + public void AssemblyAndModuleAttributeTargets(string attributeTarget) + { + string source = $""" + using System; + + [{attributeTarget}: /**/CLSCompliant(true)/**/] + """; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'CLSCompliant(true)') + IObjectCreationOperation (Constructor: System.CLSCompliantAttribute..ctor(System.Boolean isCompliant)) (OperationKind.ObjectCreation, Type: System.CLSCompliantAttribute, IsImplicit) (Syntax: 'CLSCompliant(true)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: isCompliant) (OperationKind.Argument, Type: null) (Syntax: 'true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = attributeTarget switch + { + "assembly" => DiagnosticDescription.None, + "module" => new DiagnosticDescription[] + { + // (3,20): warning CS3012: You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking + // [module: /**/CLSCompliant(true)/**/] + Diagnostic(ErrorCode.WRN_CLS_NotOnModules, "CLSCompliant(true)").WithLocation(3, 20), + }, + _ => throw TestExceptionUtilities.UnexpectedValue(attributeTarget), + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ReturnAttributeTarget() + { + string source = """ + using System; + + class MyAttribute : Attribute + { + public MyAttribute(int i) + { + } + } + + public class C + { + [return: /**/My(10)/**/] + public string M() => null; + } + """; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ComplexAttribute() + { + string source = @" +using System; + +[/**/My(i: 1, b: true, o: 2)/**/] +class MyAttribute : Attribute +{ + public MyAttribute(bool b, int i, params object[] o) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(i: 1, b: true, o: 2)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Boolean b, System.Int32 i, params System.Object[] o)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Arguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: 'i: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Initializer: + IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Element Values(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '2') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ComplexAttributeWithNamedArgument() + { + string source = @" +using System; + +[My(i: 1, b: true, o: 2)] +[/**/My(i: 1, b: true, o: 2, B = 10, D = 5)/**/] +[My(i: 1, b: true, o: 2)] +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute +{ + public MyAttribute(bool b, int i, params object[] o) + { + } + + public int B { get; set; } + public int C { get; set; } + public int D { get; set; } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Boolean b, System.Int32 i, params System.Object[] o)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Arguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: 'i: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Initializer: + IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Element Values(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '2') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Initializers(2): + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'B = 10') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.B { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'B') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'D = 5') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.D { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'D') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') +"; + string expectedFlowGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (4) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Value: + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Boolean b, System.Int32 i, params System.Object[] o)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Arguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: 'i: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Initializer: + IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Element Values(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '2') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'B = 10') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.B { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'B') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'D = 5') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.D { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'D') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') + IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Next (Regular) Block[B2] + Leaving: {R1} +} +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact] + public void AttributeOnLocalFunction() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + local(); + + [/**/My(10)/**/] + void local() { } + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnBackingField() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } + + [field: /**/My(10)/**/] + public string S { get; } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void PropertyAttributeTargetOnRecordPositionalParameter() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +record R([property: /**/My(10)/**/] string S); +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics, targetFramework: TargetFramework.Net50); + } + + [Fact] + public void AttributeOnInvalidLocation() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +class C +{ + void M() + { + [/**/My(10)/**/] + int x = 5; + _ = x; + } +} +"; + var expectedDiagnostics = new[] + { + // (15,9): error CS7014: Attributes are not valid in this context. + // [/**/My(10)/**/] + Diagnostic(ErrorCode.ERR_AttributesNotAllowed, "[/**/My(10)/**/]").WithLocation(15, 9), + }; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsInvalid, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10, IsInvalid) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnEnumMember() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +enum E +{ + [/**/My(10)/**/] + A, +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnTypeParameter() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +class C <[/**/My(10)/**/] T> +{ +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnTypeParameterWithCallerMemberName_Method() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string s = ""default"") + { + } +} + +class C +{ + void M<[/**/My/**/] T>() { } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String s = ""default""])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnTypeParameterWithCallerMemberName_Class() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string s = ""default"") + { + } +} + +class C<[/**/My/**/] T> +{ +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String s = ""default""])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""default"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs index e2ba4e27fec3..08a631493ca7 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs @@ -34,11 +34,17 @@ void M() } "; string expectedOperationTree = @" -IOperation: (OperationKind.None, Type: System.Diagnostics.ConditionalAttribute) (Syntax: 'Conditional(field)') - Children(1): - IFieldReferenceOperation: System.String C.field (Static) (OperationKind.FieldReference, Type: System.String, Constant: ""field"") (Syntax: 'field') - Instance Receiver: - null +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Conditional(field)') + IObjectCreationOperation (Constructor: System.Diagnostics.ConditionalAttribute..ctor(System.String conditionString)) (OperationKind.ObjectCreation, Type: System.Diagnostics.ConditionalAttribute, IsImplicit) (Syntax: 'Conditional(field)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: conditionString) (OperationKind.Argument, Type: null) (Syntax: 'field') + IFieldReferenceOperation: System.String C.field (Static) (OperationKind.FieldReference, Type: System.String, Constant: ""field"") (Syntax: 'field') + Instance Receiver: + null + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index 3a0819e07748..6cee477a7f84 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -6520,7 +6520,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); break; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index a4a057e66f28..b1b9134e83cd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -27003,7 +27003,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); break; @@ -27014,7 +27014,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind); @@ -27028,7 +27028,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind); @@ -27137,7 +27137,7 @@ private void Handle(OperationBlockStartAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); RegisterOperationAction(context); @@ -27150,7 +27150,7 @@ private void Handle(OperationBlockStartAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind); @@ -27166,7 +27166,7 @@ private void Handle(OperationBlockStartAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind); @@ -27211,7 +27211,7 @@ private void Handle6(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); break; @@ -27222,7 +27222,7 @@ private void Handle6(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind); @@ -27236,7 +27236,7 @@ private void Handle6(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind); diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index bd94b713a244..5a68010063f9 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -277,5 +277,7 @@ public enum OperationKind ImplicitIndexerReference = 0x7b, /// Indicates an . Utf8String = 0x7c, + /// Indicates an . + Attribute = 0x7d, } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 22b7f3c81585..239d40ed5dd9 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3597,6 +3597,29 @@ public interface IUtf8StringOperation : IOperation /// string Value { get; } } + /// + /// Represents the application of an attribute. + /// + /// Current usage: + /// (1) C# attribute application. + /// (2) VB attribute application. + /// + /// + /// + /// This node is associated with the following operation kinds: + /// + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface IAttributeOperation : IOperation + { + /// + /// The operation representing the attribute. This can be a in non-error cases, or an in error cases. + /// + IOperation Operation { get; } + } #endregion #region Implementations @@ -10156,6 +10179,57 @@ internal Utf8StringOperation(string value, SemanticModel? semanticModel, SyntaxN public override void Accept(OperationVisitor visitor) => visitor.VisitUtf8String(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitUtf8String(this, argument); } + internal sealed partial class AttributeOperation : Operation, IAttributeOperation + { + internal AttributeOperation(IOperation operation, SemanticModel? semanticModel, SyntaxNode syntax, bool isImplicit) + : base(semanticModel, syntax, isImplicit) + { + Operation = SetParentOperation(operation, this); + } + public IOperation Operation { get; } + internal override int ChildOperationsCount => + (Operation is null ? 0 : 1); + internal override IOperation GetCurrent(int slot, int index) + => slot switch + { + 0 when Operation != null + => Operation, + _ => throw ExceptionUtilities.UnexpectedValue((slot, index)), + }; + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex) + { + switch (previousSlot) + { + case -1: + if (Operation != null) return (true, 0, 0); + else goto case 0; + case 0: + case 1: + return (false, 1, 0); + default: + throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); + } + } + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) + { + switch (previousSlot) + { + case int.MaxValue: + if (Operation != null) return (true, 0, 0); + else goto case 0; + case 0: + case -1: + return (false, -1, 0); + default: + throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); + } + } + public override ITypeSymbol? Type => null; + internal override ConstantValue? OperationConstantValue => null; + public override OperationKind Kind => OperationKind.Attribute; + public override void Accept(OperationVisitor visitor) => visitor.VisitAttribute(this); + public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitAttribute(this, argument); + } #endregion #region Cloner internal sealed partial class OperationCloner : OperationVisitor @@ -10759,6 +10833,11 @@ public override IOperation VisitUtf8String(IUtf8StringOperation operation, objec var internalOperation = (Utf8StringOperation)operation; return new Utf8StringOperation(internalOperation.Value, internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } + public override IOperation VisitAttribute(IAttributeOperation operation, object? argument) + { + var internalOperation = (AttributeOperation)operation; + return new AttributeOperation(Visit(internalOperation.Operation), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit); + } } #endregion @@ -10897,6 +10976,7 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ } public virtual void VisitSlicePattern(ISlicePatternOperation operation) => DefaultVisit(operation); public virtual void VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation) => DefaultVisit(operation); public virtual void VisitUtf8String(IUtf8StringOperation operation) => DefaultVisit(operation); + public virtual void VisitAttribute(IAttributeOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor { @@ -11032,6 +11112,7 @@ public abstract partial class OperationVisitor public virtual TResult? VisitSlicePattern(ISlicePatternOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitUtf8String(IUtf8StringOperation operation, TArgument argument) => DefaultVisit(operation, argument); + public virtual TResult? VisitAttribute(IAttributeOperation operation, TArgument argument) => DefaultVisit(operation, argument); } #endregion } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs index 87468b40d318..6b45c799305e 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs @@ -137,6 +137,15 @@ public static ControlFlowGraph Create(Operations.IParameterInitializerOperation return CreateCore(initializer, nameof(initializer), cancellationToken); } + /// + /// Creates a for the given executable code block . + /// + /// Root attribute operation, which must have a null parent. + /// Optional cancellation token. + public static ControlFlowGraph Create(Operations.IAttributeOperation attribute, CancellationToken cancellationToken = default) + { + return CreateCore(attribute, nameof(attribute), cancellationToken); + } /// /// Creates a for the given executable code block . diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 2eeb71305008..2d45acced071 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -96,7 +96,8 @@ public static ControlFlowGraph Create(IOperation body, ControlFlowGraph? parent body.Kind == OperationKind.ConstructorBody || body.Kind == OperationKind.FieldInitializer || body.Kind == OperationKind.PropertyInitializer || - body.Kind == OperationKind.ParameterInitializer, + body.Kind == OperationKind.ParameterInitializer || + body.Kind == OperationKind.Attribute, $"Unexpected root operation kind: {body.Kind}"); Debug.Assert(parent == null); } @@ -7736,5 +7737,10 @@ static bool setsAllProperties(ImmutableArray initializers, IEnumerab return set.Count == properties.Count(); } } + + public override IOperation VisitAttribute(IAttributeOperation operation, int? captureIdForResult) + { + return new AttributeOperation(Visit(operation.Operation, captureIdForResult)!, semanticModel: null, operation.Syntax, IsImplicit(operation)); + } } } diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 80e42d3e1c82..3dea22c6f495 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3349,4 +3349,21 @@ + + + + Represents the application of an attribute. + + Current usage: + (1) C# attribute application. + (2) VB attribute application. + + + + + + The operation representing the attribute. This can be a in non-error cases, or an in error cases. + + + diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index f6c8a9c300c3..ca197fe8090a 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -4,3 +4,9 @@ Microsoft.CodeAnalysis.GeneratorDriver.ReplaceGenerators(System.Collections.Immu override sealed Microsoft.CodeAnalysis.CompilationOptions.GetHashCode() -> int static Microsoft.CodeAnalysis.ModuleMetadata.CreateFromMetadata(System.IntPtr metadata, int size, System.Action! onDispose) -> Microsoft.CodeAnalysis.ModuleMetadata! Microsoft.CodeAnalysis.INamedTypeSymbol.IsFileLocal.get -> bool +Microsoft.CodeAnalysis.OperationKind.Attribute = 125 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.Operations.IAttributeOperation +Microsoft.CodeAnalysis.Operations.IAttributeOperation.Operation.get -> Microsoft.CodeAnalysis.IOperation! +static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IAttributeOperation! attribute, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation) -> void +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation, TArgument argument) -> TResult? \ No newline at end of file diff --git a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs index 3fca960d4dce..deb6fdcc77c0 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -65,6 +65,10 @@ public static (ControlFlowGraph graph, ISymbol associatedSymbol) GetControlFlowG graph = ControlFlowGraph.Create(parameterInitializerOperation); break; + case IAttributeOperation attributeOperation: + graph = ControlFlowGraph.Create(attributeOperation); + break; + default: return default; } @@ -1997,6 +2001,7 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && case OperationKind.SlicePattern: case OperationKind.ListPattern: case OperationKind.ImplicitIndexerReference: + case OperationKind.Attribute: return true; } diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 1a89495b86f0..3b4e85bffb77 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -2149,6 +2149,13 @@ public override void VisitWith(IWithOperation operation) Visit(operation.Initializer, "Initializer"); } + public override void VisitAttribute(IAttributeOperation operation) + { + LogString(nameof(IAttributeOperation)); + LogCommonPropertiesAndNewLine(operation); + Visit(operation.Operation); + } + #endregion } } diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index f96fb93e7910..c96219cf6807 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -1756,5 +1756,11 @@ public override void VisitWith(IWithOperation operation) IEnumerable children = SpecializedCollections.SingletonEnumerable(operation.Operand).Concat(operation.Initializer); AssertEx.Equal(children, operation.ChildOperations); } + + public override void VisitAttribute(IAttributeOperation operation) + { + Assert.Equal(OperationKind.Attribute, operation.Kind); + Assert.False(operation.ConstantValue.HasValue); + } } } diff --git a/src/Compilers/VisualBasic/Portable/Binding/AttributeSemanticModel.vb b/src/Compilers/VisualBasic/Portable/Binding/AttributeSemanticModel.vb index fbf88f8e3c35..3b186a20d8f9 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/AttributeSemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/AttributeSemanticModel.vb @@ -26,7 +26,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' Friend Shared Function Create(containingSemanticModel As SyntaxTreeSemanticModel, binder As AttributeBinder, Optional ignoreAccessibility As Boolean = False) As AttributeSemanticModel Debug.Assert(containingSemanticModel IsNot Nothing) - Return New AttributeSemanticModel(binder.Root, binder, containingSemanticModel, ignoreAccessibility:=ignoreAccessibility) + Dim owner As Symbol = GetAttributeTarget(containingSemanticModel, binder) + Dim wrappedBinder As Binder = binder + If owner IsNot Nothing Then + wrappedBinder = New LocationSpecificBinder(BindingLocation.Attribute, owner, binder) + End If + + Return New AttributeSemanticModel(binder.Root, wrappedBinder, containingSemanticModel, ignoreAccessibility:=ignoreAccessibility) + End Function + + Private Shared Function GetAttributeTarget(model As SyntaxTreeSemanticModel, binder As AttributeBinder) As Symbol + Debug.Assert(TypeOf binder.Root Is AttributeSyntax) + If TypeOf binder.Root.Parent Is AttributeListSyntax Then + Return DirectCast(model.GetDeclaredSymbolForNode(binder.Root.Parent.Parent), Symbol) + End If + + Return Nothing End Function ''' diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Attributes.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Attributes.vb index 199f9eb9d059..4b4f8e8adddc 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Attributes.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Attributes.vb @@ -255,7 +255,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim analyzedArguments = BindAttributeArguments(attributeTypeForBinding, argumentListOpt, diagnostics) Dim boundArguments As ImmutableArray(Of BoundExpression) = analyzedArguments.positionalArguments Dim boundNamedArguments As ImmutableArray(Of BoundExpression) = analyzedArguments.namedArguments - + Dim defaultArguments As BitVector = Nothing If Not attributeTypeForBinding.IsErrorType() Then ' Filter out inaccessible constructors @@ -351,10 +351,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' There should not be any used temporaries or copy back expressions because arguments must ' be constants and they cannot be passed byref. Dim argumentInfo As (Arguments As ImmutableArray(Of BoundExpression), DefaultArguments As BitVector) = PassArguments(node.Name, methodResult, boundArguments, diagnostics) - ' We don't do anything with the default parameter info currently, as we don't expose IOperations for - ' Attributes. If that changes, we can add this info to the BoundAttribute node. boundArguments = argumentInfo.Arguments - + defaultArguments = argumentInfo.DefaultArguments Debug.Assert(Not boundArguments.Any(Function(a) a.Kind = BoundKind.ByRefArgumentWithCopyBack)) If methodSym.DeclaredAccessibility <> Accessibility.Public Then @@ -369,7 +367,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If - Return New BoundAttribute(node, methodSym, boundArguments, boundNamedArguments, resultKind, type, hasErrors:=resultKind <> LookupResultKind.Good) + Return New BoundAttribute(node, methodSym, boundArguments, defaultArguments, boundNamedArguments, resultKind, type, hasErrors:=resultKind <> LookupResultKind.Good) End Function diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml b/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml index e6f82dc7b2ae..2f65bba6d114 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml @@ -662,6 +662,7 @@ + diff --git a/src/Compilers/VisualBasic/Portable/Generated/BoundNodes.xml.Generated.vb b/src/Compilers/VisualBasic/Portable/Generated/BoundNodes.xml.Generated.vb index 9dd3584870b2..cbe03dc624a2 100644 --- a/src/Compilers/VisualBasic/Portable/Generated/BoundNodes.xml.Generated.vb +++ b/src/Compilers/VisualBasic/Portable/Generated/BoundNodes.xml.Generated.vb @@ -3111,7 +3111,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend NotInheritable Class BoundAttribute Inherits BoundExpression - Public Sub New(syntax As SyntaxNode, constructor As MethodSymbol, constructorArguments As ImmutableArray(Of BoundExpression), namedArguments As ImmutableArray(Of BoundExpression), resultKind As LookupResultKind, type As TypeSymbol, Optional hasErrors As Boolean = False) + Public Sub New(syntax As SyntaxNode, constructor As MethodSymbol, constructorArguments As ImmutableArray(Of BoundExpression), constructorDefaultArguments As BitVector, namedArguments As ImmutableArray(Of BoundExpression), resultKind As LookupResultKind, type As TypeSymbol, Optional hasErrors As Boolean = False) MyBase.New(BoundKind.Attribute, syntax, type, hasErrors OrElse constructorArguments.NonNullAndHasErrors() OrElse namedArguments.NonNullAndHasErrors()) Debug.Assert(Not (constructorArguments.IsDefault), "Field 'constructorArguments' cannot be null (use Null=""allow"" in BoundNodes.xml to remove this check)") @@ -3120,6 +3120,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Me._Constructor = constructor Me._ConstructorArguments = constructorArguments + Me._ConstructorDefaultArguments = constructorDefaultArguments Me._NamedArguments = namedArguments Me._ResultKind = resultKind End Sub @@ -3139,6 +3140,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + Private ReadOnly _ConstructorDefaultArguments As BitVector + Public ReadOnly Property ConstructorDefaultArguments As BitVector + Get + Return _ConstructorDefaultArguments + End Get + End Property + Private ReadOnly _NamedArguments As ImmutableArray(Of BoundExpression) Public ReadOnly Property NamedArguments As ImmutableArray(Of BoundExpression) Get @@ -3158,9 +3166,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return visitor.VisitAttribute(Me) End Function - Public Function Update(constructor As MethodSymbol, constructorArguments As ImmutableArray(Of BoundExpression), namedArguments As ImmutableArray(Of BoundExpression), resultKind As LookupResultKind, type As TypeSymbol) As BoundAttribute - If constructor IsNot Me.Constructor OrElse constructorArguments <> Me.ConstructorArguments OrElse namedArguments <> Me.NamedArguments OrElse resultKind <> Me.ResultKind OrElse type IsNot Me.Type Then - Dim result = New BoundAttribute(Me.Syntax, constructor, constructorArguments, namedArguments, resultKind, type, Me.HasErrors) + Public Function Update(constructor As MethodSymbol, constructorArguments As ImmutableArray(Of BoundExpression), constructorDefaultArguments As BitVector, namedArguments As ImmutableArray(Of BoundExpression), resultKind As LookupResultKind, type As TypeSymbol) As BoundAttribute + If constructor IsNot Me.Constructor OrElse constructorArguments <> Me.ConstructorArguments OrElse constructorDefaultArguments <> Me.ConstructorDefaultArguments OrElse namedArguments <> Me.NamedArguments OrElse resultKind <> Me.ResultKind OrElse type IsNot Me.Type Then + Dim result = New BoundAttribute(Me.Syntax, constructor, constructorArguments, constructorDefaultArguments, namedArguments, resultKind, type, Me.HasErrors) result.CopyAttributes(Me) Return result End If @@ -12355,7 +12363,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim constructorArguments As ImmutableArray(Of BoundExpression) = Me.VisitList(node.ConstructorArguments) Dim namedArguments As ImmutableArray(Of BoundExpression) = Me.VisitList(node.NamedArguments) Dim type as TypeSymbol = Me.VisitType(node.Type) - Return node.Update(node.Constructor, constructorArguments, namedArguments, node.ResultKind, type) + Return node.Update(node.Constructor, constructorArguments, node.ConstructorDefaultArguments, namedArguments, node.ResultKind, type) End Function Public Overrides Function VisitLateMemberAccess(node As BoundLateMemberAccess) As BoundNode @@ -13570,6 +13578,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return New TreeDumperNode("attribute", Nothing, New TreeDumperNode() { New TreeDumperNode("constructor", node.Constructor, Nothing), New TreeDumperNode("constructorArguments", Nothing, From x In node.ConstructorArguments Select Visit(x, Nothing)), + New TreeDumperNode("constructorDefaultArguments", node.ConstructorDefaultArguments, Nothing), New TreeDumperNode("namedArguments", Nothing, From x In node.NamedArguments Select Visit(x, Nothing)), New TreeDumperNode("resultKind", node.ResultKind, Nothing), New TreeDumperNode("type", node.Type, Nothing) diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index c2b504cd97b5..dfbba8e5a398 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -279,10 +279,11 @@ Namespace Microsoft.CodeAnalysis.Operations Return CreateBoundReDimClauseOperation(DirectCast(boundNode, BoundRedimClause)) Case BoundKind.TypeArguments Return CreateBoundTypeArgumentsOperation(DirectCast(boundNode, BoundTypeArguments)) + Case BoundKind.Attribute + Return CreateBoundAttributeOperation(DirectCast(boundNode, BoundAttribute)) Case BoundKind.AddressOfOperator, BoundKind.ArrayLiteral, - BoundKind.Attribute, BoundKind.ByRefArgumentWithCopyBack, BoundKind.CompoundAssignmentTargetPlaceholder, BoundKind.EraseStatement, @@ -672,6 +673,23 @@ Namespace Microsoft.CodeAnalysis.Operations Return New InvalidOperation(children, _semanticModel, syntax, type, constantValue, isImplicit) End Function + Private Function CreateBoundAttributeOperation(boundAttribute As BoundAttribute) As IAttributeOperation + Dim isAttributeImplicit = boundAttribute.WasCompilerGenerated + If boundAttribute.Constructor Is Nothing OrElse boundAttribute.ConstructorArguments.Length <> boundAttribute.Constructor.ParameterCount Then + Dim invalidOperation = OperationFactory.CreateInvalidOperation(_semanticModel, boundAttribute.Syntax, GetIOperationChildren(boundAttribute), isImplicit:=True) + Return New AttributeOperation(invalidOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit) + End If + + Dim initializer As ObjectOrCollectionInitializerOperation = Nothing + If Not boundAttribute.NamedArguments.IsEmpty Then + Dim namedArguments = CreateFromArray(Of BoundExpression, IOperation)(boundAttribute.NamedArguments) + initializer = New ObjectOrCollectionInitializerOperation(namedArguments, _semanticModel, boundAttribute.Syntax, boundAttribute.Type, isImplicit:=True) + End If + + Dim objectCreationOperation = New ObjectCreationOperation(boundAttribute.Constructor, initializer, DeriveArguments(boundAttribute), _semanticModel, boundAttribute.Syntax, boundAttribute.Type, boundAttribute.ConstantValueOpt, isImplicit:=True) + Return New AttributeOperation(objectCreationOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit) + End Function + Private Function CreateBoundTryCastOperation(boundTryCast As BoundTryCast) As IOperation Return CreateBoundConversionOrCastOperation(boundTryCast, isTryCast:=True) End Function diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb index 9fc134ff6893..3c74090e9728 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb @@ -183,6 +183,9 @@ Namespace Microsoft.CodeAnalysis.Operations Case BoundKind.RaiseEventStatement Dim boundRaiseEvent = DirectCast(boundNode, BoundRaiseEventStatement) Return DeriveArguments(DirectCast(boundRaiseEvent.EventInvocation, BoundCall)) + Case BoundKind.Attribute + Dim boundAttribute = DirectCast(boundNode, BoundAttribute) + Return DeriveArguments(boundAttribute.ConstructorArguments, boundAttribute.Constructor.Parameters, boundAttribute.ConstructorDefaultArguments) Case Else Throw ExceptionUtilities.UnexpectedValue(boundNode.Kind) End Select diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb index f2e3df8ea507..ddb74b62dd63 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb @@ -2217,6 +2217,229 @@ End Class CompileAndVerify(compilation, sourceSymbolValidator:=attributeValidator, symbolValidator:=attributeValidator) End Sub + + Public Sub TestAttributeCallerInfoSemanticModel() + Dim source = " +Imports System +Imports System.Runtime.CompilerServices + +Class Attr + Inherits Attribute + + Public Sub New( Optional s As String = Nothing) + End Sub +End Class + +Class C + 'BIND:""Attr"" + Sub M0() + End Sub +End Class +" + Dim comp = CreateCompilation(source) + comp.VerifyDiagnostics() + Dim tree = comp.SyntaxTrees(0) + Dim root = tree.GetRoot() + Dim attrSyntax = root.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim semanticModel = comp.GetSemanticModel(tree) + Dim m0 = semanticModel.GetDeclaredSymbol(root.DescendantNodes().OfType(Of MethodStatementSyntax)().Last()) + Dim attrs = m0.GetAttributes() + Assert.Equal("M0", attrs.Single().ConstructorArguments.Single().Value) + Dim expectedTree As String = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Sub Attr..ctor([s As System.String = Nothing])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + VerifyOperationTreeForTest(Of AttributeSyntax)(comp, fileName:="", expectedTree) + + Dim operation = semanticModel.GetOperation(attrSyntax) + Dim operationTreeFromSemanticModel = OperationTreeVerifier.GetOperationTree(comp, operation) + OperationTreeVerifier.Verify(expectedTree, operationTreeFromSemanticModel) + End Sub + + + Public Sub TestAttributeCallerInfoSemanticModel_Method_Speculative() + Dim source = " +Imports System +Imports System.Runtime.CompilerServices + +Class Attr + Inherits Attribute + + Public Sub New( Optional s As String = Nothing) + End Sub +End Class + +Class C + + Sub M0() + End Sub +End Class +" + Dim comp = CreateCompilation(source) + comp.VerifyDiagnostics() + Dim tree = comp.SyntaxTrees(0) + Dim root = tree.GetRoot() + Dim attrSyntax = root.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim semanticModel = comp.GetSemanticModel(tree) + Dim newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseArgumentList("()"))) + Dim newAttrSyntax = newRoot.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim speculativeModel As SemanticModel = Nothing + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, speculativeModel)) + Dim speculativeOperation = speculativeModel.GetOperation(newAttrSyntax) + Dim expectedTree As String = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Sub Attr..ctor([s As System.String = Nothing])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + Dim speculativeOperationTree = OperationTreeVerifier.GetOperationTree(comp, speculativeOperation) + OperationTreeVerifier.Verify(expectedTree, speculativeOperationTree) + End Sub + + + Public Sub TestAttributeCallerInfoSemanticModel_Parameter_Speculative() + Dim source = " +Imports System +Imports System.Runtime.CompilerServices + +Class Attr + Inherits Attribute + + Public Sub New( Optional s As String = Nothing) + End Sub +End Class + +Class C + Sub M0( x As Integer) + End Sub +End Class +" + Dim comp = CreateCompilation(source) + comp.VerifyDiagnostics() + Dim tree = comp.SyntaxTrees(0) + Dim root = tree.GetRoot() + Dim attrSyntax = root.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim semanticModel = comp.GetSemanticModel(tree) + Dim newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseArgumentList("()"))) + Dim newAttrSyntax = newRoot.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim speculativeModel As SemanticModel = Nothing + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, speculativeModel)) + Dim speculativeOperation = speculativeModel.GetOperation(newAttrSyntax) + Dim expectedTree As String = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Sub Attr..ctor([s As System.String = Nothing])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + Dim speculativeOperationTree = OperationTreeVerifier.GetOperationTree(comp, speculativeOperation) + OperationTreeVerifier.Verify(expectedTree, speculativeOperationTree) + End Sub + + + Public Sub TestAttributeCallerInfoSemanticModel_Class_Speculative() + Dim source = " +Imports System +Imports System.Runtime.CompilerServices + +Class Attr + Inherits Attribute + + Public Sub New( Optional s As String = Nothing) + End Sub +End Class + + +Class C +End Class +" + Dim comp = CreateCompilation(source) + comp.VerifyDiagnostics() + Dim tree = comp.SyntaxTrees(0) + Dim root = tree.GetRoot() + Dim attrSyntax = root.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim semanticModel = comp.GetSemanticModel(tree) + Dim newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseArgumentList("()"))) + Dim newAttrSyntax = newRoot.DescendantNodes().OfType(Of AttributeSyntax)().Last() + Dim speculativeModel As SemanticModel = Nothing + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, speculativeModel)) + Dim speculativeOperation = speculativeModel.GetOperation(newAttrSyntax) + Dim expectedTree As String = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Sub Attr..ctor([s As System.String = Nothing])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsImplicit) (Syntax: 'Attr') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + Dim speculativeOperationTree = OperationTreeVerifier.GetOperationTree(comp, speculativeOperation) + OperationTreeVerifier.Verify(expectedTree, speculativeOperationTree) + End Sub + + + Public Sub TestAttributeCallerInfoSemanticModel_Speculative_AssemblyTarget() + Dim source = " +Imports System +Imports System.Runtime.CompilerServices + + + +Class Attr + Inherits Attribute + + Public Sub New( Optional s As String = ""default_value"") + End Sub +End Class +" + Dim comp = CreateCompilation(source) + comp.VerifyDiagnostics() + Dim tree = comp.SyntaxTrees(0) + Dim root = tree.GetRoot() + Dim attrSyntax = root.DescendantNodes().OfType(Of AttributeSyntax)().First() + Dim semanticModel = comp.GetSemanticModel(tree) + Dim newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseArgumentList("()"))) + Dim newAttrSyntax = newRoot.DescendantNodes().OfType(Of AttributeSyntax)().First() + Dim speculativeModel As SemanticModel = Nothing + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.Position, newAttrSyntax, speculativeModel)) + Dim speculativeOperation = speculativeModel.GetOperation(newAttrSyntax) + Dim expectedTree As String = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Assembly: Attr()') + IObjectCreationOperation (Constructor: Sub Attr..ctor([s As System.String = ""default_value""])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Assembly: Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""default_value"", IsImplicit) (Syntax: 'Attr') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + Dim speculativeOperationTree = OperationTreeVerifier.GetOperationTree(comp, speculativeOperation) + OperationTreeVerifier.Verify(expectedTree, speculativeOperationTree) + End Sub + + #End Region #Region "Error Tests" diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.vb new file mode 100644 index 000000000000..26f7faa4ea2c --- /dev/null +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.vb @@ -0,0 +1,748 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + + Public Sub TestCallerInfoImplicitCall() + Dim source = Optional lineNumber As Integer = -1) + Console.WriteLine(lineNumber) + End Sub +End Class + +'BIND:"A" +Class Test +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestCallerMemberName_Class() + Dim source = Optional callerName As String = "") + Console.WriteLine(callerName) + End Sub +End Class + +'BIND:"A" +Class Test +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestCallerMemberName_Method() + Dim source = Optional callerName As String = "") + Console.WriteLine(callerName) + End Sub +End Class + +Class Test + 'BIND:"A" + Public Sub M() + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestCallerMemberName_Parameter() + Dim source = Optional callerName As String = "") + End Sub +End Class + +Class Test + Public Sub M( x As String)'BIND:"My" + End Sub +End Class +]]>.Value + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestNonExistingAttribute() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithoutArguments() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithExplicitArgument() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithExplicitArgument_IncorrectTypePassed() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithExplicitArgumentOptionalParameter() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithOptionalParameterNotPassed(withParentheses As Boolean) + Dim attribute = If(withParentheses, "My()", "My") + Dim attributeListSyntax = "<" + attribute + ">" + "'BIND:""My""" + Dim source = .Value + attributeListSyntax + .Value + + Dim expectedOperationTree = +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: '<%= attribute %>') + IObjectCreationOperation (Constructor: Sub MyAttribute..ctor([value As System.String = ""])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: '<%= attribute %>') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +.Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestConversion() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub BadAttributeParameterType() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub BadAttributeParameterType2() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeWithExplicitNullArgument() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + Public Sub New(Optional x As Type = Nothing) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeWithDefaultNullArgument() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + Public Sub New(Optional x As Type = Nothing) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeWithTypeOfArgument() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + Public Sub New(Optional x As Type = Nothing) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub InvalidValue() + Dim source = 'BIND:"A" +Class A + Inherits CodeAccessSecurityAttribute + Public Sub New(Optional x As SecurityAction = 0) + MyBase.New(x) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"A" + ~ +BC30610: Class 'A' must either be declared 'MustInherit' or override the following inherited 'MustOverride' member(s): + SecurityAttribute: Public MustOverride Overloads Function CreatePermission() As IPermission. +Class A + ~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub InvalidAttributeParameterType() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + + Public Sub New(ParamArray x As Integer()(,)) + End Sub +End Class + +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + + + Public Sub AssemblyAndModuleAttributeTargets(attributeTarget As String) + Dim source = $" +Imports System + +<{attributeTarget}: CLSCompliant(True)>'BIND:""{attributeTarget}: CLSCompliant(True)"" +" + Dim syntax As String + If attributeTarget = "Assembly" Then + syntax = "Assembly: C ... liant(True)" + ElseIf attributeTarget = "Module" Then + syntax = "Module: CLS ... liant(True)" + Else + Throw TestExceptionUtilities.UnexpectedValue(attributeTarget) + End If + + Dim expectedOperationTree = $" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: '{syntax}') + IObjectCreationOperation (Constructor: Sub System.CLSCompliantAttribute..ctor(isCompliant As System.Boolean)) (OperationKind.ObjectCreation, Type: System.CLSCompliantAttribute, IsImplicit) (Syntax: '{syntax}') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: isCompliant) (OperationKind.Argument, Type: null) (Syntax: 'True') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'True') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub ReturnAttributeTarget() + Dim source = String 'BIND:"My(10)" + Return Nothing + End Function +End Class +]]>.Value + + Dim expectedOperationTree = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: Sub MyAttribute..ctor(x As System.Int32)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeOnEnumMember() + Dim source = 'BIND:"My(10)" + A +End Enum +]]>.Value + + Dim expectedOperationTree = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: Sub MyAttribute..ctor(x As System.Int32)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb index 24f969a68008..edc7d1cf8d82 100644 --- a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb @@ -27,11 +27,17 @@ Class C End Class]]>.Value Dim expectedOperationTree = .Value Dim expectedDiagnostics = String.Empty diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/GetSemanticInfoTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/GetSemanticInfoTests.vb index c4a012152162..3b8cfcca6d00 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/GetSemanticInfoTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/GetSemanticInfoTests.vb @@ -5985,7 +5985,7 @@ End Module Dim symbolInfo = model.GetSymbolInfo(node) Assert.Null(symbolInfo.Symbol) - Assert.Equal(CandidateReason.NotReferencable, symbolInfo.CandidateReason) + Assert.Equal(CandidateReason.StaticInstanceMismatch, symbolInfo.CandidateReason) End Sub