Skip to content

Commit

Permalink
[GH-30] - scaffolding
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Aug 24, 2018
1 parent ab68d85 commit 9300e00
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CallInfoAnalyzer : AbstractDiagnosticAnalyzer
{
public CallInfoAnalyzer()
: base(new DiagnosticDescriptorsProvider())
{
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptorsProvider.CallInfoArgumentOutOfRange);

public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression);
}

private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
{
var invocationExpression = syntaxNodeContext.Node;
var methodSymbolInfo = syntaxNodeContext.SemanticModel.GetSymbolInfo(invocationExpression);

if (methodSymbolInfo.Symbol?.Kind != SymbolKind.Method)
{
return;
}

var methodSymbol = (IMethodSymbol)methodSymbolInfo.Symbol;
if (IsCallInfoMethod(methodSymbol) == false)
{
return;
}
}

private bool IsCallInfoMethod(IMethodSymbol methodSymbol)
{
return methodSymbol.ContainingType.ToString() == "NSubstitute.Core.CallInfo";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ internal class AbstractDiagnosticDescriptorsProvider<T> : IDiagnosticDescriptors
public DiagnosticDescriptor NonVirtualWhenSetupSpecification { get; } = DiagnosticDescriptors<T>.NonVirtualWhenSetupSpecification;

public DiagnosticDescriptor ReEntrantSubstituteCall { get; } = DiagnosticDescriptors<T>.ReEntrantSubstituteCall;

public DiagnosticDescriptor CallInfoArgumentOutOfRange { get; } = DiagnosticDescriptors<T>.ReEntrantSubstituteCall;
}
}
8 changes: 8 additions & 0 deletions src/NSubstitute.Analyzers.Shared/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ internal class DiagnosticDescriptors<T>
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public static DiagnosticDescriptor CallInfoArgumentOutOfRange { get; } =
CreateDiagnosticDescriptor(
name: nameof(CallInfoArgumentOutOfRange),
id: DiagnosticIdentifiers.CallInfoArgumentOutOfRange,
category: DiagnosticCategories.Usage,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

private static DiagnosticDescriptor CreateDiagnosticDescriptor(
string name, string id, string category, DiagnosticSeverity defaultSeverity, bool isEnabledByDefault)
{
Expand Down
1 change: 1 addition & 0 deletions src/NSubstitute.Analyzers.Shared/DiagnosticIdentifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ internal class DiagnosticIdentifiers
public static readonly string NonVirtualReceivedSetupSpecification = "NS011";
public static readonly string NonVirtualWhenSetupSpecification = "NS012";
public static readonly string ReEntrantSubstituteCall = "NS013";
public static readonly string CallInfoArgumentOutOfRange = "NS014";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ internal interface IDiagnosticDescriptorsProvider
DiagnosticDescriptor NonVirtualWhenSetupSpecification { get; }

DiagnosticDescriptor ReEntrantSubstituteCall { get; }

DiagnosticDescriptor CallInfoArgumentOutOfRange { get; }
}
}
29 changes: 28 additions & 1 deletion src/NSubstitute.Analyzers.Shared/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion src/NSubstitute.Analyzers.Shared/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@
<value>Can not provide constructor arguments when substituting for a delegate.</value>
<comment>An optional longer localizable description of the diagnostic.</comment>
</data>

<data name="SubstituteConstructorArgumentsForDelegateTitle" xml:space="preserve">
<value>Can not provide constructor arguments when substituting for a delegate.</value>
<comment>The title of the diagnostic.</comment>
Expand Down Expand Up @@ -258,5 +257,17 @@
<data name="ReEntrantSubstituteCallTitle" xml:space="preserve">
<value>Re-entrant substitute call.</value>
<comment>The title of the diagnostic.</comment>
</data>
<data name="CallInfoArgumentOutOfRangeDescription" xml:space="preserve">
<value>Unable to find argument.</value>
<comment>An optional longer localizable description of the diagnostic.</comment>
</data>
<data name="CallInfoArgumentOutOfRangeMessageFormat" xml:space="preserve">
<value>There is no argument at position {0}</value>
<comment>The format-able message the diagnostic displays.</comment>
</data>
<data name="CallInfoArgumentOutOfRangeTitle" xml:space="preserve">
<value>Unable to find matching argument.</value>
<comment>The title of the diagnostic.</comment>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers;
using NSubstitute.Analyzers.Shared;
using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers;
using Xunit;

namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.CallInfoAnalyzerTests
{
public class CallInfoAnalyzerTests : CSharpDiagnosticVerifier
{
[Fact]
public async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds()
{
var source = @"using System;
using NSubstitute;
namespace MyNamespace
{
public interface Foo
{
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<Foo>();
substitute.Bar(Arg.Any<int>()).Returns(callInfo => callInfo.ArgAt<int>(1));
}
}
}";
var expectedDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange,
Severity = DiagnosticSeverity.Warning,
Message = "There is no argument at position 1",
Locations = new[]
{
new DiagnosticResultLocation(18, 13)
}
};

await VerifyDiagnostic(source, expectedDiagnostic);
}

protected override DiagnosticAnalyzer GetDiagnosticAnalyzer()
{
return new CallInfoAnalyzer();
}
}
}

0 comments on commit 9300e00

Please sign in to comment.