Example #1
0
        private static string ApplyTransformations(ILogSink logSink, string type, string input, IImmutableList <Transformation> transformations)
        {
            logSink.Info("Applying {0} transformations to input: '{1}'.", type, input);

            foreach (var transformation in transformations)
            {
                input = Regex.Replace(input, transformation.Pattern, transformation.Replacement);

                logSink.Debug(
                    " - after transformation '{0}' -> '{1}', input is now '{2}'.",
                    transformation.Pattern,
                    transformation.Replacement,
                    input);
            }

            return(input);
        }
Example #2
0
        public void Log <TState>(
            MS.LogLevel logLevel,
            MS.EventId eventId,
            TState state,
            Exception exception,
            Func <TState, Exception, string> formatter)
        {
            if (logLevel == MS.LogLevel.Trace)
            {
                return;
            }

            var message = formatter(state, exception);

            // Very annoyingly, MSBuild appends new lines onto messages itself.
            if (message.EndsWith("\r\n"))
            {
                message = message.Substring(0, message.Length - 2);
            }

            switch (logLevel)
            {
            case MS.LogLevel.Debug:
                logSink.Debug(message);
                break;

            case MS.LogLevel.Information:
                logSink.Info(message);
                break;

            case MS.LogLevel.Warning:
                logSink.Warn(message);
                break;

            default:
                logSink.Error(message);
                break;
            }
        }
Example #3
0
        public Configuration(
            ILogSink logSink,
            IEnumerable <Transformation> namespaceTransformations,
            IEnumerable <Transformation> nameTransformations,
            IEnumerable <Filter> interfaceFilters,
            IEnumerable <Plugin> plugins)
        {
            this.logSink = logSink;
            this.namespaceTransformations = namespaceTransformations.ToImmutableList();
            this.nameTransformations      = nameTransformations.ToImmutableList();
            this.interfaceFilters         = interfaceFilters.ToImmutableList();
            this.plugins = plugins.ToImmutableList();

            if (logSink.IsEnabled)
            {
                var namespaceTransformationsLog = this
                                                  .namespaceTransformations
                                                  .Aggregate(
                    new StringBuilder(),
                    (sb, next) => sb.Append(" - Namespaces matching '").Append(next.Pattern).Append("' will be replaced with '").Append(next.Replacement).AppendLine("'."), sb => sb.ToString());
                var nameTransformationsLog = this
                                             .nameTransformations
                                             .Aggregate(
                    new StringBuilder(),
                    (sb, next) => sb.Append(" - Names matching '").Append(next.Pattern).Append("' will be replaced with '").Append(next.Replacement).AppendLine("'."), sb => sb.ToString());
                var interfaceFiltersLog = this
                                          .interfaceFilters
                                          .Aggregate(
                    new StringBuilder(),
                    (sb, next) => sb.Append(" - Interfaces matching '").Append(next.Pattern).Append("' will be '").Append(next.Type == FilterType.Include ? "included" : "excluded").AppendLine("."), sb => sb.ToString());
                var pluginsLog = this
                                 .plugins
                                 .Aggregate(
                    new StringBuilder(),
                    (sb, next) => sb.Append(" - Plugin with assembly-qualified name '").Append(next.AssemblyQualifiedName).AppendLine("' will be applied."));
                logSink.Debug(logSource, $"Created configuration with the following rules:{Environment.NewLine}{namespaceTransformationsLog}{nameTransformationsLog}{interfaceFiltersLog}{pluginsLog}");
            }
        }
Example #4
0
        public async static Task <IImmutableList <SyntaxNode> > GenerateMocksAsync(
            ILogSink logSink,
            Language language,
            Solution solution,
            Func <INamedTypeSymbol, bool> interfacePredicate,
            Func <INamedTypeSymbol, string> mockNamespaceSelector,
            Func <INamedTypeSymbol, string> mockNameSelector,
            IImmutableList <IPlugin> plugins)
        {
            var syntaxGenerator = SyntaxGenerator.GetGenerator(solution.Workspace, language.ToSyntaxGeneratorLanguageName());
            var compilations    = await Task.WhenAll(
                solution
                .Projects
                .Select(async project =>
            {
                var compilation = await project.GetCompilationAsync();
                // make sure the compilation has a reference to PCLMock
                compilation = compilation.AddReferences(MetadataReference.CreateFromFile(typeof(MockBase <>).Assembly.Location));

                foreach (var plugin in plugins)
                {
                    compilation = plugin.InitializeCompilation(compilation);
                }

                if (logSink.IsEnabled)
                {
                    logSink.Debug(logSource, "Compilation generated for project '{0}' with references:", project.Name);

                    foreach (var reference in compilation.References)
                    {
                        logSink.Debug(logSource, "- {0}", reference.Display);
                    }
                }

                return(compilation);
            }));

            return(compilations
                   .SelectMany(
                       compilation =>
                       GetSymbolsRecursive(compilation.SourceModule.GlobalNamespace)
                       .OfType <INamedTypeSymbol>()
                       .Where(typeSymbol => typeSymbol.TypeKind == TypeKind.Interface && !typeSymbol.IsImplicitlyDeclared)
                       .Where(typeSymbol => interfacePredicate == null || interfacePredicate(typeSymbol))
                       .Select(interfaceSymbol => new
            {
                InterfaceSymbol = interfaceSymbol,
                Compilation = compilation
            }))
                   .Select(
                       x =>
            {
                var @namespace = mockNamespaceSelector(x.InterfaceSymbol);
                var name = mockNameSelector(x.InterfaceSymbol);

                if (logSink.IsEnabled)
                {
                    logSink.Positive(
                        logSource,
                        "Generating mock for interface '{0}' with namespace '{1}', name '{2}'.",
                        x.InterfaceSymbol,
                        @namespace,
                        name);
                }

                var semanticModel = x.Compilation.GetSemanticModel(x.InterfaceSymbol.DeclaringSyntaxReferences.First().SyntaxTree);

                return GenerateMock(logSink, language, syntaxGenerator, semanticModel, x.InterfaceSymbol, @namespace, name, plugins);
            })
                   .Select((syntaxNode, i) => i == 0 ? syntaxGenerator.WithLeadingComments(syntaxNode, headerComment, language) : syntaxNode)
                   .ToImmutableList());
        }
Example #5
0
        /// <inheritdoc />
        public SyntaxNode GenerateConfigureBehavior(
            ILogSink logSink,
            SyntaxGenerator syntaxGenerator,
            SemanticModel semanticModel,
            ISymbol symbol)
        {
            logSink.Debug(logSource, "Considering symbol '{0}'.", symbol);

            var propertySymbol = symbol as IPropertySymbol;
            var methodSymbol   = symbol as IMethodSymbol;

            INamedTypeSymbol returnType = null;

            if (propertySymbol != null)
            {
                if (propertySymbol.GetMethod == null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a write-only property.", symbol);
                    return(null);
                }

                returnType = propertySymbol.GetMethod.ReturnType as INamedTypeSymbol;
            }
            else if (methodSymbol != null)
            {
                if (methodSymbol.AssociatedSymbol != null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a method with an associated symbol.", symbol);
                    return(null);
                }

                if (methodSymbol.IsGenericMethod)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a generic method.", symbol);
                    return(null);
                }

                returnType = methodSymbol.ReturnType as INamedTypeSymbol;
            }
            else
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it is neither a property nor a method.", symbol);
                return(null);
            }

            if (returnType == null)
            {
                logSink.Warn(logSource, "Ignoring symbol '{0}' because its return type could not be determined (it's probably a generic).", symbol);
                return(null);
            }

            var taskBaseType = semanticModel
                               .Compilation
                               .GetTypeByMetadataName("System.Threading.Tasks.Task");

            var genericTaskBaseType = semanticModel
                                      .Compilation
                                      .GetTypeByMetadataName("System.Threading.Tasks.Task`1");

            if (taskBaseType == null || genericTaskBaseType == null)
            {
                logSink.Warn(logSource, "Failed to resolve Task classes.");
                return(null);
            }

            var itType = semanticModel
                         .Compilation
                         .GetTypeByMetadataName("PCLMock.It");

            if (itType == null)
            {
                logSink.Error(logSource, "Failed to resolve It class.");
                return(null);
            }

            var isAnyMethod = itType
                              .GetMembers("IsAny")
                              .Single();

            if (isAnyMethod == null)
            {
                logSink.Error(logSource, "Failed to resolve IsAny method.");
                return(null);
            }

            var isGenericTask = returnType.IsGenericType && returnType.ConstructedFrom == genericTaskBaseType;
            var isTask        = returnType == taskBaseType;

            if (!isTask && !isGenericTask)
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it does not return a Task or Task<T>.", symbol);
                return(null);
            }

            ITypeSymbol taskType = semanticModel
                                   .Compilation
                                   .GetSpecialType(SpecialType.System_Boolean);

            if (isGenericTask)
            {
                taskType = returnType.TypeArguments[0];
            }

            var lambdaParameterName = symbol.GetUniqueName();

            SyntaxNode lambdaExpression;

            if (propertySymbol != null)
            {
                if (!propertySymbol.IsIndexer)
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x.SymbolName)
                    //         .Return(Task.FromResult(default(T)));
                    lambdaExpression = syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        propertySymbol.Name);
                }
                else
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x[It.IsAny<P1>(), It.IsAny<P2>() ...)
                    //         .Return(Task.FromResult(default(T)));
                    var whenArguments = propertySymbol
                                        .Parameters
                                        .Select(
                        parameter =>
                        syntaxGenerator.InvocationExpression(
                            syntaxGenerator.MemberAccessExpression(
                                syntaxGenerator.TypeExpression(itType),
                                syntaxGenerator.GenericName(
                                    "IsAny",
                                    typeArguments: new[]
                    {
                        parameter.Type
                    }))));

                    lambdaExpression = syntaxGenerator.ElementAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        arguments: whenArguments);
                }
            }
            else
            {
                // GENERATED CODE:
                //
                //     this
                //         .When(x => x.SymbolName(It.IsAny<P1>(), It.IsAny<P2>() ...)
                //         .Return(Task.FromResult(default(T)));
                var whenArguments = methodSymbol
                                    .Parameters
                                    .Select(
                    parameter =>
                    syntaxGenerator.InvocationExpression(
                        syntaxGenerator.MemberAccessExpression(
                            syntaxGenerator.TypeExpression(itType),
                            syntaxGenerator.GenericName(
                                "IsAny",
                                typeArguments: new[]
                {
                    parameter.Type
                }))));

                lambdaExpression = syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        methodSymbol.Name),
                    arguments: whenArguments);
            }

            var whenLambdaArgument = syntaxGenerator.ValueReturningLambdaExpression(
                lambdaParameterName,
                lambdaExpression);

            var whenInvocation = syntaxGenerator.InvocationExpression(
                syntaxGenerator.MemberAccessExpression(
                    syntaxGenerator.ThisExpression(),
                    syntaxGenerator.IdentifierName("When")),
                whenLambdaArgument);

            var fromResultInvocation = syntaxGenerator.InvocationExpression(
                syntaxGenerator.WithTypeArguments(
                    syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.TypeExpression(taskBaseType),
                        "FromResult"),
                    syntaxGenerator.TypeExpression(taskType)),
                arguments: new[]
            {
                syntaxGenerator.DefaultExpression(taskType)
            });

            var result = syntaxGenerator.ExpressionStatement(
                syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        whenInvocation,
                        syntaxGenerator.IdentifierName("Return")),
                    arguments: new[]
            {
                fromResultInvocation
            }));

            return(result);
        }
Example #6
0
        /// <inheritdoc />
        public SyntaxNode GenerateConfigureBehavior(
            ILogSink logSink,
            SyntaxGenerator syntaxGenerator,
            SemanticModel semanticModel,
            ISymbol symbol)
        {
            logSink.Debug(logSource, "Considering symbol '{0}'.", symbol);

            var propertySymbol = symbol as IPropertySymbol;
            var methodSymbol   = symbol as IMethodSymbol;

            INamedTypeSymbol returnType = null;

            if (propertySymbol != null)
            {
                if (propertySymbol.GetMethod == null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a write-only property.", symbol);
                    return(null);
                }

                returnType = propertySymbol.GetMethod.ReturnType as INamedTypeSymbol;
            }
            else if (methodSymbol != null)
            {
                if (methodSymbol.AssociatedSymbol != null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a method with an associated symbol.", symbol);
                    return(null);
                }

                if (methodSymbol.IsGenericMethod)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a generic method.", symbol);
                    return(null);
                }

                returnType = methodSymbol.ReturnType as INamedTypeSymbol;
            }
            else
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it is neither a property nor a method.", symbol);
                return(null);
            }

            if (returnType == null)
            {
                logSink.Warn(logSource, "Ignoring symbol '{0}' because its return type could not be determined (it's probably a generic).", symbol);
                return(null);
            }

            var disposableInterfaceType = semanticModel
                                          .Compilation
                                          .GetTypeByMetadataName("System.IDisposable");

            if (disposableInterfaceType == null)
            {
                logSink.Warn(logSource, "Failed to resolve IDisposable type.");
                return(null);
            }

            if (returnType != disposableInterfaceType)
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because its return type is not IDisposable.");
                return(null);
            }

            var disposableType = semanticModel
                                 .Compilation
                                 .GetTypeByMetadataName("System.Reactive.Disposables.Disposable");

            if (disposableType == null)
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because Disposable type could not be resolved (probably a missing reference to System.Reactive.Core).");
                return(null);
            }

            var itType = semanticModel
                         .Compilation
                         .GetTypeByMetadataName("PCLMock.It");

            if (itType == null)
            {
                logSink.Error(logSource, "Failed to resolve It class.");
                return(null);
            }

            var isAnyMethod = itType
                              .GetMembers("IsAny")
                              .Single();

            if (isAnyMethod == null)
            {
                logSink.Error(logSource, "Failed to resolve IsAny method.");
                return(null);
            }

            var lambdaParameterName = symbol.GetUniqueName();

            SyntaxNode lambdaExpression;

            if (propertySymbol != null)
            {
                if (!propertySymbol.IsIndexer)
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x.SymbolName)
                    //         .Return(Disposable.Empty);
                    lambdaExpression = syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        propertySymbol.Name);
                }
                else
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x[It.IsAny<P1>(), It.IsAny<P2>() ...)
                    //         .Return(Disposable.Empty);
                    var whenArguments = propertySymbol
                                        .Parameters
                                        .Select(
                        parameter =>
                        syntaxGenerator.InvocationExpression(
                            syntaxGenerator.MemberAccessExpression(
                                syntaxGenerator.TypeExpression(itType),
                                syntaxGenerator.GenericName(
                                    "IsAny",
                                    typeArguments: new[]
                    {
                        parameter.Type
                    }))));

                    lambdaExpression = syntaxGenerator.ElementAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        arguments: whenArguments);
                }
            }
            else
            {
                // GENERATED CODE:
                //
                //     this
                //         .When(x => x.SymbolName(It.IsAny<P1>(), It.IsAny<P2>() ...)
                //         .Return(Disposable.Empty);
                var whenArguments = methodSymbol
                                    .Parameters
                                    .Select(
                    parameter =>
                    syntaxGenerator.InvocationExpression(
                        syntaxGenerator.MemberAccessExpression(
                            syntaxGenerator.TypeExpression(itType),
                            syntaxGenerator.GenericName(
                                "IsAny",
                                typeArguments: new[]
                {
                    parameter.Type
                }))));

                lambdaExpression = syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        methodSymbol.Name),
                    arguments: whenArguments);
            }

            var whenLambdaArgument = syntaxGenerator.ValueReturningLambdaExpression(
                lambdaParameterName,
                lambdaExpression);

            var whenInvocation = syntaxGenerator.InvocationExpression(
                syntaxGenerator.MemberAccessExpression(
                    syntaxGenerator.ThisExpression(),
                    syntaxGenerator.IdentifierName("When")),
                whenLambdaArgument);

            var result = syntaxGenerator.ExpressionStatement(
                syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        whenInvocation,
                        syntaxGenerator.IdentifierName("Return")),
                    arguments: new[]
            {
                syntaxGenerator.MemberAccessExpression(
                    syntaxGenerator.TypeExpression(disposableType),
                    syntaxGenerator.IdentifierName("Empty"))
            }));

            return(result);
        }
Example #7
0
        /// <inheritdoc />
        public SyntaxNode GenerateConfigureBehavior(
            ILogSink logSink,
            SyntaxGenerator syntaxGenerator,
            SemanticModel semanticModel,
            ISymbol symbol)
        {
            logSink.Debug(logSource, "Considering symbol '{0}'.", symbol);

            var propertySymbol = symbol as IPropertySymbol;
            var methodSymbol   = symbol as IMethodSymbol;

            INamedTypeSymbol returnType = null;

            if (propertySymbol != null)
            {
                if (propertySymbol.GetMethod == null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a write-only property.", symbol);
                    return(null);
                }

                returnType = propertySymbol.GetMethod.ReturnType as INamedTypeSymbol;
            }
            else if (methodSymbol != null)
            {
                if (methodSymbol.AssociatedSymbol != null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a method with an associated symbol.", symbol);
                    return(null);
                }

                if (methodSymbol.IsGenericMethod)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a generic method.", symbol);
                    return(null);
                }

                returnType = methodSymbol.ReturnType as INamedTypeSymbol;
            }
            else
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it is neither a property nor a method.", symbol);
                return(null);
            }

            if (returnType == null)
            {
                logSink.Warn(logSource, "Ignoring symbol '{0}' because its return type could not be determined (it's probably a sgeneric).", symbol);
                return(null);
            }

            var observableInterfaceType = semanticModel
                                          .Compilation
                                          .GetTypeByMetadataName("System.IObservable`1");

            var observableType = semanticModel
                                 .Compilation
                                 .GetTypeByMetadataName("System.Reactive.Linq.Observable");

            if (observableInterfaceType == null)
            {
                logSink.Warn(logSource, "Failed to resolve System.IObservable<T>.");
                return(null);
            }

            if (observableType == null)
            {
                logSink.Warn(logSource, "Failed to resolve System.Reactive.Linq.Observable.");
                return(null);
            }

            var itType = semanticModel
                         .Compilation
                         .GetTypeByMetadataName("PCLMock.It");

            if (itType == null)
            {
                logSink.Error(logSource, "Failed to resolve It class.");
                return(null);
            }

            var isAnyMethod = itType
                              .GetMembers("IsAny")
                              .Single();

            if (isAnyMethod == null)
            {
                logSink.Error(logSource, "Failed to resolve IsAny method.");
                return(null);
            }

            var isObservable = returnType.IsGenericType && returnType.ConstructedFrom == observableInterfaceType;

            if (!isObservable)
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it does not return IObservable<T>.", symbol);
                return(null);
            }

            var observableInnerType = returnType.TypeArguments[0];
            var lambdaParameterName = symbol.GetUniqueName();

            SyntaxNode lambdaExpression;

            if (propertySymbol != null)
            {
                if (!propertySymbol.IsIndexer)
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x.SymbolName)
                    //         .Return(Observable.Empty<T>());
                    lambdaExpression = syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        propertySymbol.Name);
                }
                else
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x[It.IsAny<P1>(), It.IsAny<P2>() ...)
                    //         .Return(Observable.Empty<T>());
                    var whenArguments = propertySymbol
                                        .Parameters
                                        .Select(
                        parameter =>
                        syntaxGenerator.InvocationExpression(
                            syntaxGenerator.MemberAccessExpression(
                                syntaxGenerator.TypeExpression(itType),
                                syntaxGenerator.GenericName(
                                    "IsAny",
                                    typeArguments: new[]
                    {
                        parameter.Type
                    }))));

                    lambdaExpression = syntaxGenerator.ElementAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        arguments: whenArguments);
                }
            }
            else
            {
                // GENERATED CODE:
                //
                //     this
                //         .When(x => x.SymbolName(It.IsAny<P1>(), It.IsAny<P2>() ...)
                //         .Return(Observable.Return<T>(default(T)));
                var whenArguments = methodSymbol
                                    .Parameters
                                    .Select(
                    parameter =>
                    syntaxGenerator.InvocationExpression(
                        syntaxGenerator.MemberAccessExpression(
                            syntaxGenerator.TypeExpression(itType),
                            syntaxGenerator.GenericName(
                                "IsAny",
                                typeArguments: new[]
                {
                    parameter.Type
                }))));

                lambdaExpression = syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        methodSymbol.Name),
                    arguments: whenArguments);
            }

            var whenLambdaArgument = syntaxGenerator.ValueReturningLambdaExpression(
                lambdaParameterName,
                lambdaExpression);

            var whenInvocation = syntaxGenerator.InvocationExpression(
                syntaxGenerator.MemberAccessExpression(
                    syntaxGenerator.ThisExpression(),
                    syntaxGenerator.IdentifierName("When")),
                whenLambdaArgument);

            SyntaxNode observableInvocation;

            if (propertySymbol != null)
            {
                // properties are given collection semantics by returning Observable.Empty<T>()
                observableInvocation = syntaxGenerator.InvocationExpression(
                    syntaxGenerator.WithTypeArguments(
                        syntaxGenerator.MemberAccessExpression(
                            syntaxGenerator.TypeExpression(observableType),
                            "Empty"),
                        syntaxGenerator.TypeExpression(observableInnerType)));
            }
            else
            {
                // methods are given async operation semantics by returning Observable.Return(default(T))
                observableInvocation = syntaxGenerator.InvocationExpression(
                    syntaxGenerator.WithTypeArguments(
                        syntaxGenerator.MemberAccessExpression(
                            syntaxGenerator.TypeExpression(observableType),
                            "Return"),
                        syntaxGenerator.TypeExpression(observableInnerType)),
                    arguments: new[]
                {
                    syntaxGenerator.DefaultExpression(observableInnerType)
                });
            }

            var result = syntaxGenerator.ExpressionStatement(
                syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        whenInvocation,
                        syntaxGenerator.IdentifierName("Return")),
                    arguments: new[]
            {
                observableInvocation
            }));

            return(result);
        }
Example #8
0
        /// <inheritdoc />
        public SyntaxNode GenerateConfigureBehavior(
            ILogSink logSink,
            SyntaxGenerator syntaxGenerator,
            SemanticModel semanticModel,
            ISymbol symbol)
        {
            logSink.Debug(logSource, "Considering symbol '{0}'.", symbol);

            var propertySymbol = symbol as IPropertySymbol;
            var methodSymbol   = symbol as IMethodSymbol;

            INamedTypeSymbol returnType = null;

            if (propertySymbol != null)
            {
                if (propertySymbol.GetMethod == null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a write-only property.", symbol);
                    return(null);
                }

                returnType = propertySymbol.GetMethod.ReturnType as INamedTypeSymbol;
            }
            else if (methodSymbol != null)
            {
                if (methodSymbol.AssociatedSymbol != null)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a method with an associated symbol.", symbol);
                    return(null);
                }

                if (methodSymbol.IsGenericMethod)
                {
                    logSink.Debug(logSource, "Ignoring symbol '{0}' because it is a generic method.", symbol);
                    return(null);
                }

                returnType = methodSymbol.ReturnType as INamedTypeSymbol;
            }
            else
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it is neither a property nor a method.", symbol);
                return(null);
            }

            if (returnType == null)
            {
                logSink.Warn(logSource, "Ignoring symbol '{0}' because its return type could not be determined (it's probably a generic).", symbol);
                return(null);
            }

            if (!returnType.IsGenericType)
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because its return type is not a generic type, so it cannot be one of the supported collection types.");
                return(null);
            }

            SyntaxNode returnValueSyntax;

            if (!TryGetReturnValueSyntaxForEnumerableReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForCollectionReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForDictionaryReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForSetReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForImmutableListReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForImmutableDictionaryReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForImmutableQueueReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForImmutableSetReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax) &&
                !TryGetReturnValueSyntaxForImmutableStackReturnType(logSink, syntaxGenerator, semanticModel, returnType, out returnValueSyntax))
            {
                logSink.Debug(logSource, "Ignoring symbol '{0}' because it does not return a supported collection type.", symbol);
                return(null);
            }

            var itType = semanticModel
                         .Compilation
                         .GetTypeByMetadataName("PCLMock.It");

            if (itType == null)
            {
                logSink.Error(logSource, "Failed to resolve It class.");
                return(null);
            }

            var isAnyMethod = itType
                              .GetMembers("IsAny")
                              .Single();

            if (isAnyMethod == null)
            {
                logSink.Error(logSource, "Failed to resolve IsAny method.");
                return(null);
            }

            var lambdaParameterName = symbol.GetUniqueName();

            SyntaxNode lambdaExpression;

            if (propertySymbol != null)
            {
                if (!propertySymbol.IsIndexer)
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x.SymbolName)
                    //         .Return(returnValueSyntax);
                    lambdaExpression = syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        propertySymbol.Name);
                }
                else
                {
                    // GENERATED CODE:
                    //
                    //     this
                    //         .When(x => x[It.IsAny<P1>(), It.IsAny<P2>() ...)
                    //         .Return(returnValueSyntax);
                    var whenArguments = propertySymbol
                                        .Parameters
                                        .Select(
                        parameter =>
                        syntaxGenerator.InvocationExpression(
                            syntaxGenerator.MemberAccessExpression(
                                syntaxGenerator.TypeExpression(itType),
                                syntaxGenerator.GenericName(
                                    "IsAny",
                                    typeArguments: new[]
                    {
                        parameter.Type
                    }))));

                    lambdaExpression = syntaxGenerator.ElementAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        arguments: whenArguments);
                }
            }
            else
            {
                // GENERATED CODE:
                //
                //     this
                //         .When(x => x.SymbolName(It.IsAny<P1>(), It.IsAny<P2>() ...)
                //         .Return(returnValueSyntax);
                var whenArguments = methodSymbol
                                    .Parameters
                                    .Select(
                    parameter =>
                    syntaxGenerator.InvocationExpression(
                        syntaxGenerator.MemberAccessExpression(
                            syntaxGenerator.TypeExpression(itType),
                            syntaxGenerator.GenericName(
                                "IsAny",
                                typeArguments: new[]
                {
                    parameter.Type
                }))));

                lambdaExpression = syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        syntaxGenerator.IdentifierName(lambdaParameterName),
                        methodSymbol.Name),
                    arguments: whenArguments);
            }

            var whenLambdaArgument = syntaxGenerator.ValueReturningLambdaExpression(
                lambdaParameterName,
                lambdaExpression);

            var whenInvocation = syntaxGenerator.InvocationExpression(
                syntaxGenerator.MemberAccessExpression(
                    syntaxGenerator.ThisExpression(),
                    syntaxGenerator.IdentifierName("When")),
                whenLambdaArgument);

            var result = syntaxGenerator.ExpressionStatement(
                syntaxGenerator.InvocationExpression(
                    syntaxGenerator.MemberAccessExpression(
                        whenInvocation,
                        syntaxGenerator.IdentifierName("Return")),
                    arguments: new[]
            {
                returnValueSyntax
            }));

            return(result);
        }