Skip to content
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,7 +4,6 @@

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.Operations;
Expand Down Expand Up @@ -54,18 +53,22 @@ public bool IsSuppressMessageAttributeWithNamedArguments(
CancellationToken cancellationToken,
out ImmutableArray<(string name, IOperation value)> namedAttributeArguments)
{
var operation = (model.GetOperation(attributeSyntax, cancellationToken) as IAttributeOperation)?.Operation;
if (operation is not IObjectCreationOperation { Initializer: { } initializerOperation })
var attribute = model.GetOperation(attributeSyntax, cancellationToken);
if (attribute == null)
{
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 initializer in initializerOperation.Initializers)
foreach (var childOperation in attribute.ChildOperations)
{
var simpleAssignment = (ISimpleAssignmentOperation)initializer;
if (simpleAssignment.Target is IPropertyReferenceOperation propertyReference &&
if (childOperation is ISimpleAssignmentOperation simpleAssignment &&
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,14 +183,27 @@ 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.
symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, OperationKind.None,
// 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,
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.Attribute:
case OperationKind.None:
case OperationKind.ParameterInitializer:
// Skip blocks from attributes and parameter initializers.
// Skip blocks from attributes (which have OperationKind.None) 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>
internal static Symbol GetAttributedMember(Symbol symbol)
private 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,12 +16,10 @@ 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 @@ -32,7 +30,6 @@ private AttributeSemanticModel(
{
Debug.Assert(syntax != null);
_aliasOpt = aliasOpt;
_attributeTarget = attributeTarget;
}

/// <summary>
Expand All @@ -41,7 +38,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, attributeTarget, aliasOpt, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt);
return new AttributeSemanticModel(syntax, attributeType, aliasOpt, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt);
}

/// <summary>
Expand All @@ -52,22 +49,7 @@ public static AttributeSemanticModel CreateSpeculative(SyntaxTreeSemanticModel p
Debug.Assert(parentSemanticModel != null);
Debug.Assert(rootBinder != null);
Debug.Assert(rootBinder.IsSemanticModelBinder);

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;
return new AttributeSemanticModel(syntax, attributeType, aliasOpt, rootBinder, parentSemanticModelOpt: parentSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition: position);
}

private NamedTypeSymbol AttributeType
Expand Down Expand Up @@ -107,7 +89,8 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDi
if (node.Kind() == SyntaxKind.Attribute)
{
var attribute = (AttributeSyntax)node;
return binder.BindAttribute(attribute, AttributeType, attributedMember: ContextualAttributeBinder.GetAttributedMember(_attributeTarget), diagnostics);
// 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);
}
else if (SyntaxFacts.IsAttributeName(node))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// 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 @@ -279,9 +280,8 @@ public CSharpOperationFactory(SemanticModel semanticModel)
return CreateBoundInterpolatedStringArgumentPlaceholder((BoundInterpolatedStringArgumentPlaceholder)boundNode);
case BoundKind.InterpolatedStringHandlerPlaceholder:
return CreateBoundInterpolatedStringHandlerPlaceholder((BoundInterpolatedStringHandlerPlaceholder)boundNode);
case BoundKind.Attribute:
return CreateBoundAttributeOperation((BoundAttribute)boundNode);

case BoundKind.Attribute:
case BoundKind.ArgList:
case BoundKind.ArgListOperator:
case BoundKind.ConvertedStackAllocExpression:
Expand Down Expand Up @@ -493,27 +493,6 @@ 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 or AttributeArgumentSyntax } ? (expression.Syntax.Parent, expression.WasCompilerGenerated) : (value.Syntax, true);
(SyntaxNode syntax, bool isImplicit) = expression.Syntax is { Parent: ArgumentSyntax parent } ? (parent, expression.WasCompilerGenerated) : (value.Syntax, true);
return new ArgumentOperation(
kind,
parameter,
Expand Down Expand Up @@ -238,15 +238,6 @@ 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