diff --git a/global.json b/global.json index 05d0ae3d..8afc0f0d 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "3.1.201" + "version": "3.1.404" } } diff --git a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs index 4bd4e327..f3dc659d 100644 --- a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs +++ b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs @@ -66,6 +66,12 @@ protected override IEnumerable FindInvocations(SyntaxNodeAnalysisCon foreach (var invocationExpressionSyntax in body.DescendantNodes().Where(node => node.IsKind(SyntaxKind.SimpleMemberAccessExpression) || node.IsKind(SyntaxKind.ElementAccessExpression))) { + var parentNode = invocationExpressionSyntax.Parent; + if (parentNode.IsKind(SyntaxKind.ElementAccessExpression)) + { + continue; + } + yield return invocationExpressionSyntax; } } diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoAnalyzer.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoAnalyzer.cs index 5f383e29..bca4a77c 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoAnalyzer.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoAnalyzer.cs @@ -115,7 +115,7 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext) foreach (var argumentExpressionSyntax in invocationOperation.GetOrderedArgumentOperationsWithoutInstanceArgument()) { - var callInfoContext = _callInfoFinder.GetCallInfoContext(syntaxNodeContext.SemanticModel, argumentExpressionSyntax.Value.Syntax); + var callInfoContext = _callInfoFinder.GetCallInfoContext(syntaxNodeContext.SemanticModel, argumentExpressionSyntax); AnalyzeArgAtInvocations(syntaxNodeContext, callInfoContext, substituteCallParameters); diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoFinder.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoFinder.cs index 7830f497..7a9ae4e6 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoFinder.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoFinder.cs @@ -2,23 +2,23 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; +using NSubstitute.Analyzers.Shared.Extensions; namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers { internal abstract class AbstractCallInfoFinder : ICallInfoFinder { - public CallInfoContext GetCallInfoContext( - SemanticModel semanticModel, SyntaxNode syntaxNode) + public CallInfoContext GetCallInfoContext(SemanticModel semanticModel, IArgumentOperation argumentOperation) { - var callInfoParameterSymbol = GetCallInfoParameterSymbol(semanticModel, syntaxNode); - if (callInfoParameterSymbol == null) + var callContext = CallInfoContext.Empty; + foreach (var syntaxNode in argumentOperation.GetSyntaxes()) { - return CallInfoContext.Empty; + var callInfoParameterSymbol = GetCallInfoParameterSymbol(semanticModel, syntaxNode); + var currentContext = GetCallInfoContextInternal(semanticModel, syntaxNode); + callContext = callContext.Merge(CreateFilteredCallInfoContext(semanticModel, currentContext, callInfoParameterSymbol)); } - var callContext = GetCallInfoContextInternal(semanticModel, syntaxNode); - - return CreateFilteredCallInfoContext(semanticModel, callContext, callInfoParameterSymbol); + return callContext; } protected abstract CallInfoContext GetCallInfoContextInternal(SemanticModel semanticModel, SyntaxNode syntaxNode); diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractConflictingArgumentAssignmentsAnalyzer.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractConflictingArgumentAssignmentsAnalyzer.cs index 7e4b8226..663d7648 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractConflictingArgumentAssignmentsAnalyzer.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractConflictingArgumentAssignmentsAnalyzer.cs @@ -89,7 +89,7 @@ private IEnumerable FindCallInfoIndexers(SyntaxNodeAnalysisContext s // perf - dont use linq in hotpaths foreach (var argumentOperation in invocationOperation.GetOrderedArgumentOperationsWithoutInstanceArgument()) { - foreach (var indexerExpressionSyntax in _callInfoFinder.GetCallInfoContext(syntaxNodeContext.SemanticModel, argumentOperation.Value.Syntax).IndexerAccesses) + foreach (var indexerExpressionSyntax in _callInfoFinder.GetCallInfoContext(syntaxNodeContext.SemanticModel, argumentOperation).IndexerAccesses) { if (IsAssigned(syntaxNodeContext, indexerExpressionSyntax)) { diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractNonSubstitutableMemberReceivedInOrderAnalyzer.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractNonSubstitutableMemberReceivedInOrderAnalyzer.cs index 1c623873..a85810b4 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractNonSubstitutableMemberReceivedInOrderAnalyzer.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractNonSubstitutableMemberReceivedInOrderAnalyzer.cs @@ -29,11 +29,11 @@ protected AbstractNonSubstitutableMemberReceivedInOrderAnalyzer( : base(diagnosticDescriptorsProvider, nonSubstitutableMemberAnalysis) { _substitutionNodeFinder = substitutionNodeFinder; + _analyzeInvocationAction = AnalyzeInvocation; + NonVirtualSetupDescriptor = diagnosticDescriptorsProvider.NonVirtualReceivedInOrderSetupSpecification; SupportedDiagnostics = ImmutableArray.Create( DiagnosticDescriptorsProvider.InternalSetupSpecification, DiagnosticDescriptorsProvider.NonVirtualReceivedInOrderSetupSpecification); - _analyzeInvocationAction = AnalyzeInvocation; - NonVirtualSetupDescriptor = diagnosticDescriptorsProvider.NonVirtualReceivedInOrderSetupSpecification; } protected override DiagnosticDescriptor NonVirtualSetupDescriptor { get; } diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/CallInfoContext.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/CallInfoContext.cs index 4ff6ee9c..7487d18c 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/CallInfoContext.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/CallInfoContext.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis; namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers @@ -26,5 +27,13 @@ public CallInfoContext( ArgAtInvocations = argAtInvocations; ArgInvocations = argInvocations; } + + public CallInfoContext Merge(CallInfoContext callInfoContext) + { + return new CallInfoContext( + ArgAtInvocations.Concat(callInfoContext.ArgAtInvocations).ToList(), + ArgInvocations.Concat(callInfoContext.ArgInvocations).ToList(), + IndexerAccesses.Concat(callInfoContext.IndexerAccesses).ToList()); + } } } \ No newline at end of file diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/ICallInfoFinder.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/ICallInfoFinder.cs index 0bc492ff..ea6e6007 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/ICallInfoFinder.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/ICallInfoFinder.cs @@ -1,9 +1,10 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Operations; namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers { internal interface ICallInfoFinder { - CallInfoContext GetCallInfoContext(SemanticModel semanticModel, SyntaxNode syntaxNode); + CallInfoContext GetCallInfoContext(SemanticModel semanticModel, IArgumentOperation argumentOperation); } } \ No newline at end of file diff --git a/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs b/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs index ad09b82f..47f2551b 100644 --- a/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs +++ b/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; @@ -109,6 +110,23 @@ public static IOperation GetSubstituteOperation(this IInvocationOperation invoca return (int)literal.ConstantValue.Value; } + public static IEnumerable GetSyntaxes(this IArgumentOperation argumentOperation) + { + if (argumentOperation.Parameter.IsParams) + { + var initializerElementValues = (argumentOperation.Value as IArrayCreationOperation)?.Initializer.ElementValues; + + foreach (var operation in initializerElementValues ?? Enumerable.Empty()) + { + yield return operation.Syntax; + } + + yield break; + } + + yield return argumentOperation.Value.Syntax; + } + private static bool IsImplicitlyProvidedArrayWithoutValues(IArgumentOperation arg) { return arg.IsImplicit &&