public void WithSyntaxErrors(DiagnosticAnalyzer analyzer) { var code = @" namespace N { using System; using System.IO; internal class C : SyntaxError { private readonly Stream stream = File.SyntaxError(string.Empty); private bool disposed; protected override void Dispose(bool disposing) { if (this.syntaxError) { return; } this.disposed = true; if (disposing) { this.stream.Dispose(); } base.Dispose(disposing); } } }"; var sln = CodeFactory.CreateSolution(code, CodeFactory.DefaultCompilationOptions(analyzer, RoslynAssert.SuppressedDiagnostics), RoslynAssert.MetadataReferences); var diagnostics = Analyze.GetDiagnostics(analyzer, sln); RoslynAssert.NoDiagnostics(diagnostics); }
public void CtorCallingSelf(DiagnosticAnalyzer analyzer) { var testCode = @" namespace N { internal abstract class C { internal C() : this() { } } }"; var sln = CodeFactory.CreateSolution(testCode, CodeFactory.DefaultCompilationOptions(analyzer), RoslynAssert.MetadataReferences); var diagnostics = Analyze.GetDiagnostics(analyzer, sln); RoslynAssert.NoDiagnostics(diagnostics); }
private async Task <ImmutableArray <CodeAction> > GetOfferedFixesInternalAsync(string source, int?diagnosticIndex, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, CancellationToken cancellationToken) { var document = CodeFactory.CreateDocument(source, analyzers); var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false); var index = diagnosticIndex ?? 0; Assert.True(index < analyzerDiagnostics.Count()); var actions = new List <CodeAction>(); // do not pass unsupported diagnostics to a code fix provider if (codeFixProvider.FixableDiagnosticIds.Contains(analyzerDiagnostics[index].Id)) { var context = new CodeFixContext(document, analyzerDiagnostics[index], (a, d) => actions.Add(a), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); } return(actions.ToImmutableArray()); }
public async Task WithSyntaxErrors() { var syntaxErrorCode = @" using System; using System.IO; public class Foo : SyntaxError { private readonly Stream stream = File.SyntaxError(string.Empty); private bool disposed; protected override void Dispose(bool disposing) { if (this.syntaxError) { return; } this.disposed = true; if (disposing) { this.stream.Dispose(); } base.Dispose(disposing); } }"; var analyzers = this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(); await DiagnosticVerifier.GetSortedDiagnosticsFromDocumentsAsync( analyzers, CodeFactory.GetDocuments( new[] { syntaxErrorCode }, analyzers, Enumerable.Empty <string>()), CancellationToken.None) .ConfigureAwait(false); }
/// <summary> /// Given classes in the form of strings, their language, and an <see cref="DiagnosticAnalyzer"/> to apply to /// it, return the <see cref="Diagnostic"/>s found in the string after converting it to a /// <see cref="Document"/>. /// </summary> /// <param name="sources">Classes in the form of strings.</param> /// <param name="analyzers">The analyzers to be run on the sources.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param> /// <returns>A collection of <see cref="Diagnostic"/>s that surfaced in the source code, sorted by /// <see cref="Diagnostic.Location"/>.</returns> public static Task <ImmutableArray <Diagnostic> > GetSortedDiagnosticsAsync(string[] sources, ImmutableArray <DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken) { return(GetSortedDiagnosticsFromDocumentsAsync(analyzers, CodeFactory.GetDocuments(sources, analyzers, Enumerable.Empty <string>()), cancellationToken)); }
private async Task VerifyFixInternalAsync( ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, string[] oldSources, string[] newSources, int?codeFixIndex, bool allowNewCompilerDiagnostics, int numberOfIterations, Func <ImmutableArray <DiagnosticAnalyzer>, CodeFixProvider, int?, Project, int, CancellationToken, Task <Project> > getFixedProject, CancellationToken cancellationToken) { var project = CodeFactory.CreateProject(oldSources, analyzers); var compilerDiagnostics = await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); project = await getFixedProject(analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken).ConfigureAwait(false); var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false)); // Check if applying the code fix introduced any new compiler diagnostics if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any()) { // Format and get the compiler diagnostics again so that the locations make sense in the output project = await ReformatProjectDocumentsAsync(project, cancellationToken).ConfigureAwait(false); newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false)); var message = new StringBuilder(); message.Append("Fix introduced new compiler diagnostics:\r\n"); newCompilerDiagnostics.Aggregate(message, (sb, d) => sb.Append(d.ToString()).Append("\r\n")).IgnoreReturnValue(); foreach (var document in project.Documents) { message.Append("\r\n").Append(document.Name).Append(":\r\n"); message.Append((await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).ToFullString()); message.Append("\r\n"); } Assert.Fail(message.ToString()); } // After applying all of the code fixes, compare the resulting string to the inputted one var updatedDocuments = project.Documents.ToArray(); Assert.AreEqual($"{newSources.Length} documents", $"{updatedDocuments.Length} documents"); for (var i = 0; i < updatedDocuments.Length; i++) { var actual = await GetStringFromDocumentAsync(updatedDocuments[i], cancellationToken).ConfigureAwait(false); var expectedCode = new CodeReader(newSources[i]); var actualCode = new CodeReader(actual); if (expectedCode != actualCode) { Console.WriteLine("Expected:"); Console.Write(expectedCode); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Actual:"); Console.Write(actualCode); Console.WriteLine(); } Assert.AreEqual(expectedCode, actualCode); } }
public async Task Repro(DiagnosticAnalyzer analyzer) { var testCode = @" namespace Gu.Analyzers { using System.Collections; using System.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; internal static partial class Disposable { internal static Result IsMemberDisposed(ISymbol member, TypeDeclarationSyntax context, SemanticModel semanticModel, CancellationToken cancellationToken) { return IsMemberDisposed(member, semanticModel.GetDeclaredSymbolSafe(context, cancellationToken), semanticModel, cancellationToken); } internal static Result IsMemberDisposed(ISymbol member, ITypeSymbol context, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!(member is IFieldSymbol || member is IPropertySymbol) || context == null) { return Result.Unknown; } using (var pooled = DisposeWalker.Create(context, semanticModel, cancellationToken)) { return pooled.Item.IsMemberDisposed(member); } } internal static bool IsMemberDisposed(ISymbol member, IMethodSymbol disposeMethod, SemanticModel semanticModel, CancellationToken cancellationToken) { if (member == null || disposeMethod == null) { return false; } foreach (var reference in disposeMethod.DeclaringSyntaxReferences) { var node = reference.GetSyntax(cancellationToken) as MethodDeclarationSyntax; using (var pooled = DisposeWalker.Create(disposeMethod, semanticModel, cancellationToken)) { foreach (var invocation in pooled.Item) { var method = semanticModel.GetSymbolSafe(invocation, cancellationToken) as IMethodSymbol; if (method == null || method.Parameters.Length != 0 || method != KnownSymbol.IDisposable.Dispose) { continue; } if (TryGetDisposedRootMember(invocation, semanticModel, cancellationToken, out ExpressionSyntax disposed)) { if (SymbolComparer.Equals(member, semanticModel.GetSymbolSafe(disposed, cancellationToken))) { return true; } } } } using (var pooled = IdentifierNameWalker.Create(node)) { foreach (var identifier in pooled.Item.IdentifierNames) { var memberAccess = identifier.Parent as MemberAccessExpressionSyntax; if (memberAccess?.Expression is BaseExpressionSyntax) { var baseMethod = semanticModel.GetSymbolSafe(identifier, cancellationToken) as IMethodSymbol; if (baseMethod?.Name == ""Dispose"") { if (IsMemberDisposed(member, baseMethod, semanticModel, cancellationToken)) { return true; } } } if (identifier.Identifier.ValueText != member.Name) { continue; } var symbol = semanticModel.GetSymbolSafe(identifier, cancellationToken); if (member.Equals(symbol) || (member as IPropertySymbol)?.OverriddenProperty?.Equals(symbol) == true) { return true; } } } } return false; } internal static bool TryGetDisposedRootMember(InvocationExpressionSyntax disposeCall, SemanticModel semanticModel, CancellationToken cancellationToken, out ExpressionSyntax disposedMember) { if (MemberPath.TryFindRootMember(disposeCall, out disposedMember)) { var property = semanticModel.GetSymbolSafe(disposedMember, cancellationToken) as IPropertySymbol; if (property == null || property.IsAutoProperty(cancellationToken)) { return true; } if (property.GetMethod == null) { return false; } foreach (var reference in property.GetMethod.DeclaringSyntaxReferences) { var node = reference.GetSyntax(cancellationToken); using (var pooled = ReturnValueWalker.Create(node, false, semanticModel, cancellationToken)) { if (pooled.Item.Count == 0) { return false; } return MemberPath.TryFindRootMember(pooled.Item[0], out disposedMember); } } } return false; } internal sealed class DisposeWalker : ExecutionWalker, IReadOnlyList<InvocationExpressionSyntax> { private static readonly Pool<DisposeWalker> Pool = new Pool<DisposeWalker>( () => new DisposeWalker(), x => { x.invocations.Clear(); x.identifiers.Clear(); x.Clear(); }); private readonly List<InvocationExpressionSyntax> invocations = new List<InvocationExpressionSyntax>(); private readonly List<IdentifierNameSyntax> identifiers = new List<IdentifierNameSyntax>(); private DisposeWalker() { this.IsRecursive = true; } public int Count => this.invocations.Count; public InvocationExpressionSyntax this[int index] => this.invocations[index]; public IEnumerator<InvocationExpressionSyntax> GetEnumerator() => this.invocations.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.invocations).GetEnumerator(); public override void VisitInvocationExpression(InvocationExpressionSyntax node) { base.VisitInvocationExpression(node); var symbol = this.SemanticModel.GetSymbolSafe(node, this.CancellationToken) as IMethodSymbol; if (symbol == KnownSymbol.IDisposable.Dispose && symbol?.Parameters.Length == 0) { this.invocations.Add(node); } } public override void VisitIdentifierName(IdentifierNameSyntax node) { this.identifiers.Add(node); base.VisitIdentifierName(node); } internal static Pool<DisposeWalker>.Pooled Create(ITypeSymbol type, SemanticModel semanticModel, CancellationToken cancellationToken) { if (!IsAssignableTo(type)) { return Create(semanticModel, cancellationToken); } if (TryGetDisposeMethod(type, true, out IMethodSymbol disposeMethod)) { return Create(disposeMethod, semanticModel, cancellationToken); } return Create(semanticModel, cancellationToken); } internal static Pool<DisposeWalker>.Pooled Create(IMethodSymbol disposeMethod, SemanticModel semanticModel, CancellationToken cancellationToken) { if (disposeMethod != KnownSymbol.IDisposable.Dispose) { return Create(semanticModel, cancellationToken); } var pooled = Create(semanticModel, cancellationToken); foreach (var reference in disposeMethod.DeclaringSyntaxReferences) { pooled.Item.Visit(reference.GetSyntax(cancellationToken)); } return pooled; } internal Result IsMemberDisposed(ISymbol member) { foreach (var invocation in this.invocations) { if (TryGetDisposedRootMember(invocation, this.SemanticModel, this.CancellationToken, out ExpressionSyntax disposed) && SymbolComparer.Equals(member, this.SemanticModel.GetSymbolSafe(disposed, this.CancellationToken))) { return Result.Yes; } } foreach (var name in this.identifiers) { if (SymbolComparer.Equals(member, this.SemanticModel.GetSymbolSafe(name, this.CancellationToken))) { return Result.Maybe; } } return Result.No; } private static Pool<DisposeWalker>.Pooled Create(SemanticModel semanticModel, CancellationToken cancellationToken) { var pooled = Pool.GetOrCreate(); pooled.Item.SemanticModel = semanticModel; pooled.Item.CancellationToken = cancellationToken; return pooled; } } } using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; internal static class SemanticModelExt { internal static SymbolInfo GetSymbolInfoSafe(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) { return semanticModel.SemanticModelFor(node) ?.GetSymbolInfo(node, cancellationToken) ?? default(SymbolInfo); } internal static bool IsEither<T1, T2>(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) where T1 : ISymbol where T2 : ISymbol { return semanticModel.GetSymbolSafe(node, cancellationToken).IsEither<T1, T2>() || semanticModel.GetDeclaredSymbolSafe(node, cancellationToken).IsEither<T1, T2>() || semanticModel.GetTypeInfoSafe(node, cancellationToken).Type.IsEither<T1, T2>(); } internal static ISymbol GetSymbolSafe(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) { var awaitExpression = node as AwaitExpressionSyntax; if (awaitExpression != null) { return GetSymbolSafe(semanticModel, awaitExpression, cancellationToken); } return semanticModel.SemanticModelFor(node) ?.GetSymbolInfo(node, cancellationToken) .Symbol; } internal static ISymbol GetSymbolSafe(this SemanticModel semanticModel, AwaitExpressionSyntax node, CancellationToken cancellationToken) { return semanticModel.GetSymbolSafe(node.Expression, cancellationToken); } internal static IMethodSymbol GetSymbolSafe(this SemanticModel semanticModel, MethodDeclarationSyntax node, CancellationToken cancellationToken) { return (IMethodSymbol)semanticModel.GetSymbolSafe((SyntaxNode)node, cancellationToken); } internal static IMethodSymbol GetSymbolSafe(this SemanticModel semanticModel, ConstructorInitializerSyntax node, CancellationToken cancellationToken) { return (IMethodSymbol)semanticModel.GetSymbolSafe((SyntaxNode)node, cancellationToken); } internal static IFieldSymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, FieldDeclarationSyntax node, CancellationToken cancellationToken) { return (IFieldSymbol)semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static IMethodSymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, ConstructorDeclarationSyntax node, CancellationToken cancellationToken) { return (IMethodSymbol)semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static ISymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, BasePropertyDeclarationSyntax node, CancellationToken cancellationToken) { return semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static IPropertySymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, PropertyDeclarationSyntax node, CancellationToken cancellationToken) { return (IPropertySymbol)semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static IPropertySymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, IndexerDeclarationSyntax node, CancellationToken cancellationToken) { return (IPropertySymbol)semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static IMethodSymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, MethodDeclarationSyntax node, CancellationToken cancellationToken) { return (IMethodSymbol)semanticModel.SemanticModelFor(node) .GetDeclaredSymbol(node, cancellationToken); } internal static ITypeSymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, TypeDeclarationSyntax node, CancellationToken cancellationToken) { return (ITypeSymbol)semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static ISymbol GetDeclaredSymbolSafe(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) { return semanticModel.SemanticModelFor(node) ?.GetDeclaredSymbol(node, cancellationToken); } internal static Optional<object> GetConstantValueSafe(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) { return semanticModel.SemanticModelFor(node) ?.GetConstantValue(node, cancellationToken) ?? default(Optional<object>); } internal static TypeInfo GetTypeInfoSafe(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) { return semanticModel.SemanticModelFor(node) ?.GetTypeInfo(node, cancellationToken) ?? default(TypeInfo); } /// <summary> /// Gets the semantic model for <paramref name=""expression""/> /// This can be needed for partial classes. /// </summary> /// <param name=""semanticModel"">The semantic model.</param> /// <param name=""expression"">The expression.</param> /// <returns>The semantic model that corresponds to <paramref name=""expression""/></returns> internal static SemanticModel SemanticModelFor(this SemanticModel semanticModel, SyntaxNode expression) { if (semanticModel == null || expression == null || expression.IsMissing) { return null; } if (ReferenceEquals(semanticModel.SyntaxTree, expression.SyntaxTree)) { return semanticModel; } if (semanticModel.Compilation.ContainsSyntaxTree(expression.SyntaxTree)) { return semanticModel.Compilation.GetSemanticModel(expression.SyntaxTree); } foreach (var metadataReference in semanticModel.Compilation.References) { var compilationReference = metadataReference as CompilationReference; if (compilationReference != null) { if (compilationReference.Compilation.ContainsSyntaxTree(expression.SyntaxTree)) { return compilationReference.Compilation.GetSemanticModel(expression.SyntaxTree); } } } return null; } } }"; Console.WriteLine(analyzer); var analyzers = ImmutableArray.Create(analyzer); await DiagnosticVerifier.GetSortedDiagnosticsFromDocumentsAsync( analyzers, CodeFactory.GetDocuments( new[] { testCode }, analyzers, Enumerable.Empty <string>()), CancellationToken.None) .ConfigureAwait(false); }