private void AnalyzeOperation(OperationAnalysisContext context) { IArrayCreationExpression arrayCreationExpression = context.Operation as IArrayCreationExpression; // We can't replace array allocations in attributes, as they're persisted to metadata // TODO: Once we have operation walkers, we can replace this syntactic check with an operation-based check. if (arrayCreationExpression.Syntax.Ancestors().Any(IsAttributeSyntax)) { return; } if (arrayCreationExpression.DimensionSizes.Length == 1) { var dimensionSize = arrayCreationExpression.DimensionSizes[0]; if (dimensionSize.ConstantValue.HasValue && (int)dimensionSize.ConstantValue.Value == 0) { // pointers can't be used as generic arguments if (arrayCreationExpression.ElementType.TypeKind != TypeKind.Pointer) { context.ReportDiagnostic(context.Operation.Syntax.CreateDiagnostic(UseArrayEmptyDescriptor)); } } } }
private static void AnalyzeOperation(OperationAnalysisContext context, ImmutableArray<INamedTypeSymbol> taskTypes) { IAwaitExpression awaitExpression = context.Operation as IAwaitExpression; // Get the type of the expression being awaited and check it's a task type. ITypeSymbol typeOfAwaitedExpression = awaitExpression?.AwaitedValue?.Type; if (typeOfAwaitedExpression != null && taskTypes.Contains(typeOfAwaitedExpression.OriginalDefinition)) { context.ReportDiagnostic(awaitExpression.AwaitedValue.Syntax.CreateDiagnostic(Rule)); } }
private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol containingType) { var operation = context.Operation as IInvocationExpression; var method = operation.TargetMethod; if (method != null && (method.IsAbstract || method.IsVirtual) && method.ContainingType == containingType) { context.ReportDiagnostic(operation.Syntax.CreateDiagnostic(Rule)); } }
/// <summary> /// Check to see if we have an invocation to string.Equals that has an empty string as an argument. /// </summary> private static void AnalyzeInvocationExpression(OperationAnalysisContext context) { var invocationOperation = (IInvocationExpression)context.Operation; if (invocationOperation.ArgumentsInSourceOrder.Length > 0) { IMethodSymbol methodSymbol = invocationOperation.TargetMethod; if (methodSymbol != null && IsStringEqualsMethod(methodSymbol) && HasAnEmptyStringArgument(invocationOperation)) { context.ReportDiagnostic(invocationOperation.Syntax.CreateDiagnostic(s_rule)); } } }
/// <summary> /// Check to see if we have a equals or not equals expression where an empty string is being /// compared. /// </summary> private static void AnalyzeBinaryExpression(OperationAnalysisContext context) { var binaryOperation = (IBinaryOperatorExpression)context.Operation; if (binaryOperation.BinaryOperationKind != BinaryOperationKind.StringEquals && binaryOperation.BinaryOperationKind != BinaryOperationKind.StringNotEquals) { return; } if (IsEmptyString(binaryOperation.LeftOperand) || IsEmptyString(binaryOperation.RightOperand)) { context.ReportDiagnostic(binaryOperation.Syntax.CreateDiagnostic(s_rule)); } }
private void AnalyzeOperation(OperationAnalysisContext context) { var switchOperation = (ISwitchStatement)context.Operation; var switchBlock = switchOperation.Syntax; var tree = switchBlock.SyntaxTree; if (SwitchIsIncomplete(switchOperation, out var missingCases, out var missingDefaultCase) && !tree.OverlapsHiddenPosition(switchBlock.Span, context.CancellationToken)) { Debug.Assert(missingCases || missingDefaultCase); var properties = ImmutableDictionary<string, string>.Empty .Add(PopulateSwitchHelpers.MissingCases, missingCases.ToString()) .Add(PopulateSwitchHelpers.MissingDefaultCase, missingDefaultCase.ToString()); var diagnostic = Diagnostic.Create( HiddenDescriptor, switchBlock.GetLocation(), properties: properties); context.ReportDiagnostic(diagnostic); } }
private void AnalyzeOperation(OperationAnalysisContext context) { var syntaxTree = context.Operation.Syntax.SyntaxTree; var cancellationToken = context.CancellationToken; var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { return; } var option = optionSet.GetOption(CodeStyleOptions.PreferExplicitTupleNames, context.Compilation.Language); var severity = option.Notification.Value; if (severity == DiagnosticSeverity.Hidden) { return; } var fieldReferenceOperation = (IFieldReferenceExpression)context.Operation; var field = fieldReferenceOperation.Field; if (field.ContainingType.IsTupleType) { if (field.CorrespondingTupleField.Equals(field)) { var namedField = GetNamedField(field.ContainingType, field, cancellationToken); if (namedField != null) { var memberAccessSyntax = fieldReferenceOperation.Syntax; var nameNode = memberAccessSyntax.ChildNodesAndTokens().Reverse().FirstOrDefault(); if (nameNode != null) { var properties = ImmutableDictionary<string, string>.Empty.Add( nameof(ElementName), namedField.Name); context.ReportDiagnostic(Diagnostic.Create( GetDescriptorWithSeverity(severity), nameNode.GetLocation(), properties)); } } } } }
private static void ReportDiagnostic( OperationAnalysisContext oaContext, IInvocationExpression invocationExpression, IMethodSymbol targetMethod, IMethodSymbol correctOverload) { oaContext.ReportDiagnostic( invocationExpression.Syntax.CreateDiagnostic( Rule, targetMethod.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), oaContext.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), correctOverload.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat))); }
private void AnalyzeOperation(OperationAnalysisContext context) { if (context.ContainingSymbol.IsStatic) { return; } var memberReference = (IMemberReferenceExpression)context.Operation; // this is a static reference so we don't care if it's qualified if (memberReference.Instance == null) { return; } // if we're not referencing `this.` or `Me.` (e.g., a parameter, local, etc.) if (memberReference.Instance.Kind != OperationKind.InstanceReferenceExpression) { return; } // If we can't be qualified (e.g., because we're already qualified with `base.`), we're done. if (!CanMemberAccessBeQualified(context.ContainingSymbol, memberReference.Instance.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 (memberReference.Member == null || memberReference.Member.IsStatic) { return; } var syntaxTree = context.Operation.Syntax.SyntaxTree; var cancellationToken = context.CancellationToken; var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { return; } var language = context.Operation.Syntax.Language; var applicableOption = GetApplicableOptionFromSymbolKind(memberReference.Member.Kind); var optionValue = optionSet.GetOption(applicableOption, language); var shouldOptionBePresent = optionValue.Value; var isQualificationPresent = IsAlreadyQualifiedMemberAccess(memberReference.Instance.Syntax); if (shouldOptionBePresent && !isQualificationPresent) { var severity = optionValue.Notification.Value; if (severity != DiagnosticSeverity.Hidden) { context.ReportDiagnostic(Diagnostic.Create( GetDescriptorWithSeverity(severity), context.Operation.Syntax.GetLocation())); } } }
/// <summary>Reports a diagnostic warning for an array creation that should be replaced.</summary> /// <param name="context">The context.</param> /// <param name="arrayCreationExpression">The array creation expression to be replaced.</param> internal void Report(OperationAnalysisContext context, SyntaxNode arrayCreationExpression) { context.ReportDiagnostic(Diagnostic.Create(UseArrayEmptyDescriptor, arrayCreationExpression.GetLocation())); }
private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); }
public void Analyze(OperationAnalysisContext context, ISymbol owningSymbol) { if (context.Operation.IsInvalid) { // not interested in invalid expression return; } var invocation = (IInvocationExpression)context.Operation; var method = invocation.TargetMethod; // check basic stuff that FxCop checks. if (method.IsFromMscorlib(_compilation)) { // Methods defined within mscorlib are excluded from this rule, // since mscorlib cannot depend on System.Uri, which is defined // in System.dll return; } if (method.GetResultantVisibility() != SymbolVisibility.Public) { // only apply to methods that are exposed outside return; } var node = _expressionGetter(context.Operation.Syntax); if (node == null) { // we don't have right expression node to check overloads return; } // REVIEW: why IOperation doesn't contain things like compilation and semantic model? // it seems wierd that I need to do this to get thsoe. var model = _compilation.GetSemanticModel(context.Operation.Syntax.SyntaxTree); var stringParameters = method.Parameters.GetParametersOfType(_string); if (!stringParameters.Any()) { // no string parameter. not interested. return; } // now do cheap string check whether those string parameter contains uri word list we are looking for. if (!stringParameters.ParameterNamesContainUriWordSubstring(context.CancellationToken)) { // no string parameter that contains what we are looking for. return; } // now we make sure we actually have overloads that contains uri type parameter var overloads = model.GetMemberGroup(node, context.CancellationToken).OfType<IMethodSymbol>(); if (!overloads.HasOverloadWithParameterOfType(method, _uri, context.CancellationToken)) { // no overload that contains uri as parameter return; } // now we do more expensive word parsing to find exact parameter that contains url in parameter name var indicesSet = new HashSet<int>(method.GetParameterIndices(stringParameters.GetParametersThatContainUriWords(context.CancellationToken), context.CancellationToken)); // now we search exact match. this is exactly same behavior as old FxCop foreach (IMethodSymbol overload in overloads) { context.CancellationToken.ThrowIfCancellationRequested(); if (method.Equals(overload) || overload.Parameters.Length != method.Parameters.Length) { // either itself, or signature is not same continue; } if (!method.ParameterTypesAreSame(overload, Enumerable.Range(0, method.Parameters.Length).Where(i => !indicesSet.Contains(i)), context.CancellationToken)) { // check whether remaining parameters match existing types, otherwise, we are not interested continue; } // original FxCop implementation doesnt account for case where original method call contains // 2+ string uri parameters that has overload with matching uri parameters. original implementation works // when there is exactly 1 parameter having matching uri overload. this implementation follow that. foreach (int index in indicesSet) { // check other string uri parameters matches original type if (!method.ParameterTypesAreSame(overload, indicesSet.Where(i => i != index), context.CancellationToken)) { continue; } // okay all other type match. check the main one if (overload.Parameters[index].Type?.Equals(_uri) == true) { context.ReportDiagnostic( node.CreateDiagnostic( Rule, owningSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat), overload.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat), method.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat))); // we no longer interested in this overload. there can be only 1 match break; } } } }
public void Analyze(OperationAnalysisContext analysisContext) { var invocationExpression = (IInvocationExpression)analysisContext.Operation; if (invocationExpression.TargetMethod.OriginalDefinition.Equals(_gcSuppressFinalizeMethodSymbol)) { _suppressFinalizeCalled = true; if (_semanticModel == null) { _semanticModel = analysisContext.Compilation.GetSemanticModel(analysisContext.Operation.Syntax.SyntaxTree); } // Check for GC.SuppressFinalize outside of IDisposable.Dispose() if (_expectedUsage == SuppressFinalizeUsage.MustNotCall) { analysisContext.ReportDiagnostic(invocationExpression.Syntax.CreateDiagnostic( OutsideDisposeRule, _containingMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat), _gcSuppressFinalizeMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat))); } // Checks for GC.SuppressFinalize(this) if (invocationExpression.ArgumentsInSourceOrder.Count() != 1) { return; } var parameterSymbol = _semanticModel.GetSymbolInfo(invocationExpression.ArgumentsInSourceOrder.Single().Syntax).Symbol as IParameterSymbol; if (parameterSymbol == null || !parameterSymbol.IsThis) { analysisContext.ReportDiagnostic(invocationExpression.Syntax.CreateDiagnostic( NotPassedThisRule, _containingMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat), _gcSuppressFinalizeMethodSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat))); } } }
public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary <string, string>?properties, IOperation operation, params string[] messageArgs) { context.ReportDiagnostic(CreateDiagnostic(descriptor, operation.Syntax.GetLocation(), properties, messageArgs)); }
/// <summary>Reports a diagnostic warning for an array creation that should be replaced.</summary> /// <param name="context">The context.</param> /// <param name="arrayCreationExpression">The array creation expression to be replaced.</param> internal void Report(OperationAnalysisContext context, SyntaxNode arrayCreationExpression) { context.ReportDiagnostic(Diagnostic.Create(UseArrayEmptyDescriptor, arrayCreationExpression.GetLocation())); }
private static void AnalyzeConstructorBody(OperationAnalysisContext context) { IConstructorBodyOperation operation = ((IConstructorBodyOperation)context.Operation); Compilation compilation = context.Compilation; TypeSet exceptions = GetIgnoredExceptionSet(compilation); IMethodSymbol symbol = operation.GetSymbol(); if (symbol.IsStatic) { foreach (CrefSyntax cref in operation.GetDeclarationSyntax().GetDocumentedExceptions()) { Diagnostic diagnostic = Diagnostic.Create( descriptor: Descriptors.StaticConstructorsShouldNotThrowExceptions, location: cref.Ancestors().OfType <XmlNodeSyntax>().FirstOrDefault()?.GetLocation() ); context.ReportDiagnostic(diagnostic); } context.ReportUndocumentedExceptions( descriptorForThrow: Descriptors.StaticConstructorsShouldNotThrowExceptions, descriptorForInvocation: Descriptors.StaticConstructorsShouldCatchCalleeExceptions, documented: exceptions, exceptions: operation.GetExceptionalOperations(compilation) ); } else { exceptions = exceptions.Add(operation.GetDocumentedExceptions()); context.ReportUndocumentedExceptions( descriptorForThrow: Descriptors.DocumentThrownExceptions, descriptorForInvocation: Descriptors.DocumentCalleeExceptions, documented: exceptions, exceptions: operation.GetExceptionalOperations(compilation) ); if (operation.Initializer is null) { // // HACK: Implicit/compiler-generated calls to parameterless base-class constructors are // not included in the operation hierarchy and need to be handled explicitly. // if ( (symbol is IMethodSymbol constructor) && !constructor.IsStatic && (constructor.ContainingType is INamedTypeSymbol type) && (type.TypeKind == TypeKind.Class) && (type.BaseType is INamedTypeSymbol baseType) && (baseType.GetParameterlessInstanceConstructor() is IMethodSymbol baseParameterlessConstructor) ) { Location location = operation.GetDeclarationSyntax().GetIdentifierLocations().First(); // // TODO: better diagnostic message which refers to the implicit parameterless constructor call // context.ReportUndocumentedExceptions( descriptorForThrow: Descriptors.DocumentThrownExceptions, descriptorForInvocation: Descriptors.DocumentCalleeExceptions, documented: exceptions, exceptions: baseParameterlessConstructor .GetDocumentedExceptions(compilation) .Select(exception => new ExceptionalOperation(location, baseParameterlessConstructor, exception)) ); } } } }
private void AnalyzeSymbolInitializer(OperationAnalysisContext context) { ISymbolInitializerOperation operation = ((ISymbolInitializerOperation)context.Operation); Compilation compilation = context.Compilation; ISymbol initializedMember = operation.GetInitializedMembers().First(); IEnumerable <ExceptionalOperation> initializerExceptions = operation.GetExceptionalOperations(compilation); if (initializedMember.IsStatic) { foreach (ExceptionalOperation initializerException in initializerExceptions) { context.ReportDiagnostic(Diagnostic.Create( Descriptors.StaticInitializersShouldNotThrowExceptions, initializerException.Location )); } } else { IEnumerable <IMethodSymbol> initializationConstructors = operation.GetInitializationConstructors(); TypeSet documentedExceptionsInAllInitializationConstructors = TypeSet.Universal; foreach (IMethodSymbol constructor in initializationConstructors) { TypeSet documentedExceptions = constructor.GetDocumentedExceptionSet(compilation); documentedExceptionsInAllInitializationConstructors &= documentedExceptions; foreach (ExceptionalOperation exception in initializerExceptions) { ITypeSymbol exceptionType = exception.Type; if (!documentedExceptions.Contains(exceptionType)) { foreach (Location location in constructor.Locations) { context.ReportDiagnostic(Diagnostic.Create( Descriptors.DocumentInstanceMemberInitializerExceptions, location, exceptionType.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat), initializedMember.Name )); } } } } // For each exception that isn't documented in all the right places, add an additional diagnostic at the throw/invocation site foreach (ExceptionalOperation exception in initializerExceptions) { Boolean documentedInAllInitializationConstructors = documentedExceptionsInAllInitializationConstructors.Contains(exception.Type); if (!documentedInAllInitializationConstructors) { context.ReportUndocumentedException( descriptorForThrow: Descriptors.DocumentInstanceMemberInitializerThrownExceptions, descriptorForInvocation: Descriptors.DocumentInstanceMemberInitializerCalleeExceptions, exception: exception ); } } } }
static void CheckSegments(OperationAnalysisContext ctx, IList <IFormatStringSegment> segments, int formatStart, IList <IArgumentOperation> formatArguments) { if (segments.Count == 0) { return; } SyntaxTree syntaxTree = ctx.Operation.Syntax.SyntaxTree; var outOfBoundsFormat = GettextCatalog.GetString("The index '{0}' is out of bounds of the passed arguments"); var multipleFormat = GettextCatalog.GetString("Multiple:\n{0}"); int argumentCount = formatArguments.Count; foreach (var segment in segments) { var errors = segment.Errors.ToList(); if (segment is FormatItem formatItem) { var location = Location.Create(syntaxTree, new Microsoft.CodeAnalysis.Text.TextSpan(formatStart + segment.StartLocation, segment.EndLocation - segment.StartLocation)); if (formatItem.Index >= argumentCount) { ctx.ReportDiagnostic(Diagnostic.Create( descriptor, location, string.Format(outOfBoundsFormat, formatItem.Index) )); } if (formatItem.HasErrors) { var errorMessage = string.Join(Environment.NewLine, errors.Select(error => error.Message).ToArray()); string messageFormat; if (errors.Count > 1) { messageFormat = multipleFormat; } else { messageFormat = "{0}"; } ctx.ReportDiagnostic(Diagnostic.Create( descriptor, location, string.Format(messageFormat, errorMessage) )); } } else if (segment.HasErrors) { foreach (var error in errors) { ctx.ReportDiagnostic(Diagnostic.Create( descriptor, Location.Create(syntaxTree, new Microsoft.CodeAnalysis.Text.TextSpan(formatStart + error.StartLocation, error.EndLocation - error.StartLocation)), error.Message )); } } } }
public void Analyze(OperationAnalysisContext context) { var invocation = (IInvocationOperation)context.Operation; var method = invocation.TargetMethod; // check basic stuff that FxCop checks. if (method.IsFromMscorlib(_compilation)) { // Methods defined within mscorlib are excluded from this rule, // since mscorlib cannot depend on System.Uri, which is defined // in System.dll return; } if (!context.Options.MatchesConfiguredVisibility(Rule, method, context.ContainingSymbol, context.Compilation)) { // only apply to methods that are exposed outside by default return; } var node = _expressionGetter(context.Operation.Syntax); if (node == null) { // we don't have right expression node to check overloads return; } var stringParameters = method.Parameters.GetParametersOfType(_string); if (!stringParameters.Any()) { // no string parameter. not interested. return; } // now do cheap string check whether those string parameter contains uri word list we are looking for. if (!stringParameters.ParameterNamesContainUriWordSubstring(context.CancellationToken)) { // no string parameter that contains what we are looking for. return; } // now we make sure we actually have overloads that contains uri type parameter var overloads = context.Operation.SemanticModel.GetMemberGroup(node, context.CancellationToken).OfType <IMethodSymbol>(); if (!overloads.HasOverloadWithParameterOfType(method, _uri, context.CancellationToken)) { // no overload that contains uri as parameter return; } // now we do more expensive word parsing to find exact parameter that contains url in parameter name var indicesSet = new HashSet <int>(method.GetParameterIndices(stringParameters.GetParametersThatContainUriWords(context.CancellationToken), context.CancellationToken)); // now we search exact match. this is exactly same behavior as old FxCop foreach (IMethodSymbol overload in overloads) { context.CancellationToken.ThrowIfCancellationRequested(); if (method.Equals(overload) || overload.Parameters.Length != method.Parameters.Length) { // either itself, or signature is not same continue; } if (!method.ParameterTypesAreSame(overload, Enumerable.Range(0, method.Parameters.Length).Where(i => !indicesSet.Contains(i)), context.CancellationToken)) { // check whether remaining parameters match existing types, otherwise, we are not interested continue; } // original FxCop implementation doesn't account for case where original method call contains // 2+ string uri parameters that has overload with matching uri parameters. original implementation works // when there is exactly 1 parameter having matching uri overload. this implementation follow that. foreach (int index in indicesSet) { // check other string uri parameters matches original type if (!method.ParameterTypesAreSame(overload, indicesSet.Where(i => i != index), context.CancellationToken)) { continue; } // okay all other type match. check the main one if (overload.Parameters[index].Type?.Equals(_uri) == true && !Equals(overload, context.ContainingSymbol)) { context.ReportDiagnostic( node.CreateDiagnostic( Rule, context.ContainingSymbol.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat), overload.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat), method.ToDisplayString(SymbolDisplayFormats.ShortSymbolDisplayFormat))); // we no longer interested in this overload. there can be only 1 match break; } } } }
private static void AnalyzeRaiseEvent(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; var targetMethod = operation.TargetMethod; if (targetMethod.Name != nameof(EventHandler.Invoke)) { return; } if (targetMethod.Parameters.Length != 2) { return; } if (!targetMethod.Parameters[0].Type.IsObject()) { return; } var eventArgsSymbol = context.Compilation.GetTypeByMetadataName("System.EventArgs"); if (!targetMethod.Parameters[1].Type.IsOrInheritFrom(eventArgsSymbol)) { return; } var multicastDelegateSymbol = context.Compilation.GetTypeByMetadataName("System.MulticastDelegate"); if (!targetMethod.ContainingType.IsOrInheritFrom(multicastDelegateSymbol)) { return; } var instance = operation.Instance; if (instance == null) { return; } var ev = FindEvent(instance); if (ev == null) { return; } // Argument validation var senderArgument = operation.Arguments[0]; if (ev.IsStatic) { if (!IsNull(senderArgument)) { context.ReportDiagnostic(s_senderStaticRule, senderArgument); } } else { if (!IsThis(senderArgument)) { context.ReportDiagnostic(s_senderInstanceRule, senderArgument); } } var eventArgsArgument = operation.Arguments[1]; if (IsNull(eventArgsArgument)) { context.ReportDiagnostic(s_eventArgsRule, eventArgsArgument); } }
private static void ReportDiagnostic(OperationAnalysisContext context, LocalizableString format, params object[] args) { context.ReportDiagnostic( context.Operation.Syntax.CreateDiagnostic( Descriptor, string.Format(format.ToString(), args))); }
private void AnalyzeObjectCreationForXmlDocument(OperationAnalysisContext context, ISymbol variable, IObjectCreationOperation objCreation) { if (variable == null || !_xmlDocumentEnvironments.TryGetValue(variable, out var xmlDocumentEnvironment)) { xmlDocumentEnvironment = new XmlDocumentEnvironment { IsSecureResolver = false, IsXmlResolverSet = false }; } xmlDocumentEnvironment.XmlDocumentDefinition = objCreation.Syntax; SyntaxNode node = objCreation.Syntax; bool isXmlDocumentSecureResolver = false; if (!Equals(objCreation.Constructor.ContainingType, _xmlTypes.XmlDocument)) { isXmlDocumentSecureResolver = true; } // propertyInitlizer is not returned any more // and no way to get propertysymbol if (objCreation.Initializer != null) { foreach (IOperation init in objCreation.Initializer.Initializers) { if (init is IAssignmentOperation assign) { var propValue = assign.Value; if (!(assign.Target is IPropertyReferenceOperation propertyReference)) { continue; } var prop = propertyReference.Property; if (prop.MatchPropertyDerivedByName(_xmlTypes.XmlDocument, "XmlResolver")) { if (!(propValue is IConversionOperation operation)) { return; } if (SecurityDiagnosticHelpers.IsXmlSecureResolverType(operation.Operand.Type, _xmlTypes)) { isXmlDocumentSecureResolver = true; } else if (SecurityDiagnosticHelpers.IsExpressionEqualsNull(operation.Operand)) { isXmlDocumentSecureResolver = true; } else // Non secure resolvers { context.ReportDiagnostic(assign.Syntax.CreateDiagnostic(RuleXmlDocumentWithNoSecureResolver)); return; } } else { AnalyzeNeverSetProperties(context, prop, assign.Syntax.GetLocation()); } } } } xmlDocumentEnvironment.IsSecureResolver = isXmlDocumentSecureResolver; if (variable != null) { _xmlDocumentEnvironments[variable] = xmlDocumentEnvironment; } else if (!xmlDocumentEnvironment.IsSecureResolver) // Insecure temp object { context.ReportDiagnostic(node.CreateDiagnostic(RuleXmlDocumentWithNoSecureResolver)); } return; }
public void AnalyzeInvocation(OperationAnalysisContext ctx) { var operation = (IInvocationOperation)ctx.Operation; if (HasEqualityComparerArgument(operation.Arguments)) { return; } var method = operation.TargetMethod; if (method.HasOverloadWithAdditionalParameterOfType(ctx.Compilation, EqualityComparerStringType) || method.HasOverloadWithAdditionalParameterOfType(ctx.Compilation, ComparerStringType)) { ctx.ReportDiagnostic(s_rule, operation); return; } if (EnumerableType != null) { if (!method.ContainingType.IsEqualTo(EnumerableType)) { return; } if (method.Arity == 0) { return; } if (method.Arity == 1) { if (!s_enumerableMethods.Contains(method.Name, StringComparer.Ordinal)) { return; } if (!method.TypeArguments[0].IsString()) { return; } } else { if (!s_arityIndex.TryGetValue(method.Name, out var arityIndex)) { return; } if (arityIndex >= method.Arity) { return; } if (!method.TypeArguments[arityIndex].IsString()) { return; } } if (!HasEqualityComparerArgument(operation.Arguments)) { ctx.ReportDiagnostic(s_rule, operation); } } }
private void AnalyzeMethodOverloads(OperationAnalysisContext context, IMethodSymbol method, IHasArgumentsExpression expression) { if (method.MatchMethodDerivedByName(_xmlTypes.XmlDocument, SecurityMemberNames.Load) || //FxCop CA3056 method.MatchMethodDerivedByName(_xmlTypes.XmlDocument, SecurityMemberNames.LoadXml) || //FxCop CA3057 method.MatchMethodDerivedByName(_xmlTypes.XPathDocument, WellKnownMemberNames.InstanceConstructorName) || //FxCop CA3059 method.MatchMethodDerivedByName(_xmlTypes.XmlSchema, SecurityMemberNames.Read) || //FxCop CA3060 method.MatchMethodDerivedByName(_xmlTypes.DataSet, SecurityMemberNames.ReadXml) || //FxCop CA3063 method.MatchMethodDerivedByName(_xmlTypes.DataSet, SecurityMemberNames.ReadXmlSchema) || //FxCop CA3064 method.MatchMethodDerivedByName(_xmlTypes.XmlSerializer, SecurityMemberNames.Deserialize) || //FxCop CA3070 method.MatchMethodDerivedByName(_xmlTypes.DataTable, SecurityMemberNames.ReadXml) || //FxCop CA3071 method.MatchMethodDerivedByName(_xmlTypes.DataTable, SecurityMemberNames.ReadXmlSchema)) //FxCop CA3072 { if (SecurityDiagnosticHelpers.HasXmlReaderParameter(method, _xmlTypes) < 0) { DiagnosticDescriptor rule = RuleDoNotUseInsecureDtdProcessing; context.ReportDiagnostic( Diagnostic.Create( rule, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.DoNotUseDtdProcessingOverloadsMessage), method.Name ) ) ); } } else if (method.MatchMethodDerivedByName(_xmlTypes.XmlReader, SecurityMemberNames.Create)) { int xmlReaderSettingsIndex = SecurityDiagnosticHelpers.GetXmlReaderSettingsParameterIndex(method, _xmlTypes); if (xmlReaderSettingsIndex < 0) { DiagnosticDescriptor rule = RuleDoNotUseInsecureDtdProcessing; Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateWrongOverloadMessage) ) ); context.ReportDiagnostic(diag); } else { SemanticModel model = context.Compilation.GetSemanticModel(context.Operation.Syntax.SyntaxTree); IArgument arg = expression.ArgumentsInParameterOrder[xmlReaderSettingsIndex]; ISymbol settingsSymbol = arg.Value.Syntax.GetDeclaredOrReferencedSymbol(model); if(settingsSymbol == null) { return; } XmlReaderSettingsEnvironment env; if (!_xmlReaderSettingsEnvironments.TryGetValue(settingsSymbol, out env)) { // symbol for settings is not found => passed in without any change => assume insecure Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateInsecureInputMessage) ) ); context.ReportDiagnostic(diag); } else if (!env.IsDtdProcessingDisabled && !(env.IsSecureResolver && env.IsMaxCharactersFromEntitiesLimited)) { Diagnostic diag; if (env.IsConstructedInCodeBlock) { diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateInsecureConstructedMessage) ) ); } else { diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateInsecureInputMessage) ) ); } context.ReportDiagnostic(diag); } } } }
private void AnalyzeOperation(OperationAnalysisContext context, Func<SyntaxNode, bool> isAttributeSytnax) { IArrayCreationExpression arrayCreationExpression = (IArrayCreationExpression)context.Operation; // We can't replace array allocations in attributes, as they're persisted to metadata // TODO: Once we have operation walkers, we can replace this syntactic check with an operation-based check. if (arrayCreationExpression.Syntax.Ancestors().Any(isAttributeSytnax)) { return; } if (arrayCreationExpression.DimensionSizes.Length == 1) { IOperation dimensionSize = arrayCreationExpression.DimensionSizes[0]; if (dimensionSize.HasConstantValue(0)) { // Workaround for https://github.com/dotnet/roslyn/issues/10214 // Bail out for compiler generated param array creation. if (IsCompilerGeneratedParamsArray(arrayCreationExpression, context)) { return; } // pointers can't be used as generic arguments if (arrayCreationExpression.ElementType.TypeKind != TypeKind.Pointer) { var arrayType = context.Compilation.GetTypeByMetadataName(ArrayTypeName); IMethodSymbol emptyMethod = (IMethodSymbol)arrayType.GetMembers(ArrayEmptyMethodName).First(); var constructed = emptyMethod.Construct(arrayCreationExpression.ElementType); string typeName = constructed.ToDisplayString(ReportFormat); context.ReportDiagnostic(context.Operation.Syntax.CreateDiagnostic(UseArrayEmptyDescriptor, typeName)); } } } }
private void AnalyzeObjectCreationForXmlTextReader(OperationAnalysisContext context, ISymbol variable, IObjectCreationExpression objCreation) { XmlTextReaderEnvironment env; if (variable == null || !_xmlTextReaderEnvironments.TryGetValue(variable, out env)) { env = new XmlTextReaderEnvironment(_isFrameworkSecure) { XmlTextReaderDefinition = objCreation.Syntax }; } if (objCreation.Constructor.ContainingType != _xmlTypes.XmlTextReader) { env.IsDtdProcessingDisabled = true; env.IsSecureResolver = true; } foreach (ISymbolInitializer init in objCreation.MemberInitializers) { var prop = init as IPropertyInitializer; if (prop != null) { IConversionExpression operation = prop.Value as IConversionExpression; if (operation != null && SecurityDiagnosticHelpers.IsXmlTextReaderXmlResolverPropertyDerived(prop.InitializedProperty, _xmlTypes)) { env.IsXmlResolverSet = true; if (SecurityDiagnosticHelpers.IsXmlSecureResolverType(operation.Operand.Type, _xmlTypes)) { env.IsSecureResolver = true; } else if (SecurityDiagnosticHelpers.IsExpressionEqualsNull(operation.Operand)) { env.IsSecureResolver = true; } else { env.IsSecureResolver = false; } } else if (SecurityDiagnosticHelpers.IsXmlTextReaderDtdProcessingPropertyDerived(prop.InitializedProperty, _xmlTypes)) { env.IsDtdProcessingSet = true; env.IsDtdProcessingDisabled = !SecurityDiagnosticHelpers.IsExpressionEqualsDtdProcessingParse(prop.Value); } } } // if the XmlResolver or Dtdprocessing property is explicitly set when created, and is to an insecure value, generate a warning if ((env.IsXmlResolverSet && !env.IsSecureResolver) || (env.IsDtdProcessingSet && !env.IsDtdProcessingDisabled)) { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, env.XmlTextReaderDefinition.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlTextReaderSetInsecureResolutionMessage) ) ); context.ReportDiagnostic(diag); } // if the XmlResolver or Dtdprocessing property is not explicitly set when constructed for a non-temp XmlTextReader object, add env to the dictionary. else if (variable != null && !(env.IsDtdProcessingSet && env.IsXmlResolverSet)) { _xmlTextReaderEnvironments[variable] = env; } // if the is not set or set to Parse for a temporary object, report right now. else if (variable == null && !(env.IsDtdProcessingSet && env.IsXmlResolverSet && env.IsDtdProcessingDisabled && env.IsSecureResolver)) { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, env.XmlTextReaderDefinition.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlTextReaderConstructedWithNoSecureResolutionMessage) ) ); context.ReportDiagnostic(diag); } }
private void AnalyzeXmlTextReaderProperties(OperationAnalysisContext context, ISymbol assignedSymbol, IAssignmentExpression expression, bool isXmlTextReaderXmlResolverProperty, bool isXmlTextReaderDtdProcessingProperty) { XmlTextReaderEnvironment env; if (!_xmlTextReaderEnvironments.TryGetValue(assignedSymbol, out env)) { env = new XmlTextReaderEnvironment(_isFrameworkSecure); } if (isXmlTextReaderXmlResolverProperty) { env.IsXmlResolverSet = true; } else { env.IsDtdProcessingSet = true; } IConversionExpression conv = expression.Value as IConversionExpression; if (isXmlTextReaderXmlResolverProperty && conv != null && SecurityDiagnosticHelpers.IsXmlSecureResolverType(conv.Operand.Type, _xmlTypes)) { env.IsSecureResolver = true; } else if (isXmlTextReaderXmlResolverProperty && conv != null && SecurityDiagnosticHelpers.IsExpressionEqualsNull(conv.Operand)) { env.IsSecureResolver = true; } else if (isXmlTextReaderDtdProcessingProperty && conv == null && !SecurityDiagnosticHelpers.IsExpressionEqualsDtdProcessingParse(expression.Value)) { env.IsDtdProcessingDisabled = !SecurityDiagnosticHelpers.IsExpressionEqualsDtdProcessingParse(expression.Value); } else { // Generate a warning whenever the XmlResolver or DtdProcessing property is set to an insecure value Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlTextReaderSetInsecureResolutionMessage) ) ); context.ReportDiagnostic(diag); } }
private void AnalyzeOperation(OperationAnalysisContext context) { IArrayCreationExpression arrayCreationExpression = context.Operation as IArrayCreationExpression; // We can't replace array allocations in attributes, as they're persisted to metadata // TODO: Once we have operation walkers, we can replace this syntactic check with an operation-based check. if (arrayCreationExpression.Syntax.Ancestors().Any(IsAttributeSyntax)) { return; } if (arrayCreationExpression.DimensionSizes.Length == 1) { IOperation dimensionSize = arrayCreationExpression.DimensionSizes[0]; if (dimensionSize.HasConstantValue(0)) { // Workaround for https://github.com/dotnet/roslyn/issues/10214 // Bail out for compiler generated param array creation. if (IsCompilerGeneratedParamsArray(arrayCreationExpression, context)) { return; } // pointers can't be used as generic arguments if (arrayCreationExpression.ElementType.TypeKind != TypeKind.Pointer) { context.ReportDiagnostic(context.Operation.Syntax.CreateDiagnostic(UseArrayEmptyDescriptor)); } } } }
public void AnalyzeInvocationOperation(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; if (operation.TargetMethod.Name == nameof(ValueType.GetHashCode)) { var actualType = operation.Children.FirstOrDefault()?.GetActualType(); if (actualType == null) { return; } if (IsStruct(actualType) && HasDefaultEqualsOrHashCodeImplementations(actualType)) { context.ReportDiagnostic(s_rule, operation); } } else if (operation.TargetMethod.Name == nameof(ValueType.Equals)) { var actualType = operation.Children.FirstOrDefault()?.GetActualType(); if (actualType == null) { return; } if (IsStruct(actualType) && HasDefaultEqualsOrHashCodeImplementations(actualType)) { context.ReportDiagnostic(s_rule, operation); } } else if (IsImmutableCreateMethod(operation.TargetMethod)) { var type = operation.TargetMethod.TypeArguments[0]; if (IsStruct(type) && HasDefaultEqualsOrHashCodeImplementations(type)) { if (operation.TargetMethod.ContainingType.IsEqualTo(ImmutableSortedDictionarySymbol)) { if (operation.TargetMethod.Parameters.Any(arg => arg.Type.IsEqualTo(IComparerSymbol?.Construct(type)))) { return; } } else { if (operation.TargetMethod.Parameters.Any(arg => arg.Type.IsEqualTo(IEqualityComparerSymbol?.Construct(type)))) { return; } } context.ReportDiagnostic(s_rule2, operation); } } bool IsImmutableCreateMethod(IMethodSymbol methodSymbol) { var names = new[] { "Create", "CreateBuilder", "CreateRange", }; var builderTypes = new[] { ImmutableDictionarySymbol, ImmutableHashSetSymbol, ImmutableSortedDictionarySymbol, }; return(methodSymbol.Arity >= 1 && names.Contains(methodSymbol.Name, StringComparer.Ordinal) && builderTypes.Any(type => type.IsEqualTo(methodSymbol.ContainingType.OriginalDefinition))); } }
public void Analyze(OperationAnalysisContext context, ISymbol owningSymbol) { if (context.Operation.IsInvalid) { // not interested in invalid expression return; } var invocation = (IInvocationExpression)context.Operation; IMethodSymbol method = invocation.TargetMethod; SyntaxNode node = _expressionGetter(context.Operation.Syntax); if (node == null) { // we don't have right expression node to check overloads return; } // REVIEW: why IOperation doesn't contain things like compilation and semantic model? // it seems wierd that I need to do this to get thsoe. SemanticModel model = _compilation.GetSemanticModel(context.Operation.Syntax.SyntaxTree); // due to limitation of using "this" in lambda in struct INamedTypeSymbol stringType = _string; IEnumerable<IParameterSymbol> stringParameters = method.Parameters.Where(p => p.Type?.Equals(stringType) == true); if (!stringParameters.Any()) { // no string parameter. not interested. return; } // now do cheap string check whether those string parameter contains uri word list we are looking for. if (!CheckStringParametersContainUriWords(stringParameters, context.CancellationToken)) { // no string parameter that contains what we are looking for. return; } // now we make sure we actually have overloads that contains uri type parameter if (!CheckOverloadsContainUriParameters(model, method, node, context.CancellationToken)) { // no overload that contains uri as parameter return; } // now we do more expensive word parsing to find exact parameter that contains url in parameter name var indicesSet = new HashSet<int>(GetParameterIndices(method, GetStringParametersThatContainsUriWords(stringParameters, context.CancellationToken), context.CancellationToken)); // now we search exact match. this is exactly same behavior as old FxCop foreach (IMethodSymbol overload in model.GetMemberGroup(node, context.CancellationToken).OfType<IMethodSymbol>()) { context.CancellationToken.ThrowIfCancellationRequested(); if (method.Equals(overload) || overload.Parameters.Length != method.Parameters.Length) { // either itself, or signature is not same continue; } if (!CheckParameterTypes(method, overload, Enumerable.Range(0, method.Parameters.Length).Where(i => !indicesSet.Contains(i)), context.CancellationToken)) { // check whether remaining parameters match existing types, otherwise, we are not interested continue; } // original FxCop implementation doesnt account for case where original method call contains // 2+ string uri parameters that has overload with matching uri parameters. original implementation works // when there is exactly 1 parameter having matching uri overload. this implementation follow that. foreach (int index in indicesSet) { // check other string uri parameters matches original type if (!CheckParameterTypes(method, overload, indicesSet.Where(i => i != index), context.CancellationToken)) { continue; } // okay all other type match. check the main one if (overload.Parameters[index].Type?.Equals(_uri) == true) { context.ReportDiagnostic(node.CreateDiagnostic(Rule, owningSymbol.ToDisplayString(s_symbolDisplayFormat), overload.ToDisplayString(s_symbolDisplayFormat), method.ToDisplayString(s_symbolDisplayFormat))); // we no longer interested in this overload. there can be only 1 match break; } } } }
private void AnalyzeObjectCreationForXmlDocument(OperationAnalysisContext context, ISymbol variable, IObjectCreationExpression objCreation) { XmlDocumentEnvironment xmlDocumentEnvironment; if (variable == null || !_xmlDocumentEnvironments.ContainsKey(variable)) { xmlDocumentEnvironment = new XmlDocumentEnvironment { IsSecureResolver = false, IsXmlResolverSet = false }; } else { xmlDocumentEnvironment = _xmlDocumentEnvironments[variable]; } xmlDocumentEnvironment.XmlDocumentDefinition = objCreation.Syntax; SyntaxNode node = objCreation.Syntax; bool isXmlDocumentSecureResolver = false; if (objCreation.Constructor.ContainingType != _xmlTypes.XmlDocument) { isXmlDocumentSecureResolver = true; } foreach (ISymbolInitializer init in objCreation.MemberInitializers) { var prop = init as IPropertyInitializer; if (prop != null) { if (prop.InitializedProperty.MatchPropertyDerivedByName(_xmlTypes.XmlDocument, "XmlResolver")) { IConversionExpression operation = prop.Value as IConversionExpression; if (operation == null) { return; } if (SecurityDiagnosticHelpers.IsXmlSecureResolverType(operation.Operand.Type, _xmlTypes)) { isXmlDocumentSecureResolver = true; } else if (SecurityDiagnosticHelpers.IsExpressionEqualsNull(operation.Operand)) { isXmlDocumentSecureResolver = true; } else // Non secure resolvers { IObjectCreationExpression xmlResolverObjCreated = operation.Operand as IObjectCreationExpression; if (xmlResolverObjCreated != null) { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, prop.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlDocumentWithNoSecureResolverMessage) ) ); context.ReportDiagnostic(diag); } return; } } else { AnalyzeNeverSetProperties(context, prop.InitializedProperty, prop.Syntax.GetLocation()); } } } xmlDocumentEnvironment.IsSecureResolver = isXmlDocumentSecureResolver; if (variable != null) { _xmlDocumentEnvironments[variable] = xmlDocumentEnvironment; } else if (!xmlDocumentEnvironment.IsSecureResolver) // Insecure temp object { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, node.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlDocumentWithNoSecureResolverMessage) ) ); context.ReportDiagnostic(diag); } }
private static void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols) { if (!string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal)) { return; } var binaryOperation = GetParentBinaryOperation(operation, out var countOperand); if (binaryOperation == null) { return; } if (!IsSupportedOperator(binaryOperation.OperatorKind)) { return; } if (!binaryOperation.LeftOperand.Type.IsInt32() || !binaryOperation.RightOperand.Type.IsInt32()) { return; } var opKind = NormalizeOperator(); var otherOperand = binaryOperation.LeftOperand == countOperand ? binaryOperation.RightOperand : binaryOperation.LeftOperand; if (otherOperand == null) { return; } string message = null; var properties = ImmutableDictionary <string, string> .Empty; if (otherOperand.ConstantValue.HasValue && otherOperand.ConstantValue.Value is int value) { switch (opKind) { case BinaryOperatorKind.Equals: if (value < 0) { // expr.Count() == -1 message = "Expression is always false"; properties = CreateProperties(OptimizeLinqUsageData.UseFalse); } else if (value == 0) { // expr.Count() == 0 message = "Replace 'Count() == 0' with 'Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseNotAny); } else { // expr.Count() == 1 if (!HasTake()) { message = Invariant($"Replace 'Count() == {value}' with 'Take({value + 1}).Count() == {value}'"); properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } } break; case BinaryOperatorKind.NotEquals: if (value < 0) { // expr.Count() != -1 is always true message = "Expression is always true"; properties = CreateProperties(OptimizeLinqUsageData.UseTrue); } else if (value == 0) { // expr.Count() != 0 message = "Replace 'Count() != 0' with 'Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseAny); } else { // expr.Count() != 1 if (!HasTake()) { message = Invariant($"Replace 'Count() != {value}' with 'Take({value + 1}).Count() != {value}'"); properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } } break; case BinaryOperatorKind.LessThan: if (value <= 0) { // expr.Count() < 0 message = "Expression is always false"; properties = CreateProperties(OptimizeLinqUsageData.UseFalse); } else if (value == 1) { // expr.Count() < 1 ==> expr.Count() == 0 message = "Replace 'Count() < 1' with 'Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseNotAny); } else { // expr.Count() < 10 message = Invariant($"Replace 'Count() < {value}' with 'Skip({value - 1}).Any() == false'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny) .Add("SkipMinusOne", null); } break; case BinaryOperatorKind.LessThanOrEqual: if (value < 0) { // expr.Count() <= -1 message = "Expression is always false"; properties = CreateProperties(OptimizeLinqUsageData.UseFalse); } else if (value == 0) { // expr.Count() <= 0 message = "Replace 'Count() <= 0' with 'Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseNotAny); } else { // expr.Count() < 10 message = Invariant($"Replace 'Count() <= {value}' with 'Skip({value}).Any() == false'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny); } break; case BinaryOperatorKind.GreaterThan: if (value < 0) { // expr.Count() > -1 message = "Expression is always true"; properties = CreateProperties(OptimizeLinqUsageData.UseTrue); } else if (value == 0) { // expr.Count() > 0 message = "Replace 'Count() > 0' with 'Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseAny); } else { // expr.Count() > 1 message = Invariant($"Replace 'Count() > {value}' with 'Skip({value}).Any()'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny); } break; case BinaryOperatorKind.GreaterThanOrEqual: if (value <= 0) { // expr.Count() >= 0 message = "Expression is always true"; properties = CreateProperties(OptimizeLinqUsageData.UseTrue); } else if (value == 1) { // expr.Count() >= 1 message = "Replace 'Count() >= 1' with 'Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseAny); } else { // expr.Count() >= 2 message = Invariant($"Replace 'Count() >= {value}' with 'Skip({value - 1}).Any()'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny) .Add("SkipMinusOne", null); } break; } } else { switch (opKind) { case BinaryOperatorKind.Equals: // expr.Count() == 1 if (!HasTake()) { message = "Replace 'Count() == n' with 'Take(n + 1).Count() == n'"; properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } break; case BinaryOperatorKind.NotEquals: // expr.Count() != 1 if (!HasTake()) { message = "Replace 'Count() != n' with 'Take(n + 1).Count() != n'"; properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } break; case BinaryOperatorKind.LessThan: // expr.Count() < 10 message = "Replace 'Count() < n' with 'Skip(n - 1).Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny) .Add("SkipMinusOne", null); break; case BinaryOperatorKind.LessThanOrEqual: // expr.Count() <= 10 message = "Replace 'Count() <= n' with 'Skip(n).Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny); break; case BinaryOperatorKind.GreaterThan: // expr.Count() > 1 message = "Replace 'Count() > n' with 'Skip(n).Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny); break; case BinaryOperatorKind.GreaterThanOrEqual: // expr.Count() >= 2 message = "Replace 'Count() >= n' with 'Skip(n - 1).Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny) .Add("SkipMinusOne", null); break; } } if (message != null) { properties = properties .Add("OperandOperationStart", otherOperand.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("OperandOperationLength", otherOperand.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("CountOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("CountOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)); context.ReportDiagnostic(s_optimizeCountRule, properties, binaryOperation, message); }
private void AnalyzeXmlResolverPropertyAssignmentForXmlDocument(OperationAnalysisContext context, ISymbol assignedSymbol, IAssignmentExpression expression) { bool isSecureResolver = false; IConversionExpression conv = expression.Value as IConversionExpression; if (SecurityDiagnosticHelpers.IsXmlSecureResolverType(conv.Operand.Type, _xmlTypes)) { isSecureResolver = true; } else if (conv != null && SecurityDiagnosticHelpers.IsExpressionEqualsNull(conv.Operand)) { isSecureResolver = true; } else // Assigning XmlDocument's XmlResolver to an insecure value { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, context.Operation.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlDocumentWithNoSecureResolverMessage) ) ); context.ReportDiagnostic(diag); } if (_xmlDocumentEnvironments.ContainsKey(assignedSymbol)) { XmlDocumentEnvironment xmlDocumentEnv = _xmlDocumentEnvironments[assignedSymbol]; xmlDocumentEnv.IsXmlResolverSet = true; xmlDocumentEnv.IsSecureResolver = isSecureResolver; } }
private static void LAA1002_DictionarySetEnumeration(OperationAnalysisContext context) { const string ns = "System.Collections"; SymbolDisplayFormat symbolFormat = SymbolDisplayFormat.CSharpErrorMessageFormat; Compilation comp = context.Compilation; SymbolEqualityComparer comparer = SymbolEqualityComparer.Default; bool TypeEquals(ITypeSymbol?valType, string metadataName) => valType is ITypeSymbol a && comp.GetTypeByMetadataName(metadataName) is INamedTypeSymbol b && comparer.Equals(a, b); string[] dictOrSets = { $"{ns}.Generic.IDictionary`2", $"{ns}.Generic.IReadOnlyDictionary`2", $"{ns}.Immutable.IImmutableDictionary`2", $"{ns}.IDictionary", $"{ns}.Generic.ISet`1", $"{ns}.Immutable.IImmutableSet`1", }; string[] sortedTypes = { $"{ns}.Generic.SortedDictionary`2", $"{ns}.Immutable.ImmutableSortedDictionary`2", $"{ns}.Generic.SortedSet`1", $"{ns}.Immutable.ImmutableSortedSet`1", "Bencodex.Types.Dictionary", }; bool IsDictOrSet(ITypeSymbol?valType) => valType is ITypeSymbol t && t.OriginalDefinition.AllInterfaces .OfType <ITypeSymbol>() .Select(ifce => ifce.OriginalDefinition) .OfType <ITypeSymbol>() .Any(i => dictOrSets.Any(dst => TypeEquals(i, dst))); bool IsUnordered(ITypeSymbol?valType) => valType is ITypeSymbol t && IsDictOrSet(t) && !sortedTypes.Any(sortedType => TypeEquals(t.OriginalDefinition, sortedType)); switch (context.Operation) { case IConversionOperation conv: { ITypeSymbol?endType = conv.Type; if (!TypeEquals(endType?.OriginalDefinition, $"{ns}.Generic.IEnumerable`1") && !TypeEquals(endType, $"{ns}.IEnumerable")) { return; } if (conv.Parent is IArgumentOperation arg && arg.Parent is IInvocationOperation invoke && invoke.TargetMethod.IsGenericMethod && invoke.TargetMethod.OriginalDefinition is IMethodSymbol proto) { var method = invoke.TargetMethod.Name; if (method.StartsWith("OrderBy") && TypeEquals(proto.ContainingType, "System.Linq.Enumerable") && TypeEquals( proto.ReturnType.OriginalDefinition, "System.Linq.IOrderedEnumerable`1" )) { // Ignores Linq's .OrderBy()/OrderByDescending() methods. return; } else if (method.StartsWith("To") && (method.EndsWith("Dictionary") || method.EndsWith("Set")) && IsDictOrSet(proto.ReturnType)) { // Ignores .ToDictionary()/ToHashSet() etc. return; } } if (conv.Parent is IArgumentOperation arg1 && arg1.Parent is IObjectCreationOperation @new && IsDictOrSet(@new.Type)) { // Ignores new Dictionary()/new HashSet() etc. return; } ITypeSymbol?valType = conv.Operand?.Type; if (IsUnordered(valType)) { string func = "enumerating"; if (conv.Parent is IArgumentOperation argConv) { func = argConv.Parent switch { IObjectCreationOperation c => $"passing to {c.Constructor.ToDisplayString(symbolFormat)} " + "constructor", IInvocationOperation m => $"passing to {m.TargetMethod.ToDisplayString(symbolFormat)} " + "method", _ => func, }; } Diagnostic diag = Diagnostic.Create( Rules[$"{IdPrefix}1002"], conv.Syntax.GetLocation(), valType !.ToDisplayString(symbolFormat), func ); context.ReportDiagnostic(diag); } break; } case IForEachLoopOperation loop: { IOperation collection = loop.Collection; ITypeSymbol?collType = collection.Type; if (IsUnordered(collType)) { Diagnostic diag = Diagnostic.Create( Rules[$"{IdPrefix}1002"], collection.Syntax.GetLocation(), collType.ToDisplayString(symbolFormat), "iterating via foreach" ); context.ReportDiagnostic(diag); } break; } } }
private void AnalyzeNeverSetProperties(OperationAnalysisContext context, IPropertySymbol property, Location location) { if (property.MatchPropertyDerivedByName(_xmlTypes.XmlDocument, SecurityMemberNames.InnerXml)) { DiagnosticDescriptor rule = RuleDoNotUseInsecureDtdProcessing; context.ReportDiagnostic( Diagnostic.Create( rule, location, SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.DoNotUseSetInnerXmlMessage) ) ) ); } else if (property.MatchPropertyDerivedByName(_xmlTypes.DataViewManager, SecurityMemberNames.DataViewSettingCollectionString)) { DiagnosticDescriptor rule = RuleDoNotUseInsecureDtdProcessing; context.ReportDiagnostic( Diagnostic.Create( rule, location, SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.ReviewDtdProcessingPropertiesMessage) ) ) ); } }
private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol?expressionTypeOpt) { var cancellationToken = context.CancellationToken; var throwOperation = (IThrowOperation)context.Operation; if (throwOperation.Exception == null) { return; } var throwStatementSyntax = throwOperation.Syntax; var semanticModel = context.Operation.SemanticModel; Contract.ThrowIfNull(semanticModel); var ifOperation = GetContainingIfOperation( semanticModel, throwOperation, cancellationToken); // This throw statement isn't parented by an if-statement. Nothing to // do here. if (ifOperation == null) { return; } if (ifOperation.WhenFalse != null) { // Can't offer this if the 'if-statement' has an 'else-clause'. return; } var option = context.GetOption(_preferThrowExpressionOption); if (!option.Value) { return; } if (IsInExpressionTree(semanticModel, throwStatementSyntax, expressionTypeOpt, cancellationToken)) { return; } if (ifOperation.Parent is not IBlockOperation containingBlock) { return; } if (!TryDecomposeIfCondition(ifOperation, out var localOrParameter)) { return; } if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter, out var expressionStatement, out var assignmentExpression)) { return; } if (!localOrParameter.GetSymbolType().CanAddNullCheck()) { return; } // We found an assignment using this local/parameter. Now, just make sure there // were no intervening accesses between the check and the assignment. if (ValueIsAccessed( semanticModel, ifOperation, containingBlock, localOrParameter, expressionStatement, assignmentExpression)) { return; } // Ok, there were no intervening writes or accesses. This check+assignment can be simplified. var allLocations = ImmutableArray.Create( ifOperation.Syntax.GetLocation(), throwOperation.Exception.Syntax.GetLocation(), assignmentExpression.Value.Syntax.GetLocation()); context.ReportDiagnostic( DiagnosticHelper.Create(Descriptor, throwStatementSyntax.GetLocation(), option.Notification.Severity, additionalLocations: allLocations, properties: null)); }
private void AnalyzeBinaryOperator(OperationAnalysisContext context) { var operation = (IBinaryOperation)context.Operation; if (operation.OperatorMethod is not null) { // We shouldn't report diagnostic on overloaded operator as the behavior can change. return; } if ( operation.OperatorKind is not(BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals) ) { return; } if (!_syntaxFacts.IsBinaryExpression(operation.Syntax)) { return; } var rightOperand = operation.RightOperand; var leftOperand = operation.LeftOperand; if (rightOperand.Type is null || leftOperand.Type is null) { return; } if ( rightOperand.Type.SpecialType != SpecialType.System_Boolean || leftOperand.Type.SpecialType != SpecialType.System_Boolean ) { return; } var isOperatorEquals = operation.OperatorKind == BinaryOperatorKind.Equals; _syntaxFacts.GetPartsOfBinaryExpression( operation.Syntax, out _, out var operatorToken, out _ ); var properties = ImmutableDictionary.CreateBuilder <string, string?>(); if (TryGetLiteralValue(rightOperand) == isOperatorEquals) { properties.Add( RedundantEqualityConstants.RedundantSide, RedundantEqualityConstants.Right ); } else if (TryGetLiteralValue(leftOperand) == isOperatorEquals) { properties.Add( RedundantEqualityConstants.RedundantSide, RedundantEqualityConstants.Left ); } if (properties.Count == 1) { context.ReportDiagnostic( Diagnostic.Create( Descriptor, operatorToken.GetLocation(), additionalLocations: new[] { operation.Syntax.GetLocation() }, properties: properties.ToImmutable() ) ); } return;
private void OnOperation(OperationAnalysisContext operationContext) { var invocation = (IInvocationOperation)operationContext.Operation; if (!IsStringFormatMethod(invocation.TargetMethod, out ITypeSymbol returnType, out int formatStringParameterIndex)) { return; } var argument = invocation.Arguments[formatStringParameterIndex].Value; switch (argument.Kind) { case OperationKind.Invocation: if (CheckForNestedStringFormat(operationContext, invocation, (IInvocationOperation)argument)) { return; } break; case OperationKind.InterpolatedString: operationContext.ReportDiagnostic(Diagnostic.Create(NestedRule, argument.Syntax.GetLocation(), "an interpolated string", invocation.TargetMethod.ToDisplayString())); break; } if (returnType.SpecialType == SpecialType.System_String || returnType.SpecialType == SpecialType.System_Void) { var paramsArguments = invocation.Arguments[formatStringParameterIndex + 1].Value; if (paramsArguments is IArrayCreationOperation arrayCreation) { if (arrayCreation.Initializer.ElementValues.IsEmpty && returnType.SpecialType == SpecialType.System_String) { //string format with no arguments operationContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryRule, argument.Syntax.GetLocation())); return; } if (argument.Kind == OperationKind.Literal && argument.Type.SpecialType == SpecialType.System_String) { string formatValue = (string)argument.ConstantValue.Value; if (_formatRegex.IsMatch(formatValue)) { if (arrayCreation.Initializer.ElementValues.Length == 0) { //string format ala string.format("{0}"); operationContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryRule, argument.Syntax.GetLocation())); return; } if (ArrayContainsString(0, arrayCreation)) { //string format ala string.format("{0}", 3); operationContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryRule, argument.Syntax.GetLocation())); return; } } } } else if (paramsArguments is IConversionOperation conversion) { if (argument.Kind == OperationKind.Literal && argument.Type.SpecialType == SpecialType.System_String) { if (((string)argument.ConstantValue.Value) == "{0}" && conversion.Operand.Type.SpecialType == SpecialType.System_String) { operationContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryRule, argument.Syntax.GetLocation())); return; } } } } }
private static void ReportDiagnostic(OperationAnalysisContext context, object messageArg) => context.ReportDiagnostic(Diagnostic.Create(_descriptor, NarrowDownSyntax(context.Operation.Syntax).GetLocation(), messageArg));
private void AnalyzeMethodOverloads(OperationAnalysisContext context, IMethodSymbol method, IHasArgumentsExpression expression) { if (method.MatchMethodDerivedByName(_xmlTypes.XmlDocument, SecurityMemberNames.Load) || //FxCop CA3056 method.MatchMethodDerivedByName(_xmlTypes.XmlDocument, SecurityMemberNames.LoadXml) || //FxCop CA3057 method.MatchMethodDerivedByName(_xmlTypes.XPathDocument, WellKnownMemberNames.InstanceConstructorName) || //FxCop CA3059 method.MatchMethodDerivedByName(_xmlTypes.XmlSchema, SecurityMemberNames.Read) || //FxCop CA3060 method.MatchMethodDerivedByName(_xmlTypes.DataSet, SecurityMemberNames.ReadXml) || //FxCop CA3063 method.MatchMethodDerivedByName(_xmlTypes.DataSet, SecurityMemberNames.ReadXmlSchema) || //FxCop CA3064 method.MatchMethodDerivedByName(_xmlTypes.XmlSerializer, SecurityMemberNames.Deserialize) || //FxCop CA3070 method.MatchMethodDerivedByName(_xmlTypes.DataTable, SecurityMemberNames.ReadXml) || //FxCop CA3071 method.MatchMethodDerivedByName(_xmlTypes.DataTable, SecurityMemberNames.ReadXmlSchema)) //FxCop CA3072 { if (SecurityDiagnosticHelpers.HasXmlReaderParameter(method, _xmlTypes) < 0) { DiagnosticDescriptor rule = RuleDoNotUseInsecureDtdProcessing; context.ReportDiagnostic( Diagnostic.Create( rule, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.DoNotUseDtdProcessingOverloadsMessage), method.Name ) ) ); } } else if (method.MatchMethodDerivedByName(_xmlTypes.XmlReader, SecurityMemberNames.Create)) { int xmlReaderSettingsIndex = SecurityDiagnosticHelpers.GetXmlReaderSettingsParameterIndex(method, _xmlTypes); if (xmlReaderSettingsIndex < 0) { DiagnosticDescriptor rule = RuleDoNotUseInsecureDtdProcessing; Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateWrongOverloadMessage) ) ); context.ReportDiagnostic(diag); } else { SemanticModel model = context.Compilation.GetSemanticModel(context.Operation.Syntax.SyntaxTree); IArgument arg = expression.ArgumentsInParameterOrder[xmlReaderSettingsIndex]; ISymbol settingsSymbol = arg.Value.Syntax.GetDeclaredOrReferencedSymbol(model); if (settingsSymbol == null) { return; } XmlReaderSettingsEnvironment env; if (!_xmlReaderSettingsEnvironments.TryGetValue(settingsSymbol, out env)) { // symbol for settings is not found => passed in without any change => assume insecure Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateInsecureInputMessage) ) ); context.ReportDiagnostic(diag); } else if (!env.IsDtdProcessingDisabled && !(env.IsSecureResolver && env.IsMaxCharactersFromEntitiesLimited)) { Diagnostic diag; if (env.IsConstructedInCodeBlock) { diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateInsecureConstructedMessage) ) ); } else { diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, expression.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlReaderCreateInsecureInputMessage) ) ); } context.ReportDiagnostic(diag); } } } }
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)); } }
private void AnalyzeObjectCreationForXmlDocument(OperationAnalysisContext context, ISymbol variable, IObjectCreationExpression objCreation) { XmlDocumentEnvironment xmlDocumentEnvironment; if (variable == null || !_xmlDocumentEnvironments.ContainsKey(variable)) { xmlDocumentEnvironment = new XmlDocumentEnvironment { IsSecureResolver = false, IsXmlResolverSet = false }; } else { xmlDocumentEnvironment = _xmlDocumentEnvironments[variable]; } xmlDocumentEnvironment.XmlDocumentDefinition = objCreation.Syntax; SyntaxNode node = objCreation.Syntax; bool isXmlDocumentSecureResolver = false; if (objCreation.Constructor.ContainingType != _xmlTypes.XmlDocument) { isXmlDocumentSecureResolver = true; } foreach (ISymbolInitializer init in objCreation.MemberInitializers) { var prop = init as IPropertyInitializer; if (prop != null) { if (prop.InitializedProperty.MatchPropertyDerivedByName(_xmlTypes.XmlDocument, "XmlResolver")) { IConversionExpression operation = prop.Value as IConversionExpression; if (operation == null) { return; } if (SecurityDiagnosticHelpers.IsXmlSecureResolverType(operation.Operand.Type, _xmlTypes)) { isXmlDocumentSecureResolver = true; } else if (SecurityDiagnosticHelpers.IsExpressionEqualsNull(operation.Operand)) { isXmlDocumentSecureResolver = true; } else // Non secure resolvers { IObjectCreationExpression xmlResolverObjCreated = operation.Operand as IObjectCreationExpression; if (xmlResolverObjCreated != null) { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, prop.Syntax.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlDocumentWithNoSecureResolverMessage) ) ); context.ReportDiagnostic(diag); } return; } } else { AnalyzeNeverSetProperties(context, prop.InitializedProperty, prop.Syntax.GetLocation()); } } } xmlDocumentEnvironment.IsSecureResolver = isXmlDocumentSecureResolver; if (variable != null) { _xmlDocumentEnvironments[variable] = xmlDocumentEnvironment; } else if (!xmlDocumentEnvironment.IsSecureResolver) // Insecure temp object { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, node.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlDocumentWithNoSecureResolverMessage) ) ); context.ReportDiagnostic(diag); } }
private void AnalyzeOperation(OperationAnalysisContext context) { var syntaxTree = context.Operation.Syntax.SyntaxTree; if (!IsSupported(syntaxTree.Options)) { return; } var cancellationToken = context.CancellationToken; var throwOperation = (IThrowStatement)context.Operation; var throwStatement = throwOperation.Syntax; var optionSet = context.Options.GetOptionSet(); var option = optionSet.GetOption(CodeStyleOptions.PreferThrowExpression, throwStatement.Language); if (!option.Value) { return; } var compilation = context.Compilation; var semanticModel = compilation.GetSemanticModel(throwStatement.SyntaxTree); var ifOperation = GetContainingIfOperation( semanticModel, throwOperation, cancellationToken); // This throw statement isn't parented by an if-statement. Nothing to // do here. if (ifOperation == null) { return; } var containingBlock = GetOperation( semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement; if (containingBlock == null) { return; } ISymbol localOrParameter; if (!TryDecomposeIfCondition(ifOperation, out localOrParameter)) { return; } IExpressionStatement expressionStatement; IAssignmentExpression assignmentExpression; if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter, out expressionStatement, out assignmentExpression)) { return; } // We found an assignment using this local/parameter. Now, just make sure there // were no intervening writes between the check and the assignement. var dataFlow = semanticModel.AnalyzeDataFlow( ifOperation.Syntax, expressionStatement.Syntax); if (dataFlow.WrittenInside.Contains(localOrParameter)) { return; } // Ok, there were no intervening writes. This check+assignment can be simplified. var allLocations = ImmutableArray.Create( ifOperation.Syntax.GetLocation(), throwOperation.ThrownObject.Syntax.GetLocation(), assignmentExpression.Value.Syntax.GetLocation()); var descriptor = CreateDescriptor(option.Notification.Value); context.ReportDiagnostic( Diagnostic.Create(descriptor, throwStatement.GetLocation(), additionalLocations: allLocations)); // Fade out the rest of the if that surrounds the 'throw' exception. var tokenBeforeThrow = throwStatement.GetFirstToken().GetPreviousToken(); var tokenAfterThrow = throwStatement.GetLastToken().GetNextToken(); context.ReportDiagnostic( Diagnostic.Create(s_unnecessaryCodeDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( ifOperation.Syntax.SpanStart, tokenBeforeThrow.Span.End)), additionalLocations: allLocations)); if (ifOperation.Syntax.Span.End > tokenAfterThrow.Span.Start) { context.ReportDiagnostic( Diagnostic.Create(s_unnecessaryCodeDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( tokenAfterThrow.Span.Start, ifOperation.Syntax.Span.End)), additionalLocations: allLocations)); } }
private void AnalyzeObjectCreationForXmlTextReader(OperationAnalysisContext context, ISymbol variable, IObjectCreationExpression objCreation) { XmlTextReaderEnvironment env; if (variable == null || !_xmlTextReaderEnvironments.TryGetValue(variable, out env)) { env = new XmlTextReaderEnvironment(_isFrameworkSecure) { XmlTextReaderDefinition = objCreation.Syntax }; } if (objCreation.Constructor.ContainingType != _xmlTypes.XmlTextReader) { env.IsDtdProcessingDisabled = true; env.IsSecureResolver = true; } foreach (ISymbolInitializer init in objCreation.MemberInitializers) { var prop = init as IPropertyInitializer; if (prop != null) { IConversionExpression operation = prop.Value as IConversionExpression; if (operation != null && SecurityDiagnosticHelpers.IsXmlTextReaderXmlResolverPropertyDerived(prop.InitializedProperty, _xmlTypes)) { env.IsXmlResolverSet = true; if (SecurityDiagnosticHelpers.IsXmlSecureResolverType(operation.Operand.Type, _xmlTypes)) { env.IsSecureResolver = true; } else if (SecurityDiagnosticHelpers.IsExpressionEqualsNull(operation.Operand)) { env.IsSecureResolver = true; } else { env.IsSecureResolver = false; } } else if (SecurityDiagnosticHelpers.IsXmlTextReaderDtdProcessingPropertyDerived(prop.InitializedProperty, _xmlTypes)) { env.IsDtdProcessingSet = true; env.IsDtdProcessingDisabled = !SecurityDiagnosticHelpers.IsExpressionEqualsDtdProcessingParse(prop.Value); } } } // if the XmlResolver or Dtdprocessing property is explicitly set when created, and is to an insecure value, generate a warning if ((env.IsXmlResolverSet && !env.IsSecureResolver) || (env.IsDtdProcessingSet && !env.IsDtdProcessingDisabled)) { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, env.XmlTextReaderDefinition.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlTextReaderSetInsecureResolutionMessage) ) ); context.ReportDiagnostic(diag); } // if the XmlResolver or Dtdprocessing property is not explicitly set when constructed for a non-temp XmlTextReader object, add env to the dictionary. else if (variable != null && !(env.IsDtdProcessingSet && env.IsXmlResolverSet)) { _xmlTextReaderEnvironments[variable] = env; } // if the is not set or set to Parse for a temporary object, report right now. else if (variable == null && !(env.IsDtdProcessingSet && env.IsXmlResolverSet && env.IsDtdProcessingDisabled && env.IsSecureResolver)) { Diagnostic diag = Diagnostic.Create( RuleDoNotUseInsecureDtdProcessing, env.XmlTextReaderDefinition.GetLocation(), SecurityDiagnosticHelpers.GetLocalizableResourceString( nameof(DesktopAnalyzersResources.XmlTextReaderConstructedWithNoSecureResolutionMessage) ) ); context.ReportDiagnostic(diag); } }
/// <summary>Reports a diagnostic warning for a boxing operation.</summary> /// <param name="context">The context.</param> /// <param name="boxingExpression">The expression that produces the boxing.</param> internal void Report(OperationAnalysisContext context, SyntaxNode boxingExpression) { context.ReportDiagnostic(Diagnostic.Create(BoxingDescriptor, boxingExpression.GetLocation())); }
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 TSimpleNameSyntax simpleName)) { return; } var syntaxTree = context.Operation.Syntax.SyntaxTree; var cancellationToken = context.CancellationToken; var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { return; } var applicableOption = QualifyMembersHelpers.GetApplicableOptionFromSymbolKind(operation); var optionValue = optionSet.GetOption(applicableOption, context.Operation.Syntax.Language); 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)); } }
/// <summary> /// Creates a new instance of the <see cref="ContextualDiagnosticReceiver{T}"/> class that accepts only <see cref="OperationAnalysisContext"/>. /// </summary> /// <param name="context">Context of this <see cref="ContextualDiagnosticReceiver{T}"/>.</param> public static ContextualDiagnosticReceiver <OperationAnalysisContext> Operation(OperationAnalysisContext context) { return(new ContextualDiagnosticReceiver <OperationAnalysisContext>((context, diag) => context.ReportDiagnostic(diag), context)); }
private void AnalyzeOperation(OperationAnalysisContext context) { var syntaxTree = context.Operation.Syntax.SyntaxTree; if (!IsSupported(syntaxTree.Options)) { return; } var cancellationToken = context.CancellationToken; var throwOperation = (IThrowStatement)context.Operation; var throwStatement = throwOperation.Syntax; var options = context.Options; var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { return; } var option = optionSet.GetOption(CodeStyleOptions.PreferThrowExpression, throwStatement.Language); if (!option.Value) { return; } var compilation = context.Compilation; var semanticModel = compilation.GetSemanticModel(throwStatement.SyntaxTree); var ifOperation = GetContainingIfOperation( semanticModel, throwOperation, cancellationToken); // This throw statement isn't parented by an if-statement. Nothing to // do here. if (ifOperation == null) { return; } var containingBlock = GetOperation( semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement; if (containingBlock == null) { return; } if (!TryDecomposeIfCondition(ifOperation, out var localOrParameter)) { return; } if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter, out var expressionStatement, out var assignmentExpression)) { return; } // We found an assignment using this local/parameter. Now, just make sure there // were no intervening accesses between the check and the assignment. var statements = containingBlock.Statements; var ifOperationIndex = statements.IndexOf(ifOperation); var expressionStatementIndex = statements.IndexOf(expressionStatement); if (expressionStatementIndex > ifOperationIndex + 1) { // There are intermediary statements between the check and the assignment. // Make sure they don't try to access the local. var dataFlow = semanticModel.AnalyzeDataFlow( statements[ifOperationIndex + 1].Syntax, statements[expressionStatementIndex - 1].Syntax); if (dataFlow.ReadInside.Contains(localOrParameter) || dataFlow.WrittenInside.Contains(localOrParameter)) { return; } } // Ok, there were no intervening writes or accesses. This check+assignment can be simplified. var allLocations = ImmutableArray.Create( ifOperation.Syntax.GetLocation(), throwOperation.ThrownObject.Syntax.GetLocation(), assignmentExpression.Value.Syntax.GetLocation()); var descriptor = GetDescriptorWithSeverity(option.Notification.Value); context.ReportDiagnostic( Diagnostic.Create(descriptor, throwStatement.GetLocation(), additionalLocations: allLocations)); // Fade out the rest of the if that surrounds the 'throw' exception. var tokenBeforeThrow = throwStatement.GetFirstToken().GetPreviousToken(); var tokenAfterThrow = throwStatement.GetLastToken().GetNextToken(); context.ReportDiagnostic( Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( ifOperation.Syntax.SpanStart, tokenBeforeThrow.Span.End)), additionalLocations: allLocations)); if (ifOperation.Syntax.Span.End > tokenAfterThrow.Span.Start) { context.ReportDiagnostic( Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( tokenAfterThrow.Span.Start, ifOperation.Syntax.Span.End)), additionalLocations: allLocations)); } }
private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol expressionTypeOpt) { var syntaxTree = context.Operation.Syntax.SyntaxTree; if (!IsSupported(syntaxTree.Options)) { return; } var cancellationToken = context.CancellationToken; var throwOperation = (IThrowOperation)context.Operation; var throwStatementSyntax = throwOperation.Syntax; var compilation = context.Compilation; var semanticModel = compilation.GetSemanticModel(throwStatementSyntax.SyntaxTree); var ifOperation = GetContainingIfOperation( semanticModel, throwOperation, cancellationToken); // This throw statement isn't parented by an if-statement. Nothing to // do here. if (ifOperation == null) { return; } if (ifOperation.WhenFalse != null) { // Can't offer this if the 'if-statement' has an 'else-clause'. return; } var options = context.Options; var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { return; } var option = optionSet.GetOption(CodeStyleOptions.PreferThrowExpression, throwStatementSyntax.Language); if (!option.Value) { return; } var semanticFacts = GetSemanticFactsService(); if (semanticFacts.IsInExpressionTree(semanticModel, throwStatementSyntax, expressionTypeOpt, cancellationToken)) { return; } var containingBlock = semanticModel.GetOperation(ifOperation.Syntax.Parent, cancellationToken) as IBlockOperation; if (containingBlock == null) { return; } if (!TryDecomposeIfCondition(ifOperation, out var localOrParameter)) { return; } if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter, out var expressionStatement, out var assignmentExpression)) { return; } // We found an assignment using this local/parameter. Now, just make sure there // were no intervening accesses between the check and the assignment. if (ValueIsAccessed( semanticModel, ifOperation, containingBlock, localOrParameter, expressionStatement, assignmentExpression)) { return; } // Ok, there were no intervening writes or accesses. This check+assignment can be simplified. var allLocations = ImmutableArray.Create( ifOperation.Syntax.GetLocation(), throwOperation.Exception.Syntax.GetLocation(), assignmentExpression.Value.Syntax.GetLocation()); var descriptor = GetDescriptorWithSeverity(option.Notification.Value); context.ReportDiagnostic( Diagnostic.Create(descriptor, throwStatementSyntax.GetLocation(), additionalLocations: allLocations)); // Fade out the rest of the if that surrounds the 'throw' exception. var tokenBeforeThrow = throwStatementSyntax.GetFirstToken().GetPreviousToken(); var tokenAfterThrow = throwStatementSyntax.GetLastToken().GetNextToken(); context.ReportDiagnostic( Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( ifOperation.Syntax.SpanStart, tokenBeforeThrow.Span.End)), additionalLocations: allLocations)); if (ifOperation.Syntax.Span.End > tokenAfterThrow.Span.Start) { context.ReportDiagnostic( Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( tokenAfterThrow.Span.Start, ifOperation.Syntax.Span.End)), additionalLocations: allLocations)); } }
/// <summary>Reports a diagnostic warning for a boxing operation.</summary> /// <param name="context">The context.</param> /// <param name="boxingExpression">The expression that produces the boxing.</param> internal void Report(OperationAnalysisContext context, SyntaxNode boxingExpression) { context.ReportDiagnostic(Diagnostic.Create(BoxingDescriptor, boxingExpression.GetLocation())); }
private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); }
protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation) { if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation, out var actualOperation, out var constraintExpression)) { return; } if (actualOperation == null) { return; } var actualType = AssertHelper.GetUnwrappedActualType(actualOperation); if (actualType == null) { return; } foreach (var constraintPartExpression in constraintExpression.ConstraintParts) { if (constraintPartExpression.HasIncompatiblePrefixes() || HasCustomComparer(constraintPartExpression) || constraintPartExpression.HasUnknownExpressions()) { continue; } var constraintMethod = constraintPartExpression.GetConstraintMethod(); if (constraintMethod == null) { continue; } if (!SupportedConstraints.Contains(constraintMethod.Name)) { continue; } var expectedOperation = constraintPartExpression.GetExpectedArgument(); if (expectedOperation == null) { continue; } var expectedType = expectedOperation.Type; if (expectedType == null) { continue; } if (actualType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { actualType = ((INamedTypeSymbol)actualType).TypeArguments[0]; } if (expectedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { expectedType = ((INamedTypeSymbol)expectedType).TypeArguments[0]; } if (actualType.SpecialType == SpecialType.System_Object || expectedType.SpecialType == SpecialType.System_Object) { // An instance of object might not implement IComparable resulting in runtime errors. context.ReportDiagnostic(Diagnostic.Create( comparableOnObjectDescriptor, expectedOperation.Syntax.GetLocation(), constraintMethod.Name)); } else if (!CanCompare(actualType, expectedType, context.Compilation)) { context.ReportDiagnostic(Diagnostic.Create( comparableTypesDescriptor, expectedOperation.Syntax.GetLocation(), constraintMethod.Name)); } } }
private void AnalyzeOperation(OperationAnalysisContext context) { var syntaxTree = context.Operation.Syntax.SyntaxTree; if (!IsSupported(syntaxTree.Options)) { return; } var cancellationToken = context.CancellationToken; var throwOperation = (IThrowStatement)context.Operation; var throwStatement = throwOperation.Syntax; var options = context.Options; var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { return; } var option = optionSet.GetOption(CodeStyleOptions.PreferThrowExpression, throwStatement.Language); if (!option.Value) { return; } var compilation = context.Compilation; var semanticModel = compilation.GetSemanticModel(throwStatement.SyntaxTree); var ifOperation = GetContainingIfOperation( semanticModel, throwOperation, cancellationToken); // This throw statement isn't parented by an if-statement. Nothing to // do here. if (ifOperation == null) { return; } var containingBlock = GetOperation( semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement; if (containingBlock == null) { return; } if (!TryDecomposeIfCondition(ifOperation, out var localOrParameter)) { return; } if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter, out var expressionStatement, out var assignmentExpression)) { return; } // We found an assignment using this local/parameter. Now, just make sure there // were no intervening accesses between the check and the assignment. var statements = containingBlock.Statements; var ifOperationIndex = statements.IndexOf(ifOperation); var expressionStatementIndex = statements.IndexOf(expressionStatement); if (expressionStatementIndex > ifOperationIndex + 1) { // There are intermediary statements between the check and the assignment. // Make sure they don't try to access the local. var dataFlow = semanticModel.AnalyzeDataFlow( statements[ifOperationIndex + 1].Syntax, statements[expressionStatementIndex - 1].Syntax); if (dataFlow.ReadInside.Contains(localOrParameter) || dataFlow.WrittenInside.Contains(localOrParameter)) { return; } } // Ok, there were no intervening writes or accesses. This check+assignment can be simplified. var allLocations = ImmutableArray.Create( ifOperation.Syntax.GetLocation(), throwOperation.ThrownObject.Syntax.GetLocation(), assignmentExpression.Value.Syntax.GetLocation()); var descriptor = CreateDescriptorWithSeverity(option.Notification.Value); context.ReportDiagnostic( Diagnostic.Create(descriptor, throwStatement.GetLocation(), additionalLocations: allLocations)); // Fade out the rest of the if that surrounds the 'throw' exception. var tokenBeforeThrow = throwStatement.GetFirstToken().GetPreviousToken(); var tokenAfterThrow = throwStatement.GetLastToken().GetNextToken(); context.ReportDiagnostic( Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( ifOperation.Syntax.SpanStart, tokenBeforeThrow.Span.End)), additionalLocations: allLocations)); if (ifOperation.Syntax.Span.End > tokenAfterThrow.Span.Start) { context.ReportDiagnostic( Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( tokenAfterThrow.Span.Start, ifOperation.Syntax.Span.End)), additionalLocations: allLocations)); } }
static void AnalyzeOperation(OperationAnalysisContext context, IMethodSymbol containsKeyMethod, IMethodSymbol remove1Param, IMethodSymbol?remove2Param) { var conditionalOperation = (IConditionalOperation)context.Operation; IInvocationOperation?invocationOperation = null; switch (conditionalOperation.Condition) { case IInvocationOperation iOperation: invocationOperation = iOperation; break; case IUnaryOperation unaryOperation when unaryOperation.OperatorKind == UnaryOperatorKind.Not: if (unaryOperation.Operand is IInvocationOperation operand) { invocationOperation = operand; } break; case IParenthesizedOperation parenthesizedOperation: if (parenthesizedOperation.Operand is IInvocationOperation invocation) { invocationOperation = invocation; } break; default: return; } if (invocationOperation == null || !invocationOperation.TargetMethod.OriginalDefinition.Equals(containsKeyMethod, SymbolEqualityComparer.Default)) { return; } if (conditionalOperation.WhenTrue.Children.Any()) { using var additionalLocation = ArrayBuilder <Location> .GetInstance(2); additionalLocation.Add(conditionalOperation.Syntax.GetLocation()); switch (conditionalOperation.WhenTrue.Children.First()) { case IInvocationOperation childInvocationOperation: if (childInvocationOperation.TargetMethod.OriginalDefinition.Equals(remove1Param, SymbolEqualityComparer.Default) || childInvocationOperation.TargetMethod.OriginalDefinition.Equals(remove2Param, SymbolEqualityComparer.Default)) { additionalLocation.Add(childInvocationOperation.Syntax.Parent.GetLocation()); context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule, additionalLocations: additionalLocation.ToImmutable(), null)); } break; case IExpressionStatementOperation childStatementOperation: /* * If the if statement contains a block, only proceed if one of the methods calls Remove. * However, a fixer is only offered if there is a single method in the block. */ var nestedInvocationOperation = childStatementOperation.Children.OfType <IInvocationOperation>() .FirstOrDefault(op => op.TargetMethod.OriginalDefinition.Equals(remove1Param, SymbolEqualityComparer.Default) || op.TargetMethod.OriginalDefinition.Equals(remove2Param, SymbolEqualityComparer.Default)); if (nestedInvocationOperation != null) { additionalLocation.Add(nestedInvocationOperation.Syntax.Parent.GetLocation()); context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule, additionalLocations: additionalLocation.ToImmutable(), null)); } break; default: break; } } }
private static void AnalyzeObjectCreation(OperationAnalysisContext context, ISymbol owningSymbol) { var arrayCreationExpression = (IArrayCreationExpression)context.Operation; if (IsMultiDimensionalArray(arrayCreationExpression.Type)) { context.ReportDiagnostic(arrayCreationExpression.Syntax.CreateDiagnostic(BodyRule, owningSymbol.Name, arrayCreationExpression.Type)); } }
private void AnalyzeOperation(OperationAnalysisContext context) { var syntaxTree = context.Operation.Syntax.SyntaxTree; if (!IsSupported(syntaxTree.Options)) { return; } var cancellationToken = context.CancellationToken; var throwOperation = (IThrowStatement)context.Operation; var throwStatement = throwOperation.Syntax; var optionSet = context.Options.GetOptionSet(); var option = optionSet.GetOption(CodeStyleOptions.PreferThrowExpression, throwStatement.Language); if (!option.Value) { return; } var compilation = context.Compilation; var semanticModel = compilation.GetSemanticModel(throwStatement.SyntaxTree); var ifOperation = GetContainingIfOperation( semanticModel, throwOperation, cancellationToken); // This throw statement isn't parented by an if-statement. Nothing to // do here. if (ifOperation == null) { return; } var containingBlock = GetOperation( semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement; if (containingBlock == null) { return; } ISymbol localOrParameter; if (!TryDecomposeIfCondition(ifOperation, out localOrParameter)) { return; } IExpressionStatement expressionStatement; IAssignmentExpression assignmentExpression; if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter, out expressionStatement, out assignmentExpression)) { return; } // We found an assignment using this local/parameter. Now, just make sure there // were no intervening writes between the check and the assignement. var dataFlow = semanticModel.AnalyzeDataFlow( ifOperation.Syntax, expressionStatement.Syntax); if (dataFlow.WrittenInside.Contains(localOrParameter)) { return; } // Ok, there were no intervening writes. This check+assignment can be simplified. var allLocations = ImmutableArray.Create( ifOperation.Syntax.GetLocation(), throwOperation.ThrownObject.Syntax.GetLocation(), assignmentExpression.Value.Syntax.GetLocation()); var descriptor = CreateDescriptor(option.Notification.Value); context.ReportDiagnostic( Diagnostic.Create(descriptor, throwStatement.GetLocation(), additionalLocations: allLocations)); // Fade out the rest of the if that surrounds the 'throw' exception. var tokenBeforeThrow = throwStatement.GetFirstToken().GetPreviousToken(); var tokenAfterThrow = throwStatement.GetLastToken().GetNextToken(); context.ReportDiagnostic( Diagnostic.Create(s_unnecessaryCodeDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( ifOperation.Syntax.SpanStart, tokenBeforeThrow.Span.End)), additionalLocations: allLocations)); if (ifOperation.Syntax.Span.End > tokenAfterThrow.Span.Start) { context.ReportDiagnostic( Diagnostic.Create(s_unnecessaryCodeDescriptor, Location.Create(syntaxTree, TextSpan.FromBounds( tokenAfterThrow.Span.Start, ifOperation.Syntax.Span.End)), additionalLocations: allLocations)); } }