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