private static void ConvertExtensibleMethod(List <XElement> configEntries, ExtensibleMethod enrichment, string prefix)
        {
            string key = $"{prefix}:{enrichment.MethodName}";

            if (!enrichment.Arguments.Any())
            {
                AddEntry(configEntries, key, null);
                return;
            }

            foreach (var argument in enrichment.Arguments)
            {
                AddEntry(configEntries, $"{key}.{argument.Key}", argument.Value);
            }
        }
Exemple #2
0
        private static ExtensibleMethod GetExtensibleMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax invokedMethod, CancellationToken cancellationToken)
        {
            var method = new ExtensibleMethod
            {
                AssemblyName = semanticModel.GetSymbolInfo(invokedMethod).Symbol?.ContainingAssembly?.Name,
                MethodName   = invokedMethod.Name.ToString()
            };

            if (String.IsNullOrEmpty(method.AssemblyName))
            {
                return(null);
            }

            var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

            foreach (var argument in arguments)
            {
                var parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, cancellationToken);
                if (parameter == null)
                {
                    continue;
                }

                if (parameter.Type?.TypeKind == TypeKind.Interface)
                {
                    var objectCreation = argument.Expression as ObjectCreationExpressionSyntax;
                    // check if there are explicit arguments which are unsupported
                    if (objectCreation.ArgumentList?.Arguments.Count > 0)
                    {
                        continue;
                    }

                    var typeInfo = semanticModel.GetTypeInfo(objectCreation).Type as INamedTypeSymbol;
                    if (typeInfo == null)
                    {
                        continue;
                    }

                    // generate the assembly qualified name for usage with Type.GetType(string)
                    string name = GetAssemblyQualifiedTypeName(typeInfo);
                    method.Arguments[parameter.Name] = name;
                    continue;
                }

                var constValue = semanticModel.GetConstantValue(argument.Expression, cancellationToken);
                if (!constValue.HasValue)
                {
                    continue;
                }

                string value = null;
                if (parameter.Type.TypeKind == TypeKind.Enum)
                {
                    var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == Convert.ToInt64(constValue.Value));
                    value = enumMember.Name;
                }
                else
                {
                    value = constValue.Value?.ToString();
                }

                method.Arguments[parameter.Name] = value;
            }

            return(method);
        }
        private static void AddExtensibleMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax invokedMethod, LoggerConfiguration configuration, Action <ExtensibleMethod> addMethod, CancellationToken cancellationToken)
        {
            var methodSymbol = semanticModel.GetSymbolInfo(invokedMethod).Symbol as IMethodSymbol;
            var method       = new ExtensibleMethod
            {
                AssemblyName = methodSymbol?.ContainingAssembly?.Name,
                MethodName   = invokedMethod.Name.Identifier.ToString()
            };

            if (String.IsNullOrEmpty(method.AssemblyName))
            {
                configuration.AddError("Failed to get semantic informations for this method", invokedMethod);
                return;
            }

            // Check for explicitly given type arguments that are not part of the normal arguments
            if (methodSymbol.TypeArguments.Length > 0 && methodSymbol.TypeArguments.Length == methodSymbol.TypeParameters.Length)
            {
                for (int i = 0; i < methodSymbol.TypeArguments.Length; i++)
                {
                    var typeParamter = methodSymbol.TypeParameters[i];
                    var typeArgument = methodSymbol.TypeArguments[i];

                    if (methodSymbol.Parameters.Any(x => x.Type == typeParamter))
                    {
                        continue;
                    }

                    // Synthesize an System.Type argument if a generic version was used
                    switch (typeParamter.Name)
                    {
                    case "TSink":     // WriteTo/AuditTo.Sink<TSink>(...)
                        method.Arguments["sink"] = GetAssemblyQualifiedTypeName(typeArgument);
                        break;

                    case "TEnricher":     // Enrich.With<TEnricher>()
                        method.Arguments["enricher"] = GetAssemblyQualifiedTypeName(typeArgument);
                        break;

                    case "TFilter":     // Filter.With<TFilter>()
                        method.Arguments["filter"] = GetAssemblyQualifiedTypeName(typeArgument);
                        break;

                    case "TDestructuringPolicy":     // Destructure.With<TDestructuringPolicy>()
                        method.Arguments["policy"] = GetAssemblyQualifiedTypeName(typeArgument);
                        break;

                    case "TScalar":     // Destructure.AsScalar<TScalar>()
                        method.Arguments["scalarType"] = GetAssemblyQualifiedTypeName(typeArgument);
                        break;
                    }
                }
            }

            var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

            foreach (var argument in arguments)
            {
                var parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, cancellationToken);
                if (parameter == null)
                {
                    configuration.AddError("Failed to analyze parameter", argument);
                    continue;
                }

                string parameterName = parameter.Name;

                // Configuration Surrogates
                if (method.MethodName == "Sink" && parameterName == "logEventSink")               // WriteTo/AuditTo.Sink(ILogEventSink logEventSink, ...)
                {
                    parameterName = "sink";                                                       // Sink(this LoggerSinkConfiguration loggerSinkConfiguration, ILogEventSink sink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null)
                }
                else if (method.MethodName == "With" && parameterName == "filters")               // Filter.With(params ILogEventFilter[] filters)
                {
                    parameterName = "filter";                                                     // With(this LoggerFilterConfiguration loggerFilterConfiguration, ILogEventFilter filter)
                }
                else if (method.MethodName == "With" && parameterName == "destructuringPolicies") // Destructure.With(params IDestructuringPolicy[] destructuringPolicies)
                {
                    parameterName = "policy";                                                     // With(this LoggerDestructuringConfiguration loggerDestructuringConfiguration, IDestructuringPolicy policy)
                }
                else if (method.MethodName == "With" && parameterName == "enrichers")             // Enrich.With(params ILogEventEnricher[] enrichers)
                {
                    parameterName = "enricher";                                                   // With(this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, ILogEventEnricher enricher)
                }

                ITypeSymbol type = parameter.Type;
                if (parameter.IsParams && type is IArrayTypeSymbol array)
                {
                    type = array.ElementType;
                }

                if (type.ToString() == "System.Type")
                {
                    method.Arguments[parameterName] = NotAConstantReplacementValue;

                    var typeofExpression = argument.Expression as TypeOfExpressionSyntax;
                    if (typeofExpression == null)
                    {
                        configuration.AddError("I need a typeof(T) expression for Type arguments", argument.Expression);
                        continue;
                    }

                    var typeInfo = semanticModel.GetTypeInfo(typeofExpression.Type).Type as INamedTypeSymbol;
                    if (typeInfo == null)
                    {
                        configuration.AddError("Failed to get semantic informations for typeof expression", typeofExpression);
                        return;
                    }

                    // generate the assembly qualified name for usage with Type.GetType(string)
                    string name = GetAssemblyQualifiedTypeName(typeInfo);
                    method.Arguments[parameterName] = name;
                    continue;
                }
                else if (type.TypeKind == TypeKind.Interface || type.TypeKind == TypeKind.Class && type.IsAbstract)
                {
                    method.Arguments[parameterName] = NotAConstantReplacementValue;

                    var expressionSymbol = semanticModel.GetSymbolInfo(argument.Expression).Symbol;
                    if (expressionSymbol != null && (expressionSymbol.Kind == SymbolKind.Property || expressionSymbol.Kind == SymbolKind.Field))
                    {
                        if (!expressionSymbol.IsStatic)
                        {
                            configuration.AddError("Only static fields and properties can be used", argument.Expression);
                            continue;
                        }

                        if (expressionSymbol.DeclaredAccessibility != Accessibility.Public || expressionSymbol is IPropertySymbol property && property.GetMethod.DeclaredAccessibility != Accessibility.Public)
                        {
                            configuration.AddError("Fields and properties must be public and properties must have public getters", argument.Expression);
                            continue;
                        }

                        method.Arguments[parameterName] = GetAssemblyQualifiedTypeName(expressionSymbol.ContainingType, "::" + expressionSymbol.Name);
                        continue;
                    }

                    var objectCreation = argument.Expression as ObjectCreationExpressionSyntax;
                    if (objectCreation == null)
                    {
                        configuration.AddError("I can only infer types from `new T()` expressions", argument.Expression);
                        continue;
                    }

                    // check if there are explicit arguments which are unsupported
                    if (objectCreation.ArgumentList?.Arguments.Count > 0)
                    {
                        configuration.AddError("The configuration supports only parameterless constructors for interface or abstract type parameters", argument.Expression);
                        continue;
                    }

                    var typeInfo = semanticModel.GetTypeInfo(objectCreation).Type as INamedTypeSymbol;
                    if (typeInfo == null)
                    {
                        configuration.AddError("Failed to get semantic informations for this constructor", objectCreation);
                        return;
                    }

                    // generate the assembly qualified name for usage with Type.GetType(string)
                    string name = GetAssemblyQualifiedTypeName(typeInfo);
                    method.Arguments[parameterName] = name;
                    continue;
                }
                else if (type.ToString() == "Serilog.Core.LoggingLevelSwitch")
                {
                    var identifier = argument?.Expression as IdentifierNameSyntax;
                    if (identifier == null)
                    {
                        configuration.AddError("Failed to analyze parameter", argument);
                        continue;
                    }

                    TryAddLoggingLevelSwitch(semanticModel, identifier, configuration, cancellationToken);

                    method.Arguments[parameterName] = "$" + identifier.Identifier.Value;
                    continue;
                }

                string value      = null;
                var    constValue = semanticModel.GetConstantValue(argument.Expression, cancellationToken);
                if (!constValue.HasValue)
                {
                    configuration.AddNonConstantError(argument.Expression);
                    value = NotAConstantReplacementValue;
                }
                else
                {
                    if (type.TypeKind == TypeKind.Enum)
                    {
                        var enumMember = type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == Convert.ToInt64(constValue.Value));
                        value = enumMember.Name;
                    }
                    else
                    {
                        value = constValue.Value?.ToString();
                    }
                }

                method.Arguments[parameterName] = value;
            }

            addMethod(method);
        }
        private static void AddExtensibleMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax invokedMethod, LoggerConfiguration configuration, Action <ExtensibleMethod> addMethod, CancellationToken cancellationToken)
        {
            var method = new ExtensibleMethod
            {
                AssemblyName = semanticModel.GetSymbolInfo(invokedMethod).Symbol?.ContainingAssembly?.Name,
                MethodName   = invokedMethod.Name.ToString()
            };

            if (String.IsNullOrEmpty(method.AssemblyName))
            {
                configuration.AddError("Failed to get semantic informations for this method", invokedMethod);
                return;
            }

            var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

            foreach (var argument in arguments)
            {
                var parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, cancellationToken);
                if (parameter == null)
                {
                    configuration.AddError("Failed to analyze parameter", argument);
                    continue;
                }

                if (parameter.Type?.TypeKind == TypeKind.Interface)
                {
                    method.Arguments[parameter.Name] = NotAConstantReplacementValue;

                    var objectCreation = argument.Expression as ObjectCreationExpressionSyntax;
                    if (objectCreation == null)
                    {
                        configuration.AddError("I can only infer types from `new T()` expressions", argument.Expression);
                        continue;
                    }

                    // check if there are explicit arguments which are unsupported
                    if (objectCreation.ArgumentList?.Arguments.Count > 0)
                    {
                        configuration.AddError("The configuration supports only parameterless constructors for interface parameters", argument.Expression);
                        continue;
                    }

                    var typeInfo = semanticModel.GetTypeInfo(objectCreation).Type as INamedTypeSymbol;
                    if (typeInfo == null)
                    {
                        configuration.AddError("Failed to get semantic informations for this constructor", objectCreation);
                        return;
                    }

                    // generate the assembly qualified name for usage with Type.GetType(string)
                    string name = GetAssemblyQualifiedTypeName(typeInfo);
                    method.Arguments[parameter.Name] = name;
                    continue;
                }

                string value      = null;
                var    constValue = semanticModel.GetConstantValue(argument.Expression, cancellationToken);
                if (!constValue.HasValue)
                {
                    configuration.AddNonConstantError(argument.Expression);
                    value = NotAConstantReplacementValue;
                }
                else
                {
                    if (parameter.Type.TypeKind == TypeKind.Enum)
                    {
                        var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == Convert.ToInt64(constValue.Value));
                        value = enumMember.Name;
                    }
                    else
                    {
                        value = constValue.Value?.ToString();
                    }
                }

                method.Arguments[parameter.Name] = value;
            }

            addMethod(method);
        }