Exemple #1
0
        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);
        }
Exemple #2
0
        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());
        }
Exemple #4
0
        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);
        }
Exemple #5
0
 /// <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);
            }
        }
Exemple #7
0
        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);
        }