private static void OnCompilationStart(CompilationStartAnalysisContext context) { // To avoid false positives, as with CA1812 (avoid uninstantiated internal classes), skip any assemblies with InternalsVisibleTo. var internalsVisibleToAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesInternalsVisibleToAttribute); if (context.Compilation.Assembly.HasAttribute(internalsVisibleToAttributeSymbol)) { return; } INamedTypeSymbol?comImportAttributeType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesComImportAttribute); var candidateTypes = PooledConcurrentSet <INamedTypeSymbol> .GetInstance(SymbolEqualityComparer.Default); var baseTypes = PooledConcurrentSet <INamedTypeSymbol> .GetInstance(SymbolEqualityComparer.Default); context.RegisterSymbolAction(context => { var type = (INamedTypeSymbol)context.Symbol; if (type.TypeKind is TypeKind.Class && !type.IsAbstract && !type.IsStatic && !type.IsSealed && !type.IsExternallyVisible() && !type.HasAttribute(comImportAttributeType)) { candidateTypes.Add(type); } for (INamedTypeSymbol?baseType = type.BaseType; baseType is not null; baseType = baseType.BaseType) { baseTypes.Add(baseType.OriginalDefinition); } }, SymbolKind.NamedType); context.RegisterCompilationEndAction(context => { foreach (INamedTypeSymbol type in candidateTypes) { if (!baseTypes.Contains(type.OriginalDefinition)) { context.ReportDiagnostic(type.CreateDiagnostic(Rule, type.Name)); } } candidateTypes.Dispose(); baseTypes.Dispose(); }); }
private static void OnCompilationStart(CompilationStartAnalysisContext context) { if (!RequiredSymbols.TryGetSymbols(context.Compilation, out RequiredSymbols symbols)) { return; } context.RegisterOperationBlockStartAction(OnOperationBlockStart); return; // Local functions void OnOperationBlockStart(OperationBlockStartAnalysisContext context) { var invocations = PooledConcurrentSet <IInvocationOperation> .GetInstance(); context.RegisterOperationAction(context => { var argument = (IArgumentOperation)context.Operation; if (symbols.IsAnySubstringInvocation(argument.Value.WalkDownConversion(c => c.IsImplicit)) && argument.Parent is IInvocationOperation invocation) { invocations.Add(invocation); } }, OperationKind.Argument); context.RegisterOperationBlockEndAction(context => { foreach (var invocation in invocations) { // We search for an overload of the invoked member whose signature matches the signature of // the invoked member, except with ReadOnlySpan<char> substituted in for some of the // arguments that are Substring invocations. if (!GetBestSpanBasedOverloads(symbols, invocation, context.CancellationToken).IsEmpty) { Diagnostic diagnostic = invocation.CreateDiagnostic(Rule); context.ReportDiagnostic(diagnostic); } } invocations.Free(context.CancellationToken); }); } }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { INamedTypeSymbol?diagnosticDescriptorType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticDescriptor); if (diagnosticDescriptorType == null) { return; } // Try read the additional file containing the allowed categories, and corresponding ID ranges. var checkCategoryAndAllowedIds = TryGetCategoryAndAllowedIdsMap( compilationContext.Options.AdditionalFiles, compilationContext.CancellationToken, out AdditionalText? diagnosticCategoryAndIdRangeTextOpt, out ImmutableDictionary <string, ImmutableArray <(string?prefix, int start, int end)> >?categoryAndAllowedIdsMap, out List <Diagnostic>?invalidFileDiagnostics); // Try read the additional files containing the shipped and unshipped analyzer releases. var isAnalyzerReleaseTracking = TryGetReleaseTrackingData( compilationContext.Options.AdditionalFiles, compilationContext.CancellationToken, out var shippedData, out var unshippedData, out List <Diagnostic>?invalidReleaseFileEntryDiagnostics); var idToAnalyzerMap = new ConcurrentDictionary <string, ConcurrentDictionary <string, ConcurrentBag <Location> > >(); var seenRuleIds = PooledConcurrentSet <string> .GetInstance(); compilationContext.RegisterOperationAction(operationAnalysisContext => { var fieldInitializer = (IFieldInitializerOperation)operationAnalysisContext.Operation; if (!TryGetDescriptorCreateMethodAndArguments(fieldInitializer, diagnosticDescriptorType, out var creationMethod, out var creationArguments)) { return; } AnalyzeTitle(creationMethod, fieldInitializer, operationAnalysisContext.ReportDiagnostic); AnalyzeHelpLinkUri(operationAnalysisContext, creationArguments, out var helpLink); AnalyzeCustomTags(operationAnalysisContext, creationArguments); var(isEnabledByDefault, defaultSeverity) = GetDefaultSeverityAndEnabledByDefault(operationAnalysisContext.Compilation, creationArguments); if (!TryAnalyzeCategory(operationAnalysisContext, creationArguments, checkCategoryAndAllowedIds, diagnosticCategoryAndIdRangeTextOpt, categoryAndAllowedIdsMap, out var category, out var allowedIdsInfoList)) { allowedIdsInfoList = default; } var analyzerName = fieldInitializer.InitializedFields.First().ContainingType.Name; AnalyzeRuleId(operationAnalysisContext, creationArguments, isAnalyzerReleaseTracking, shippedData, unshippedData, seenRuleIds, diagnosticCategoryAndIdRangeTextOpt, category, analyzerName, helpLink, isEnabledByDefault, defaultSeverity, allowedIdsInfoList, idToAnalyzerMap); }, OperationKind.FieldInitializer); compilationContext.RegisterCompilationEndAction(compilationEndContext => { // Report any invalid additional file diagnostics. if (invalidFileDiagnostics != null) { foreach (var diagnostic in invalidFileDiagnostics) { compilationEndContext.ReportDiagnostic(diagnostic); } } // Report diagnostics for duplicate diagnostic ID used across analyzers. foreach (var kvp in idToAnalyzerMap) { var ruleId = kvp.Key; var analyzerToDescriptorLocationsMap = kvp.Value; if (analyzerToDescriptorLocationsMap.Count <= 1) { // ID used by a single analyzer. continue; } ImmutableSortedSet <string> sortedAnalyzerNames = analyzerToDescriptorLocationsMap.Keys.ToImmutableSortedSet(); var skippedAnalyzerName = sortedAnalyzerNames[0]; foreach (var analyzerName in sortedAnalyzerNames.Skip(1)) { var locations = analyzerToDescriptorLocationsMap[analyzerName]; foreach (var location in locations) { // Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. var diagnostic = Diagnostic.Create(UseUniqueDiagnosticIdRule, location, ruleId, skippedAnalyzerName); compilationEndContext.ReportDiagnostic(diagnostic); } } } // Report analyzer release tracking invalid entry and compilation end diagnostics. if (isAnalyzerReleaseTracking || invalidReleaseFileEntryDiagnostics != null) { RoslynDebug.Assert(shippedData != null); RoslynDebug.Assert(unshippedData != null); ReportAnalyzerReleaseTrackingDiagnostics(invalidReleaseFileEntryDiagnostics, shippedData, unshippedData, seenRuleIds, compilationEndContext); } seenRuleIds.Free(); }); }); }
public sealed override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterSymbolStartAction(visitEnumSymbol, SymbolKind.NamedType); void visitEnumSymbol(SymbolStartAnalysisContext context) { if (context.Symbol is not INamedTypeSymbol { TypeKind : TypeKind.Enum } enumSymbol) { return; } // This dictionary is populated by this thread and then read concurrently. // https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-5.0#thread-safety var membersByValue = PooledDictionary <object, IFieldSymbol> .GetInstance(); var duplicates = PooledConcurrentSet <IFieldSymbol> .GetInstance(SymbolEqualityComparer.Default); foreach (var member in enumSymbol.GetMembers()) { if (member is not IFieldSymbol { IsImplicitlyDeclared: false, HasConstantValue: true } field) { continue; } var constantValue = field.ConstantValue; if (membersByValue.ContainsKey(constantValue)) { // This field is a duplicate. We need to first check // if its initializer is another field on this enum, // and if not give a diagnostic on it. var added = duplicates.Add(field); Debug.Assert(added); } else { membersByValue[constantValue] = field; } } context.RegisterOperationAction(visitFieldInitializer, OperationKind.FieldInitializer); context.RegisterSymbolEndAction(endVisitEnumSymbol); void visitFieldInitializer(OperationAnalysisContext context) { var initializer = (IFieldInitializerOperation)context.Operation; if (initializer.InitializedFields.Length != 1) { return; } var field = initializer.InitializedFields[0]; if (duplicates.Remove(field)) { var duplicatedField = membersByValue[field.ConstantValue]; if (initializer.Value is not IConversionOperation { Operand : IFieldReferenceOperation { Field : IFieldSymbol referencedField } } || !SymbolEqualityComparer.Default.Equals(referencedField, duplicatedField)) { context.ReportDiagnostic(field.CreateDiagnostic(RuleDuplicatedValue, field.Name, field.ConstantValue, duplicatedField.Name)); } } // Check for duplicate usages of an enum field in an initializer consisting of '|' expressions var referencedSymbols = PooledHashSet <IFieldSymbol> .GetInstance(SymbolEqualityComparer.Default); var containingType = field.ContainingType; visitInitializerValue(initializer.Value); referencedSymbols.Free(context.CancellationToken); void visitInitializerValue(IOperation operation) { switch (operation) { case IBinaryOperation { OperatorKind: not BinaryOperatorKind.Or } : // only descend into '|' binary operators, not into '&', '+', ... break;
static void OnSymbolStart( SymbolStartAnalysisContext symbolStartContext, WellKnownTypeProvider wellKnownTypeProvider, ImmutableArray <INamedTypeSymbol> skippedAttributes, bool isWebProject) { // Since property/event accessors cannot be marked static themselves and the associated symbol (property/event) // has to be marked static, we want to report the diagnostic on the property/event. // So we make a note of the property/event symbols which have at least one accessor with no instance access. // At symbol end, we report candidate property/event symbols whose all accessors are candidates to be marked static. var propertyOrEventCandidates = PooledConcurrentSet <ISymbol> .GetInstance(); var accessorCandidates = PooledConcurrentSet <IMethodSymbol> .GetInstance(); var methodCandidates = PooledConcurrentSet <IMethodSymbol> .GetInstance(); // Do not flag methods that are used as delegates: https://github.com/dotnet/roslyn-analyzers/issues/1511 var methodsUsedAsDelegates = PooledConcurrentSet <IMethodSymbol> .GetInstance(); symbolStartContext.RegisterOperationAction(OnMethodReference, OperationKind.MethodReference); symbolStartContext.RegisterOperationBlockStartAction(OnOperationBlockStart); symbolStartContext.RegisterSymbolEndAction(OnSymbolEnd); return; void OnMethodReference(OperationAnalysisContext operationContext) { var methodReference = (IMethodReferenceOperation)operationContext.Operation; methodsUsedAsDelegates.Add(methodReference.Method); } void OnOperationBlockStart(OperationBlockStartAnalysisContext blockStartContext) { #pragma warning disable IDE0083 // Use pattern matching - need new compiler if (!(blockStartContext.OwningSymbol is IMethodSymbol methodSymbol)) #pragma warning restore IDE0083 // Use pattern matching { return; } // Don't run any other check for this method if it isn't a valid analysis context if (!ShouldAnalyze(methodSymbol, wellKnownTypeProvider, skippedAttributes, blockStartContext.Options, isWebProject, blockStartContext.CancellationToken)) { return; } // Don't report methods which have a single throw statement // with NotImplementedException or NotSupportedException if (blockStartContext.IsMethodNotImplementedOrSupported()) { return; } bool isInstanceReferenced = false; blockStartContext.RegisterOperationAction(operationContext => { if (((IInstanceReferenceOperation)operationContext.Operation).ReferenceKind == InstanceReferenceKind.ContainingTypeInstance) { isInstanceReferenced = true; } }, OperationKind.InstanceReference); blockStartContext.RegisterOperationBlockEndAction(blockEndContext => { if (!isInstanceReferenced) { if (methodSymbol.IsAccessorMethod()) { accessorCandidates.Add(methodSymbol); propertyOrEventCandidates.Add(methodSymbol.AssociatedSymbol); } else if (methodSymbol.IsExternallyVisible()) { if (!IsOnObsoleteMemberChain(methodSymbol, wellKnownTypeProvider)) { blockEndContext.ReportDiagnostic(methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name)); } } else { methodCandidates.Add(methodSymbol); } } }); } void OnSymbolEnd(SymbolAnalysisContext symbolEndContext) { foreach (var candidate in methodCandidates) { if (methodsUsedAsDelegates.Contains(candidate)) { continue; } if (!IsOnObsoleteMemberChain(candidate, wellKnownTypeProvider)) { symbolEndContext.ReportDiagnostic(candidate.CreateDiagnostic(Rule, candidate.Name)); } } foreach (var candidatePropertyOrEvent in propertyOrEventCandidates) { var allAccessorsAreCandidates = true; foreach (var accessor in candidatePropertyOrEvent.GetAccessors()) { if (!accessorCandidates.Contains(accessor) || IsOnObsoleteMemberChain(accessor, wellKnownTypeProvider)) { allAccessorsAreCandidates = false; break; } } if (allAccessorsAreCandidates) { symbolEndContext.ReportDiagnostic(candidatePropertyOrEvent.CreateDiagnostic(Rule, candidatePropertyOrEvent.Name)); } } propertyOrEventCandidates.Free(symbolEndContext.CancellationToken); accessorCandidates.Free(symbolEndContext.CancellationToken); methodCandidates.Free(symbolEndContext.CancellationToken); methodsUsedAsDelegates.Free(symbolEndContext.CancellationToken); } }
private void OnCompilationStart(CompilationStartAnalysisContext context) { if (!RequiredSymbols.TryGetSymbols(context.Compilation, out RequiredSymbols symbols)) { return; } context.RegisterOperationBlockStartAction(OnOperationBlockStart); return; // Local functions void OnOperationBlockStart(OperationBlockStartAnalysisContext context) { // Maintain set of all top-most concat operations so we don't report sub-expressions of an // already-reported violation. // We also don't report any diagnostic if the concat operation has too many operands for the span-based // Concat overloads to handle. var topMostConcatOperations = PooledConcurrentSet <IBinaryOperation> .GetInstance(); context.RegisterOperationAction(PopulateTopMostConcatOperations, OperationKind.Binary); context.RegisterOperationBlockEndAction(ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls); void PopulateTopMostConcatOperations(OperationAnalysisContext context) { // If the current operation is a string-concatenation operation, walk up to the top-most concat // operation and add it to the set. var binary = (IBinaryOperation)context.Operation; if (!TryGetTopMostConcatOperation(binary, out var topMostConcatOperation)) { return; } topMostConcatOperations.Add(topMostConcatOperation); } void ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls(OperationBlockAnalysisContext context) { // We report diagnostics for all top-most concat operations that contain // direct or conditional substring invocations when there is an applicable span-based overload of // the string.Concat method. // We don't report when the concatenation contains anything other than strings or character literals. foreach (var operation in topMostConcatOperations) { if (ShouldBeReported(operation)) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); } } topMostConcatOperations.Free(context.CancellationToken); } } bool ShouldBeReported(IBinaryOperation topMostConcatOperation) { var concatOperands = FlattenBinaryOperation(topMostConcatOperation); // Bail if no suitable overload of 'string.Concat' exists. if (!symbols.TryGetRoscharConcatMethodWithArity(concatOperands.Length, out _)) { return(false); } bool anySubstringInvocations = false; foreach (var operand in concatOperands) { var value = WalkDownBuiltInImplicitConversionOnConcatOperand(operand); switch (value.Type?.SpecialType) { // Report diagnostics only when operands are exclusively strings and character literals. case SpecialType.System_String: case SpecialType.System_Char when value is ILiteralOperation: if (IsAnyDirectOrConditionalSubstringInvocation(value)) { anySubstringInvocations = true; } break; default: return(false); } } return(anySubstringInvocations); } bool IsAnyDirectOrConditionalSubstringInvocation(IOperation operation) { if (operation is IConditionalAccessOperation conditionallAccessOperation) { operation = conditionallAccessOperation.WhenNotNull; } return(operation is IInvocationOperation invocation && symbols.IsAnySubstringMethod(invocation.TargetMethod)); } }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(context => { if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemString, out INamedTypeSymbol? stringType) || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemChar, out INamedTypeSymbol? charType) || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemStringComparison, out INamedTypeSymbol? stringComparisonType)) { return; } // First get all the string.IndexOf methods that we are interested in tagging var stringIndexOfMethods = stringType .GetMembers("IndexOf") .OfType <IMethodSymbol>() .WhereAsArray(s => s.Parameters.Length <= 2); var stringArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(stringType)); var charArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType)); var stringAndComparisonTypeArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(stringType), ParameterInfo.GetParameterInfo(stringComparisonType)); var charAndComparisonTypeArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType), ParameterInfo.GetParameterInfo(stringComparisonType)); // Check that the contains methods that take 2 parameters exist // string.Contains(char) is also .NETStandard2.1+ var stringContainsMethods = stringType .GetMembers("Contains") .OfType <IMethodSymbol>() .WhereAsArray(s => s.Parameters.Length <= 2); var stringAndComparisonTypeArgumentContainsMethod = stringContainsMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(stringType), ParameterInfo.GetParameterInfo(stringComparisonType)); var charAndComparisonTypeArgumentContainsMethod = stringContainsMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType), ParameterInfo.GetParameterInfo(stringComparisonType)); var charArgumentContainsMethod = stringContainsMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType)); if (stringAndComparisonTypeArgumentContainsMethod == null || charAndComparisonTypeArgumentContainsMethod == null || charArgumentContainsMethod == null) { return; } // Roslyn doesn't yet support "FindAllReferences" at a file/block level. So instead, find references to local int variables in this block. context.RegisterOperationBlockStartAction(OnOperationBlockStart); return; void OnOperationBlockStart(OperationBlockStartAnalysisContext context) { if (context.OwningSymbol is not IMethodSymbol method) { return; } // Algorithm: // We aim to change string.IndexOf -> string.Contains // 1. We register 1 callback for invocations of IndexOf. // 1a. Check if invocation.Parent is a binary operation we care about (string.IndexOf >= 0 OR string.IndexOf == -1). If so, report a diagnostic and return from the callback. // 1b. Otherwise, check if invocation.Parent is a variable declarator. If so, add the invocation as a potential violation to track into variableNameToOperationsMap. // 2. We register another callback for local references // 2a. If the local reference is not a type int, bail out. // 2b. If the local reference operation's parent is not a binary operation, add it to "localsToBailOut". // 3. In an operation block end, we check if entries in "variableNameToOperationsMap" exist in "localToBailOut". If an entry is NOT present, we report a diagnostic at that invocation. PooledConcurrentSet <ILocalSymbol> localsToBailOut = PooledConcurrentSet <ILocalSymbol> .GetInstance(); PooledConcurrentDictionary <ILocalSymbol, IInvocationOperation> variableNameToOperationsMap = PooledConcurrentDictionary <ILocalSymbol, IInvocationOperation> .GetInstance(); context.RegisterOperationAction(PopulateLocalReferencesSet, OperationKind.LocalReference); context.RegisterOperationAction(AnalyzeInvocationOperation, OperationKind.Invocation); context.RegisterOperationBlockEndAction(OnOperationBlockEnd); return; // Local Functions void PopulateLocalReferencesSet(OperationAnalysisContext context) { ILocalReferenceOperation localReference = (ILocalReferenceOperation)context.Operation; if (localReference.Local.Type.SpecialType != SpecialType.System_Int32) { return; } var parent = localReference.Parent; if (parent is IBinaryOperation binaryOperation) { var otherOperand = binaryOperation.LeftOperand is ILocalReferenceOperation ? binaryOperation.RightOperand : binaryOperation.LeftOperand; if (CheckOperatorKindAndOperand(binaryOperation, otherOperand)) { // Do nothing. This is a valid case to the tagged in the analyzer return; } } localsToBailOut.Add(localReference.Local); } void AnalyzeInvocationOperation(OperationAnalysisContext context) { var invocationOperation = (IInvocationOperation)context.Operation; if (!IsDesiredTargetMethod(invocationOperation.TargetMethod)) { return; } var parent = invocationOperation.Parent; if (parent is IBinaryOperation binaryOperation) { var otherOperand = binaryOperation.LeftOperand is IInvocationOperation ? binaryOperation.RightOperand : binaryOperation.LeftOperand; if (CheckOperatorKindAndOperand(binaryOperation, otherOperand)) { context.ReportDiagnostic(binaryOperation.CreateDiagnostic(Rule)); } } else if (parent is IVariableInitializerOperation variableInitializer) { if (variableInitializer.Parent is IVariableDeclaratorOperation variableDeclaratorOperation) { variableNameToOperationsMap.TryAdd(variableDeclaratorOperation.Symbol, invocationOperation); } else if (variableInitializer.Parent is IVariableDeclarationOperation variableDeclarationOperation && variableDeclarationOperation.Declarators.Length == 1) { variableNameToOperationsMap.TryAdd(variableDeclarationOperation.Declarators[0].Symbol, invocationOperation); } } } static bool CheckOperatorKindAndOperand(IBinaryOperation binaryOperation, IOperation otherOperand) { var operatorKind = binaryOperation.OperatorKind; if (otherOperand.ConstantValue.HasValue && otherOperand.ConstantValue.Value is int intValue) { if ((operatorKind == BinaryOperatorKind.Equals && intValue < 0) || (operatorKind == BinaryOperatorKind.GreaterThanOrEqual && intValue == 0)) { // This is the only case we are targeting in this analyzer return(true); } } return(false); } void OnOperationBlockEnd(OperationBlockAnalysisContext context) { foreach (var variableNameAndLocation in variableNameToOperationsMap) { ILocalSymbol variable = variableNameAndLocation.Key; if (!localsToBailOut.Contains(variable)) { context.ReportDiagnostic(variableNameAndLocation.Value.CreateDiagnostic(Rule)); } } variableNameToOperationsMap.Free(context.CancellationToken); localsToBailOut.Free(context.CancellationToken); } bool IsDesiredTargetMethod(IMethodSymbol targetMethod) => targetMethod.Equals(stringArgumentIndexOfMethod) || targetMethod.Equals(charArgumentIndexOfMethod) || targetMethod.Equals(stringAndComparisonTypeArgumentIndexOfMethod) || targetMethod.Equals(charAndComparisonTypeArgumentIndexOfMethod); } });