Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Revert "Implement IAttributeOperation (#59369)"" #64488

Merged
merged 1 commit into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.Operations;
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal Symbol AttributedMember
/// <summary>
/// Walk up to the nearest method/property/event.
/// </summary>
private static Symbol GetAttributedMember(Symbol symbol)
internal static Symbol GetAttributedMember(Symbol symbol)
{
for (; (object)symbol != null; symbol = symbol.ContainingSymbol)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -30,6 +32,7 @@ private AttributeSemanticModel(
{
Debug.Assert(syntax != null);
_aliasOpt = aliasOpt;
_attributeTarget = attributeTarget;
}

/// <summary>
Expand All @@ -38,7 +41,7 @@ private AttributeSemanticModel(
public static AttributeSemanticModel Create(SyntaxTreeSemanticModel containingSemanticModel, AttributeSyntax syntax, NamedTypeSymbol attributeType, AliasSymbol aliasOpt, Symbol? attributeTarget, Binder rootBinder, ImmutableDictionary<Symbol, Symbol> 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);
}

/// <summary>
Expand All @@ -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<AttributeListSyntax>()?.Parent;

if (attributedNode is not null)
{
return model.GetDeclaredSymbolForNode(attributedNode).GetSymbol();
}

return null;
}

private NamedTypeSymbol AttributeType
Expand Down Expand Up @@ -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))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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<BoundAssignmentOperator, IOperation>(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<IOperation> CreateIgnoredDimensions(BoundNode declaration, SyntaxNode declarationSyntax)
{
switch (declaration.Kind)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -238,6 +238,15 @@ internal ImmutableArray<IArgumentOperation> 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;
Expand Down
Loading