-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' of github.com:nsubstitute/NSubstitute.Analyzers into
GH-153-operation-api
- Loading branch information
Showing
30 changed files
with
3,084 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/NonSubstitutableMemberAnalysis.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers; | ||
|
||
namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers | ||
{ | ||
internal class NonSubstitutableMemberAnalysis : AbstractNonSubstitutableMemberAnalysis | ||
{ | ||
public static NonSubstitutableMemberAnalysis Instance { get; } = new NonSubstitutableMemberAnalysis(); | ||
|
||
private NonSubstitutableMemberAnalysis() | ||
{ | ||
} | ||
|
||
protected override ImmutableHashSet<Type> KnownNonVirtualSyntaxKinds { get; } = ImmutableHashSet.Create( | ||
typeof(LiteralExpressionSyntax)); | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractCallInfoFinder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers | ||
{ | ||
internal abstract class AbstractCallInfoFinder : ICallInfoFinder | ||
{ | ||
public CallInfoContext GetCallInfoContext( | ||
SemanticModel semanticModel, SyntaxNode syntaxNode) | ||
{ | ||
var callInfoParameterSymbol = GetCallInfoParameterSymbol(semanticModel, syntaxNode); | ||
if (callInfoParameterSymbol == null) | ||
{ | ||
return CallInfoContext.Empty; | ||
} | ||
|
||
var callContext = GetCallInfoContextInternal(semanticModel, syntaxNode); | ||
|
||
return CreateFilteredCallInfoContext(semanticModel, callContext, callInfoParameterSymbol); | ||
} | ||
|
||
protected abstract CallInfoContext GetCallInfoContextInternal(SemanticModel semanticModel, SyntaxNode syntaxNode); | ||
|
||
private static CallInfoContext CreateFilteredCallInfoContext( | ||
SemanticModel semanticModel, | ||
CallInfoContext callContext, | ||
IParameterSymbol callInfoParameterSymbol) | ||
{ | ||
return new CallInfoContext( | ||
argAtInvocations: GetMatchingNodes(semanticModel, callContext.ArgAtInvocations, callInfoParameterSymbol), | ||
argInvocations: GetMatchingNodes(semanticModel, callContext.ArgInvocations, callInfoParameterSymbol), | ||
indexerAccesses: GetMatchingNodes(semanticModel, callContext.IndexerAccesses, callInfoParameterSymbol)); | ||
} | ||
|
||
private static IReadOnlyList<T> GetMatchingNodes<T>( | ||
SemanticModel semanticModel, | ||
IReadOnlyList<T> nodes, | ||
IParameterSymbol parameterSymbol) where T : SyntaxNode | ||
{ | ||
return nodes.Where(node => HasMatchingParameterReference(semanticModel, node, parameterSymbol)).ToList(); | ||
} | ||
|
||
private static bool HasMatchingParameterReference( | ||
SemanticModel semanticModel, | ||
SyntaxNode syntaxNode, | ||
IParameterSymbol callInfoParameterSymbol) | ||
{ | ||
var parameterReferenceOperation = FindMatchingParameterReference(semanticModel, syntaxNode); | ||
|
||
return parameterReferenceOperation != null && | ||
parameterReferenceOperation.Parameter.Equals(callInfoParameterSymbol); | ||
} | ||
|
||
private static IParameterReferenceOperation FindMatchingParameterReference(SemanticModel semanticModel, SyntaxNode syntaxNode) | ||
{ | ||
var operation = semanticModel.GetOperation(syntaxNode); | ||
return FindMatchingParameterReference(operation); | ||
} | ||
|
||
private static IParameterReferenceOperation FindMatchingParameterReference(IOperation operation) | ||
{ | ||
IParameterReferenceOperation parameterReferenceOperation = null; | ||
switch (operation) | ||
{ | ||
case IInvocationOperation invocationOperation: | ||
parameterReferenceOperation = invocationOperation.Instance as IParameterReferenceOperation; | ||
break; | ||
case IPropertyReferenceOperation propertyReferenceOperation: | ||
parameterReferenceOperation = propertyReferenceOperation.Instance as IParameterReferenceOperation; | ||
break; | ||
} | ||
|
||
if (parameterReferenceOperation != null) | ||
{ | ||
return parameterReferenceOperation; | ||
} | ||
|
||
foreach (var innerOperation in operation?.Children ?? Enumerable.Empty<IOperation>()) | ||
{ | ||
parameterReferenceOperation = FindMatchingParameterReference(innerOperation); | ||
if (parameterReferenceOperation != null) | ||
{ | ||
return parameterReferenceOperation; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static IParameterSymbol GetCallInfoParameterSymbol(SemanticModel semanticModel, SyntaxNode syntaxNode) | ||
{ | ||
if (semanticModel.GetSymbolInfo(syntaxNode).Symbol is IMethodSymbol methodSymbol && methodSymbol.MethodKind != MethodKind.Constructor) | ||
{ | ||
return methodSymbol.Parameters.FirstOrDefault(); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
...Substitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractNonSubstitutableMemberAnalysis.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using NSubstitute.Analyzers.Shared.Extensions; | ||
|
||
namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers | ||
{ | ||
internal abstract class AbstractNonSubstitutableMemberAnalysis : INonSubstitutableMemberAnalysis | ||
{ | ||
protected abstract ImmutableHashSet<Type> KnownNonVirtualSyntaxKinds { get; } | ||
|
||
public NonSubstitutableMemberAnalysisResult Analyze( | ||
in SyntaxNodeAnalysisContext syntaxNodeContext, | ||
SyntaxNode accessedMember, | ||
ISymbol symbol = null) | ||
{ | ||
var accessedSymbol = symbol ?? syntaxNodeContext.SemanticModel.GetSymbolInfo(accessedMember).Symbol; | ||
|
||
if (accessedSymbol == null) | ||
{ | ||
return new NonSubstitutableMemberAnalysisResult( | ||
nonVirtualMemberSubstitution: KnownNonVirtualSyntaxKinds.Contains(accessedMember.GetType()), | ||
internalMemberSubstitution: false, | ||
symbol: null, | ||
member: accessedMember, | ||
memberName: accessedMember.ToString()); | ||
} | ||
|
||
var canBeSubstituted = CanBeSubstituted(syntaxNodeContext, accessedMember, accessedSymbol); | ||
|
||
if (canBeSubstituted == false) | ||
{ | ||
return new NonSubstitutableMemberAnalysisResult( | ||
nonVirtualMemberSubstitution: true, | ||
internalMemberSubstitution: false, | ||
symbol: accessedSymbol, | ||
member: accessedMember, | ||
memberName: accessedSymbol.Name); | ||
} | ||
|
||
if (accessedSymbol.MemberVisibleToProxyGenerator() == false) | ||
{ | ||
return new NonSubstitutableMemberAnalysisResult( | ||
nonVirtualMemberSubstitution: false, | ||
internalMemberSubstitution: true, | ||
symbol: accessedSymbol, | ||
member: accessedMember, | ||
memberName: accessedSymbol.Name); | ||
} | ||
|
||
return new NonSubstitutableMemberAnalysisResult( | ||
nonVirtualMemberSubstitution: false, | ||
internalMemberSubstitution: false, | ||
symbol: accessedSymbol, | ||
member: accessedMember, | ||
memberName: accessedSymbol.Name); | ||
} | ||
|
||
protected virtual bool CanBeSubstituted( | ||
SyntaxNodeAnalysisContext syntaxNodeContext, | ||
SyntaxNode accessedMember, | ||
ISymbol symbol) | ||
{ | ||
return !KnownNonVirtualSyntaxKinds.Contains(accessedMember.GetType()) && | ||
CanBeSubstituted(symbol); | ||
} | ||
|
||
private static bool CanBeSubstituted(ISymbol symbol) | ||
{ | ||
return IsInterfaceMember(symbol) || IsVirtual(symbol); | ||
} | ||
|
||
private static bool IsInterfaceMember(ISymbol symbol) | ||
{ | ||
return symbol.ContainingType?.TypeKind == TypeKind.Interface; | ||
} | ||
|
||
private static bool IsVirtual(ISymbol symbol) | ||
{ | ||
var isVirtual = symbol.IsVirtual | ||
|| (symbol.IsOverride && !symbol.IsSealed) | ||
|| symbol.IsAbstract; | ||
|
||
return isVirtual; | ||
} | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/CallInfoContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/NonSubstitutableMemberAnalysis.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis.VisualBasic.Syntax; | ||
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers; | ||
|
||
namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers | ||
{ | ||
internal class NonSubstitutableMemberAnalysis : AbstractNonSubstitutableMemberAnalysis | ||
{ | ||
public static NonSubstitutableMemberAnalysis Instance { get; } = new NonSubstitutableMemberAnalysis(); | ||
|
||
private NonSubstitutableMemberAnalysis() | ||
{ | ||
} | ||
|
||
protected override ImmutableHashSet<Type> KnownNonVirtualSyntaxKinds { get; } = ImmutableHashSet.Create( | ||
typeof(LiteralExpressionSyntax)); | ||
} | ||
} |
Oops, something went wrong.