Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Gh 36] syntax node is not within syntax tree #37

Merged
merged 1 commit into from
Sep 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,33 @@ namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
internal class ReEntrantCallFinder : AbstractReEntrantCallFinder
{
protected override ImmutableList<ISymbol> GetReEntrantSymbols(SemanticModel semanticModel, SyntaxNode rootNode)
protected override ImmutableList<ISymbol> GetReEntrantSymbols(Compilation compilation, SyntaxNode rootNode)
{
var visitor = new ReEntrantCallVisitor(this, semanticModel);
var visitor = new ReEntrantCallVisitor(this, compilation);
visitor.Visit(rootNode);
return visitor.InvocationSymbols;
}

private class ReEntrantCallVisitor : CSharpSyntaxWalker
{
private readonly ReEntrantCallFinder _reEntrantCallFinder;
private readonly SemanticModel _semanticModel;
private readonly Compilation _compilation;
private readonly HashSet<SyntaxNode> _visitedNodes = new HashSet<SyntaxNode>();
private readonly List<ISymbol> _invocationSymbols = new List<ISymbol>();

public ImmutableList<ISymbol> InvocationSymbols => _invocationSymbols.ToImmutableList();

public ReEntrantCallVisitor(ReEntrantCallFinder reEntrantCallFinder, SemanticModel semanticModel)
public ReEntrantCallVisitor(ReEntrantCallFinder reEntrantCallFinder, Compilation compilation)
{
_reEntrantCallFinder = reEntrantCallFinder;
_semanticModel = semanticModel;
_compilation = compilation;
}

public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbolInfo = _semanticModel.GetSymbolInfo(node);
if (_reEntrantCallFinder.IsReturnsLikeMethod(_semanticModel, symbolInfo.Symbol))
var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree);
var symbolInfo = semanticModel.GetSymbolInfo(node);
if (_reEntrantCallFinder.IsReturnsLikeMethod(semanticModel, symbolInfo.Symbol))
{
_invocationSymbols.Add(symbolInfo.Symbol);
}
Expand All @@ -57,7 +58,7 @@ private void VisitRelatedSymbols(SyntaxNode syntaxNode)
syntaxNode.IsKind(SyntaxKind.SimpleMemberAccessExpression)))
{
_visitedNodes.Add(syntaxNode);
foreach (var relatedNode in _reEntrantCallFinder.GetRelatedNodes(_semanticModel, syntaxNode))
foreach (var relatedNode in _reEntrantCallFinder.GetRelatedNodes(_compilation, syntaxNode))
{
Visit(relatedNode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ internal abstract class AbstractReEntrantCallFinder
[MetadataNames.NSubstituteDoMethod] = MetadataNames.NSubstituteWhenCalledType
}.ToImmutableDictionary();

public ImmutableList<ISymbol> GetReEntrantCalls(SemanticModel semanticModel, SyntaxNode rootNode)
public ImmutableList<ISymbol> GetReEntrantCalls(Compilation compilation, SyntaxNode rootNode)
{
var semanticModel = compilation.GetSemanticModel(rootNode.SyntaxTree);
var typeInfo = semanticModel.GetTypeInfo(rootNode);
return typeInfo.IsCallInfoDelegate(semanticModel) ? ImmutableList<ISymbol>.Empty : GetReEntrantSymbols(semanticModel, rootNode);
return typeInfo.IsCallInfoDelegate(semanticModel) ? ImmutableList<ISymbol>.Empty : GetReEntrantSymbols(compilation, rootNode);
}

protected abstract ImmutableList<ISymbol> GetReEntrantSymbols(SemanticModel semanticModel, SyntaxNode rootNode);
protected abstract ImmutableList<ISymbol> GetReEntrantSymbols(Compilation compilation, SyntaxNode rootNode);

protected IEnumerable<SyntaxNode> GetRelatedNodes(SemanticModel semanticModel, SyntaxNode syntaxNode)
protected IEnumerable<SyntaxNode> GetRelatedNodes(Compilation compilation, SyntaxNode syntaxNode)
{
var symbol = semanticModel.GetSymbolInfo(syntaxNode);
var symbol = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetSymbolInfo(syntaxNode);
if (symbol.Symbol != null && symbol.Symbol.Locations.Any())
{
foreach (var symbolLocation in symbol.Symbol.Locations.Where(location => location.SourceTree != null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)

foreach (var argument in argumentsForAnalysis)
{
var reentrantSymbol = ReEntrantCallFinder.GetReEntrantCalls(syntaxNodeContext.SemanticModel, argument).FirstOrDefault();
var reentrantSymbol = ReEntrantCallFinder.GetReEntrantCalls(syntaxNodeContext.Compilation, argument).FirstOrDefault();
if (reentrantSymbol != null)
{
var diagnostic = Diagnostic.Create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,33 @@ namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers
{
internal class ReEntrantCallFinder : AbstractReEntrantCallFinder
{
protected override ImmutableList<ISymbol> GetReEntrantSymbols(SemanticModel semanticModel, SyntaxNode rootNode)
protected override ImmutableList<ISymbol> GetReEntrantSymbols(Compilation compilation, SyntaxNode rootNode)
{
var visitor = new ReEntrantCallVisitor(this, semanticModel);
var visitor = new ReEntrantCallVisitor(this, compilation);
visitor.Visit(rootNode);
return visitor.InvocationSymbols;
}

private class ReEntrantCallVisitor : VisualBasicSyntaxWalker
{
private readonly ReEntrantCallFinder _reEntrantCallFinder;
private readonly SemanticModel _semanticModel;
private readonly Compilation _compilation;
private readonly HashSet<SyntaxNode> _visitedNodes = new HashSet<SyntaxNode>();
private readonly List<ISymbol> _invocationSymbols = new List<ISymbol>();

public ImmutableList<ISymbol> InvocationSymbols => _invocationSymbols.ToImmutableList();

public ReEntrantCallVisitor(ReEntrantCallFinder reEntrantCallFinder, SemanticModel semanticModel)
public ReEntrantCallVisitor(ReEntrantCallFinder reEntrantCallFinder, Compilation compilation)
{
_reEntrantCallFinder = reEntrantCallFinder;
_semanticModel = semanticModel;
_compilation = compilation;
}

public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbolInfo = ModelExtensions.GetSymbolInfo(_semanticModel, node);
if (_reEntrantCallFinder.IsReturnsLikeMethod(_semanticModel, symbolInfo.Symbol))
var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree);
var symbolInfo = semanticModel.GetSymbolInfo(node);
if (_reEntrantCallFinder.IsReturnsLikeMethod(semanticModel, symbolInfo.Symbol))
{
_invocationSymbols.Add(symbolInfo.Symbol);
}
Expand All @@ -55,7 +56,7 @@ private void VisitRelatedSymbols(SyntaxNode syntaxNode)
syntaxNode.IsKind(SyntaxKind.SimpleMemberAccessExpression)))
{
_visitedNodes.Add(syntaxNode);
foreach (var relatedNode in _reEntrantCallFinder.GetRelatedNodes(_semanticModel, syntaxNode))
foreach (var relatedNode in _reEntrantCallFinder.GetRelatedNodes(_compilation, syntaxNode))
{
var currentNode = relatedNode;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public abstract class ReEntrantReturnsSetupDiagnosticVerifier : CSharpDiagnostic

public abstract Task ReportsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn);

public abstract Task ReportsDiagnostic_WhenUsingReEntrantReturns_AcrossMultipleFiles();

protected override DiagnosticAnalyzer GetDiagnosticAnalyzer()
{
return new ReEntrantSetupAnalyzer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -574,5 +574,63 @@ private int OtherReturn(CallInfo info)
}}";
await VerifyDiagnostic(source);
}

[Fact]
public override async Task ReportsDiagnostic_WhenUsingReEntrantReturns_AcrossMultipleFiles()
{
var source = @"using NSubstitute;

namespace MyNamespace
{
public interface IFoo
{
int Bar();
}

public interface IBar
{
int Foo();
}

public class FooTests
{
public void Test()
{
var substitute = Substitute.For<IFoo>();
substitute.Bar().Returns(FooBar.ReturnThis());
}
}
}";

var secondSource = @"
using NSubstitute;

namespace MyNamespace
{
public class FooBar
{
public static int ReturnThis()
{
var substitute = Substitute.For<IBar>();
substitute.Foo().Returns(1);
return 1;
}
}
}";

var firstArgumentDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.ReEntrantSubstituteCall,
Severity = DiagnosticSeverity.Warning,
Message =
"Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(x => FooBar.ReturnThis()).",
Locations = new[]
{
new DiagnosticResultLocation(20, 38)
}
};

await VerifyDiagnostic(new[] { source, secondSource }, firstArgumentDiagnostic);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -573,5 +573,63 @@ private int OtherReturn(CallInfo info)
}}";
await VerifyDiagnostic(source);
}

[Fact]
public override async Task ReportsDiagnostic_WhenUsingReEntrantReturns_AcrossMultipleFiles()
{
var source = @"using NSubstitute;

namespace MyNamespace
{
public interface IFoo
{
int Bar();
}

public interface IBar
{
int Foo();
}

public class FooTests
{
public void Test()
{
var substitute = Substitute.For<IFoo>();
substitute.Bar().Returns<int>(FooBar.ReturnThis());
}
}
}";

var secondSource = @"
using NSubstitute;

namespace MyNamespace
{
public class FooBar
{
public static int ReturnThis()
{
var substitute = Substitute.For<IBar>();
substitute.Foo().Returns<int>(1);
return 1;
}
}
}";

var firstArgumentDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.ReEntrantSubstituteCall,
Severity = DiagnosticSeverity.Warning,
Message =
"Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(x => FooBar.ReturnThis()).",
Locations = new[]
{
new DiagnosticResultLocation(20, 43)
}
};

await VerifyDiagnostic(new[] { source, secondSource }, firstArgumentDiagnostic);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -573,5 +573,63 @@ private int OtherReturn(CallInfo info)
}}";
await VerifyDiagnostic(source);
}

[Fact]
public override async Task ReportsDiagnostic_WhenUsingReEntrantReturns_AcrossMultipleFiles()
{
var source = @"using NSubstitute;

namespace MyNamespace
{
public interface IFoo
{
int Bar();
}

public interface IBar
{
int Foo();
}

public class FooTests
{
public void Test()
{
var substitute = Substitute.For<IFoo>();
SubstituteExtensions.Returns(substitute.Bar(), FooBar.ReturnThis());
}
}
}";

var secondSource = @"
using NSubstitute;

namespace MyNamespace
{
public class FooBar
{
public static int ReturnThis()
{
var substitute = Substitute.For<IBar>();
substitute.Foo().Returns(1);
return 1;
}
}
}";

var firstArgumentDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.ReEntrantSubstituteCall,
Severity = DiagnosticSeverity.Warning,
Message =
"Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(x => FooBar.ReturnThis()).",
Locations = new[]
{
new DiagnosticResultLocation(20, 60)
}
};

await VerifyDiagnostic(new[] { source, secondSource }, firstArgumentDiagnostic);
}
}
}
Loading