private void GetSymbolAndIndicesForMemberReference(IMemberReferenceOperation memberReference, ref ISymbol symbolOpt, ref ImmutableArray <AbstractIndex> indices) { switch (memberReference) { case IFieldReferenceOperation fieldReference: symbolOpt = fieldReference.Member; break; case IEventReferenceOperation eventReference: symbolOpt = eventReference.Member; break; case IPropertyReferenceOperation propertyReference: // We are only tracking: // 1) Indexers // 2) Read-only properties. // 3) Properties with a backing field (auto-generated properties) if (propertyReference.Arguments.Length > 0 || propertyReference.Property.IsReadOnly || propertyReference.Property.IsPropertyWithBackingField()) { symbolOpt = propertyReference.Property; indices = propertyReference.Arguments.Length > 0 ? CreateAbstractIndices(propertyReference.Arguments.Select(a => a.Value).ToImmutableArray()) : ImmutableArray <AbstractIndex> .Empty; } break; } }
private void UpdateLocation( SemanticModel semanticModel, INamedTypeSymbol interfaceType, SyntaxEditor editor, ISyntaxFactsService syntaxFacts, Location location, CancellationToken cancellationToken) { var identifierName = location.FindNode(getInnermostNodeForTie: true, cancellationToken); if (identifierName == null || !syntaxFacts.IsIdentifierName(identifierName)) { return; } var node = syntaxFacts.IsNameOfMemberAccessExpression(identifierName) || syntaxFacts.IsMemberBindingExpression(identifierName.Parent) ? identifierName.Parent : identifierName; if (syntaxFacts.IsInvocationExpression(node.Parent)) { node = node.Parent; } var operation = semanticModel.GetOperation(node); var instance = operation switch { IMemberReferenceOperation memberReference => memberReference.Instance, IInvocationOperation invocation => invocation.Instance, _ => null, }; if (instance == null) { return; } if (instance.IsImplicit) { if (instance is IInstanceReferenceOperation instanceReference && instanceReference.ReferenceKind != InstanceReferenceKind.ContainingTypeInstance) { return; } // Accessing the member not off of <dot>. i.e just plain `Goo()`. Replace with // ((IGoo)this).Goo(); var generator = editor.Generator; editor.ReplaceNode( identifierName, generator.MemberAccessExpression( generator.AddParentheses(generator.CastExpression(interfaceType, generator.ThisExpression())), identifierName.WithoutTrivia()).WithTriviaFrom(identifierName)); } else { // Accessing the member like `x.Goo()`. Replace with `((IGoo)x).Goo()` editor.ReplaceNode( instance.Syntax, (current, g) => g.AddParentheses( g.CastExpression(interfaceType, current.WithoutTrivia())).WithTriviaFrom(current)); } }
private void AnalyzeAssignment(OperationAnalysisContext context) { var assignmentOperation = (ISimpleAssignmentOperation)context.Operation; // Check if there are more then one assignment in a statement if (assignmentOperation.Target is not IMemberReferenceOperation operationTarget) { return; } // This analyzer makes sense only for reference type objects if (operationTarget.Instance?.Type?.IsReferenceType != true) { return; } bool isViolationFound = operationTarget.Instance switch { ILocalReferenceOperation localInstance => AnalyzeAssignmentToMember(assignmentOperation, localInstance, (a, b) => a.Local.Equals(b.Local)), IMemberReferenceOperation memberInstance => AnalyzeAssignmentToMember(assignmentOperation, memberInstance, (a, b) => a.Member.Equals(b.Member) && a.Instance?.Syntax.ToString() == b.Instance?.Syntax.ToString()), IParameterReferenceOperation parameterInstance => AnalyzeAssignmentToMember(assignmentOperation, parameterInstance, (a, b) => a.Parameter.Equals(b.Parameter)), _ => false, }; if (isViolationFound) { var diagnostic = operationTarget.CreateDiagnostic(Rule, operationTarget.Instance.Syntax, operationTarget.Member.Name); context.ReportDiagnostic(diagnostic); } }
private void VisitMemberReference(IMemberReferenceOperation operation, IEnumerable <IOperation> additionalChildren) { Assert.NotNull(operation.Member); IEnumerable <IOperation> children; if (operation.Instance != null) { children = new[] { operation.Instance }.Concat(additionalChildren); // Make sure that all static member references or invocations of static methods do not have implicit IInstanceReferenceOperations // as their receivers if (operation.Member.IsStatic && operation.Instance is IInstanceReferenceOperation) { Assert.False(operation.Instance.IsImplicit, $"Implicit {nameof(IInstanceReferenceOperation)} on {operation.Syntax}"); } } else { children = additionalChildren; } AssertEx.Equal(children, operation.Children); }
private void OnCompilationStart(CompilationStartAnalysisContext compilationContext) { var restrictedInternalsVisibleToMap = GetRestrictedInternalsVisibleToMap(compilationContext.Compilation); if (restrictedInternalsVisibleToMap.IsEmpty) { return; } var namespaceToIsBannedMap = new ConcurrentDictionary<INamespaceSymbol, /*isBanned*/bool>(); // Verify all explicit type name specifications in declarations and executable code. compilationContext.RegisterSyntaxNodeAction( context => { var name = (TNameSyntax)context.Node; if (!IsInTypeOnlyContext(name) || name.Parent is TNameSyntax) { // Bail out if we are not in type only context or the parent is also a name // which will be analyzed separately. return; } var typeInfo = context.SemanticModel.GetTypeInfo(name, context.CancellationToken); VerifySymbol(typeInfo.Type as INamedTypeSymbol, name, context.ReportDiagnostic, restrictedInternalsVisibleToMap, namespaceToIsBannedMap); }, NameSyntaxKinds); // Verify all member usages in executable code. compilationContext.RegisterOperationAction( context => { var symbol = context.Operation switch { IObjectCreationOperation objectCreation => objectCreation.Constructor, IInvocationOperation invocation => invocation.TargetMethod, IMemberReferenceOperation memberReference => memberReference.Member, IConversionOperation conversion => conversion.OperatorMethod, IUnaryOperation unary => unary.OperatorMethod, IBinaryOperation binary => binary.OperatorMethod, IIncrementOrDecrementOperation incrementOrDecrement => incrementOrDecrement.OperatorMethod, _ => throw new NotImplementedException($"Unhandled OperationKind: {context.Operation.Kind}"), }; VerifySymbol(symbol, context.Operation.Syntax, context.ReportDiagnostic, restrictedInternalsVisibleToMap, namespaceToIsBannedMap); }, OperationKind.ObjectCreation, OperationKind.Invocation, OperationKind.EventReference, OperationKind.FieldReference, OperationKind.MethodReference, OperationKind.PropertyReference, OperationKind.Conversion, OperationKind.UnaryOperator, OperationKind.BinaryOperator, OperationKind.Increment, OperationKind.Decrement); }
public static string?GetName(this IOperation @this) { return(@this switch { IMemberReferenceOperation memberReference => memberReference.Member.Name, IInvocationOperation invocation => invocation.TargetMethod?.Name, _ => null });
private static bool IsStaticMemberOrIsLocalFunction(IOperation operation) { return(operation switch { IMemberReferenceOperation memberReferenceOperation => IsStaticMemberOrIsLocalFunctionHelper(memberReferenceOperation.Member), IInvocationOperation invocationOperation => IsStaticMemberOrIsLocalFunctionHelper(invocationOperation.TargetMethod), _ => throw ExceptionUtilities.UnexpectedValue(operation), });
private IdentifierInfo CreateForMemberReferenceExpression([NotNull] IMemberReferenceOperation operation, [NotNull] ITypeSymbol memberType) { var name = new IdentifierName(operation.Member.Name, operation.Member.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat)); return(new IdentifierInfo(name, memberType, operation.Member.Kind.ToString())); }
internal static ISymbol?GetUnderlyingSymbol(IOperation?operation) { return(operation switch { IParameterReferenceOperation paramRef => paramRef.Parameter, ILocalReferenceOperation localRef => localRef.Local, IMemberReferenceOperation memberRef => memberRef.Member, _ => null, });
private static bool IsHazardousIfNull(IOperation operation) { if (operation.Kind == OperationKind.ConditionalAccessInstance) { return(false); } return(operation.Parent switch { IMemberReferenceOperation memberReference => memberReference.Instance == operation, IArrayElementReferenceOperation arrayElementReference => arrayElementReference.ArrayReference == operation, IInvocationOperation invocation => invocation.Instance == operation, _ => false, });
private void GetSymbolAndIndicesForMemberReference(IMemberReferenceOperation memberReference, ref ISymbol symbolOpt, ref ImmutableArray <AbstractIndex> indices) { switch (memberReference) { case IFieldReferenceOperation fieldReference: symbolOpt = fieldReference.Field; if (fieldReference.Field.CorrespondingTupleField != null) { // For tuple fields, always use the CorrespondingTupleField (i.e. Item1, Item2, etc.) from the underlying value tuple type. // This allows seamless operation between named tuple elements and use of Item1, Item2, etc. to access tuple elements. var name = fieldReference.Field.CorrespondingTupleField.Name; symbolOpt = fieldReference.Field.ContainingType.GetUnderlyingValueTupleTypeOrThis().GetMembers(name).OfType <IFieldSymbol>().FirstOrDefault() ?? symbolOpt; } break; case IEventReferenceOperation eventReference: symbolOpt = eventReference.Member; break; case IPropertyReferenceOperation propertyReference: // We are only tracking: // 1) Indexers // 2) Read-only properties. // 3) Properties with a backing field (auto-generated properties) if (propertyReference.Arguments.Length > 0 || propertyReference.Property.IsReadOnly || propertyReference.Property.IsPropertyWithBackingField()) { symbolOpt = propertyReference.Property; indices = propertyReference.Arguments.Length > 0 ? CreateAbstractIndices(propertyReference.Arguments.Select(a => a.Value).ToImmutableArray()) : ImmutableArray <AbstractIndex> .Empty; } break; } }
protected virtual SyntaxNode GetSyntaxNodeToReplace(IMemberReferenceOperation memberReference) => memberReference.Syntax;
private void VisitMemberReference(IMemberReferenceOperation operation) { VisitMemberReference(operation, Array.Empty <IOperation>()); }
private void AnalyzeOperation(OperationAnalysisContext context, IOperation operation, IOperation?instanceOperation) { // this is a static reference so we don't care if it's qualified if (instanceOperation == null) { return; } // if we're not referencing `this.` or `Me.` (e.g., a parameter, local, etc.) if (instanceOperation.Kind != OperationKind.InstanceReference) { return; } // We shouldn't qualify if it is inside a property pattern if (context.Operation.Parent?.Kind == OperationKind.PropertySubpattern) { return; } // Initializer lists are IInvocationOperation which if passed to GetApplicableOptionFromSymbolKind // will incorrectly fetch the options for method call. // We still want to handle InstanceReferenceKind.ContainingTypeInstance if ((instanceOperation as IInstanceReferenceOperation)?.ReferenceKind == InstanceReferenceKind.ImplicitReceiver) { return; } // If we can't be qualified (e.g., because we're already qualified with `base.`), we're done. if (!CanMemberAccessBeQualified(context.ContainingSymbol, instanceOperation.Syntax)) { return; } // if we can't find a member then we can't do anything. Also, we shouldn't qualify // accesses to static members. if (IsStaticMemberOrIsLocalFunction(operation)) { return; } if (instanceOperation.Syntax is not TSimpleNameSyntax simpleName) { return; } var symbolKind = operation switch { IMemberReferenceOperation memberReferenceOperation => memberReferenceOperation.Member.Kind, IInvocationOperation invocationOperation => invocationOperation.TargetMethod.Kind, _ => throw ExceptionUtilities.UnexpectedValue(operation), }; var simplifierOptions = context.GetAnalyzerOptions().GetSimplifierOptions(Simplification); if (!simplifierOptions.TryGetQualifyMemberAccessOption(symbolKind, out var optionValue)) { return; } var shouldOptionBePresent = optionValue.Value; var severity = optionValue.Notification.Severity; if (!shouldOptionBePresent || severity == ReportDiagnostic.Suppress) { return; } if (!IsAlreadyQualifiedMemberAccess(simpleName)) { context.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, GetLocation(operation), severity, additionalLocations: null, properties: null)); } }