private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
        {
            var invocation = context.Node as InvocationExpressionSyntax;
            var info       = context.SemanticModel.GetSymbolInfo(invocation);
            var method     = info.Symbol as IMethodSymbol;

            if (method == null)
            {
                return;
            }

            // is serilog even present in the compilation?
            var messageTemplateAttribute = context.SemanticModel.Compilation.GetTypeByMetadataName("Serilog.Core.MessageTemplateFormatMethodAttribute");

            if (messageTemplateAttribute == null)
            {
                return;
            }

            // is it a serilog logging method?
            var attributes    = method.GetAttributes();
            var attributeData = attributes.FirstOrDefault(x => x.AttributeClass == messageTemplateAttribute);

            if (attributeData == null)
            {
                return;
            }

            string messageTemplateName = attributeData.ConstructorArguments.First().Value as string;

            // check for errors in the MessageTemplate
            var arguments      = new List <SourceArgument>();
            var properties     = new List <PropertyToken>();
            var hasErrors      = false;
            var literalSpan    = default(TextSpan);
            var exactPositions = true;
            var stringText     = default(string);

            foreach (var argument in invocation.ArgumentList.Arguments)
            {
                var paramter = RoslynHelper.DetermineParameter(argument, context.SemanticModel, true, context.CancellationToken);
                if (paramter.Name == messageTemplateName)
                {
                    string messageTemplate;

                    // is it a simple string literal?
                    var literal = argument.Expression as LiteralExpressionSyntax;
                    if (literal == null)
                    {
                        // can we at least get a computed constant value for it?
                        var constantValue = context.SemanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                        if (!constantValue.HasValue || !(constantValue.Value is string))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(ConstantMessageTemplateRule, argument.Expression.GetLocation(), argument.Expression.ToString()));
                            continue;
                        }

                        // we can't map positions back from the computed string into the real positions
                        exactPositions  = false;
                        messageTemplate = constantValue.Value as string;
                    }
                    else
                    {
                        stringText     = literal.Token.Text;
                        exactPositions = true;

                        messageTemplate = literal.Token.ValueText;
                    }

                    literalSpan = argument.Expression.GetLocation().SourceSpan;

                    var messageTemplateDiagnostics = AnalyzingMessageTemplateParser.Analyze(messageTemplate);
                    foreach (var templateDiagnostic in messageTemplateDiagnostics)
                    {
                        var property = templateDiagnostic as PropertyToken;
                        if (property != null)
                        {
                            properties.Add(property);
                            continue;
                        }

                        var diagnostic = templateDiagnostic as MessageTemplateDiagnostic;
                        if (diagnostic != null)
                        {
                            hasErrors = true;
                            ReportDiagnostic(ref context, ref literalSpan, stringText, exactPositions, TemplateRule, diagnostic);
                        }
                    }
                }
                else if (paramter.Name.StartsWith("propertyValue", StringComparison.Ordinal))
                {
                    var location = argument.GetLocation().SourceSpan;
                    arguments.Add(new SourceArgument {
                        StartIndex = location.Start, Length = location.Length
                    });
                }
            }

            // do properties match up?
            if (!hasErrors && literalSpan != default(TextSpan) && (arguments.Count > 0 || properties.Count > 0))
            {
                var diagnostics = PropertyBindingAnalyzer.AnalyzeProperties(properties, arguments);
                foreach (var diagnostic in diagnostics)
                {
                    ReportDiagnostic(ref context, ref literalSpan, stringText, exactPositions, PropertyBindingRule, diagnostic);
                }

                // are there duplicate property names?
                var usedNames = new HashSet <string>();
                foreach (var property in properties)
                {
                    if (!property.IsPositional && !usedNames.Add(property.PropertyName))
                    {
                        ReportDiagnostic(ref context, ref literalSpan, stringText, exactPositions, UniquePropertyNameRule, new MessageTemplateDiagnostic(property.StartIndex, property.Length, property.PropertyName));
                    }
                }
            }

            // is this an overload where the exception argument is used?
            var exception = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Exception");

            if (method.Parameters.First().Type == exception)
            {
                return;
            }

            // is there an overload with the exception argument?
            if (!method.ContainingType.GetMembers().OfType <IMethodSymbol>().Any(x => x.Name == method.Name && x.Parameters.FirstOrDefault()?.Type == exception))
            {
                return;
            }

            // check wether any of the format arguments is an exception
            foreach (var argument in invocation.ArgumentList.Arguments)
            {
                var arginfo = context.SemanticModel.GetTypeInfo(argument.Expression);
                if (IsException(exception, arginfo.Type))
                {
                    context.ReportDiagnostic(Diagnostic.Create(ExceptionRule, argument.GetLocation(), argument.Expression.ToFullString()));
                }
            }
        }
        private static void TryAddLoggingLevelSwitch(SemanticModel semanticModel, IdentifierNameSyntax identifier, LoggerConfiguration configuration, CancellationToken cancellationToken)
        {
            string name = "$" + identifier.Identifier.Value;

            if (configuration.LevelSwitches.ContainsKey(name))
            {
                return;
            }

            var symbol = semanticModel.GetSymbolInfo(identifier, cancellationToken).Symbol;

            if (symbol == null)
            {
                configuration.AddError("Failed to analyze parameter", identifier);
                return;
            }

            var reference = symbol.DeclaringSyntaxReferences.FirstOrDefault() as SyntaxReference;

            if (reference == null)
            {
                configuration.AddError("Could not find declaration of LoggingLevelSwitch", identifier);
                return;
            }

            var declarator = reference.GetSyntax(cancellationToken) as VariableDeclaratorSyntax;

            if (declarator == null)
            {
                configuration.AddError("Could not find declaration of LoggingLevelSwitch", identifier);
                return;
            }

            var initializer = declarator.Initializer.Value as ObjectCreationExpressionSyntax;

            if (initializer == null)
            {
                configuration.AddError("Could not find initialization of LoggingLevelSwitch", identifier);
                return;
            }

            IParameterSymbol parameter;
            object           value;
            var argument = initializer.ArgumentList.Arguments.FirstOrDefault();

            if (argument == null)
            {
                var constructor = semanticModel.GetSymbolInfo(initializer, cancellationToken).Symbol;
                if (constructor == null)
                {
                    configuration.AddError("Could not analyze LoggingLevelSwitch constructor", identifier);
                    return;
                }

                parameter = (symbol as IMethodSymbol)?.Parameters.FirstOrDefault();
                if (parameter == null)
                {
                    configuration.AddError("Could not analyze LoggingLevelSwitch constructor", identifier);
                    return;
                }

                value = parameter.ExplicitDefaultValue;
            }
            else
            {
                parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, cancellationToken);
                if (parameter == null)
                {
                    configuration.AddError("Failed to analyze parameter", argument);
                    return;
                }

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

                value = constValue.Value;
            }

            long enumIntegralValue;

            try
            {
                enumIntegralValue = Convert.ToInt64(value);
            }
            catch
            {
                configuration.AddError($"Value {value} is not within expected range", argument);
                return;
            }

            // Roslyn returns enum constant values as integers, convert it back to the enum member name
            var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == enumIntegralValue);

            configuration.LevelSwitches.Add(name, enumMember.Name);
        }
        private static LoggerConfiguration GetLoggerConfigurationFromSyntax(CodeRefactoringContext context, SemanticModel semanticModel, List <MemberAccessExpressionSyntax> configurationProperties)
        {
            var configuration = new LoggerConfiguration();

            foreach (var property in configurationProperties)
            {
                var invokedMethod = property.Ancestors().FirstOrDefault() as MemberAccessExpressionSyntax;

                // Just in case we're looking at syntax that has similiar names to LoggerConfiguration (ReadFrom, WriteTo, ...) but isn't related to Serilog
                if (invokedMethod == null)
                {
                    continue;
                }

                if (String.IsNullOrEmpty(invokedMethod?.Name?.ToString()))
                {
                    configuration.AddError("Failed to get name of method", invokedMethod);
                    continue;
                }

                string configAction = property.Name.ToString();
                if (configAction == "MinimumLevel")
                {
                    string value;
                    var    logLevel = invokedMethod.Name.ToString();
                    if (logLevel == "Is")
                    {
                        // Ask roslyn what's the constant argument value passed to this method
                        var argument = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments.FirstOrDefault();
                        if (argument == null)
                        {
                            configuration.AddError("Can't get parameter value for MinimumLevel.Is(...)", invokedMethod);
                            continue;
                        }

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

                        var accessExpression = argument?.Expression as MemberAccessExpressionSyntax;
                        if (accessExpression == null)
                        {
                            configuration.AddError("Failed to analyze parameter", argument);
                            continue;
                        }

                        var constValue = semanticModel.GetConstantValue(accessExpression, context.CancellationToken);
                        if (!constValue.HasValue)
                        {
                            configuration.AddNonConstantError(argument);
                            continue;
                        }

                        long enumIntegralValue;
                        try
                        {
                            enumIntegralValue = Convert.ToInt64(constValue.Value);
                        }
                        catch
                        {
                            configuration.AddError($"Value {constValue.Value} is not within expected range", argument);
                            continue;
                        }

                        // Roslyn returns enum constant values as integers, convert it back to the enum member name
                        var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == enumIntegralValue);
                        value = enumMember.Name;
                    }
                    else if (logLevel == "Override")
                    {
                        var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

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

                            if (parameter.Name == "source")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    configuration.AddNonConstantError(argument.Expression);
                                    value = NotAConstantReplacementValue;
                                    continue;
                                }

                                key = constValue.Value?.ToString();
                            }
                            else if (parameter.Name == "minimumLevel")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    configuration.AddNonConstantError(argument.Expression);
                                    value = NotAConstantReplacementValue;
                                    continue;
                                }

                                var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == Convert.ToInt64(constValue.Value));
                                level = enumMember.Name;
                            }
                        }

                        if (key != null && level != null)
                        {
                            configuration.MinimumLevelOverrides[key] = level;
                        }
                        continue;
                    }
                    else if (logLevel == "ControlledBy")
                    {
                        var argument = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments.FirstOrDefault();
                        if (argument == null)
                        {
                            configuration.AddError("Can't get parameter value for MinimumLevel.ControlledBy(...)", invokedMethod);
                            continue;
                        }

                        var identifier = argument?.Expression as IdentifierNameSyntax;
                        if (identifier == null)
                        {
                            configuration.AddError("Failed to analyze parameter", argument);
                            continue;
                        }

                        TryAddLoggingLevelSwitch(semanticModel, identifier, configuration, context.CancellationToken);

                        configuration.MinimumLevelControlledBy = "$" + identifier.Identifier.Value;
                        continue;
                    }
                    else if (LogLevels.Contains(logLevel))
                    {
                        value = logLevel;
                    }
                    else
                    {
                        configuration.AddError("Unknown MinimumLevel method", invokedMethod);
                        continue;
                    }

                    configuration.MinimumLevel = value;
                }
                else if (configAction == "Enrich")
                {
                    if (invokedMethod.Name.ToString() == "WithProperty")
                    {
                        var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

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

                            if (parameter.Name == "name")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    configuration.AddNonConstantError(argument.Expression);
                                    value = NotAConstantReplacementValue;
                                    continue;
                                }

                                key = constValue.Value?.ToString();
                            }
                            else if (parameter.Name == "value")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    configuration.AddNonConstantError(argument.Expression);
                                    value = NotAConstantReplacementValue;
                                    continue;
                                }

                                value = constValue.Value?.ToString();
                            }
                        }

                        if (key != null && value != null)
                        {
                            configuration.EnrichWithProperty[key] = value;
                        }
                    }
                    else
                    {
                        AddExtensibleMethod(semanticModel, invokedMethod, configuration, configuration.Enrich.Add, context.CancellationToken);
                    }
                }
                else if (configAction == "Destructure")
                {
                    AddExtensibleMethod(semanticModel, invokedMethod, configuration, configuration.Destructure.Add, context.CancellationToken);
                }
                else if (configAction == "Filter")
                {
                    AddExtensibleMethod(semanticModel, invokedMethod, configuration, configuration.Filter.Add, context.CancellationToken);
                }
                else if (configAction == "WriteTo")
                {
                    AddExtensibleMethod(semanticModel, invokedMethod, configuration, configuration.WriteTo.Add, context.CancellationToken);
                }
                else if (configAction == "AuditTo")
                {
                    AddExtensibleMethod(semanticModel, invokedMethod, configuration, configuration.AuditTo.Add, context.CancellationToken);
                }
            }

            return(configuration);
        }
        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 AnalyzeSymbol(SyntaxNodeAnalysisContext context)
        {
            var invocation = context.Node as InvocationExpressionSyntax;
            var info       = context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken);
            var method     = info.Symbol as IMethodSymbol;

            if (method == null)
            {
                return;
            }

            // is serilog even present in the compilation?
            var messageTemplateAttribute = context.SemanticModel.Compilation.GetTypeByMetadataName("Serilog.Core.MessageTemplateFormatMethodAttribute");

            if (messageTemplateAttribute == null)
            {
                return;
            }

            // check if ForContext<T> / ForContext(typeof(T)) calls use the containing type as T
            if (method.Name == ForContext && method.ReturnType.ToString() == ILogger)
            {
                CheckForContextCorrectness(ref context, invocation, method);
            }

            // is it a serilog logging method?
            var attributes    = method.GetAttributes();
            var attributeData = attributes.FirstOrDefault(x => x.AttributeClass == messageTemplateAttribute);

            if (attributeData == null)
            {
                return;
            }

            string messageTemplateName = attributeData.ConstructorArguments.First().Value as string;

            // check for errors in the MessageTemplate
            var arguments           = default(List <SourceArgument>);
            var properties          = new List <PropertyToken>();
            var hasErrors           = false;
            var literalSpan         = default(TextSpan);
            var exactPositions      = true;
            var stringText          = default(string);
            var invocationArguments = invocation.ArgumentList.Arguments;

            foreach (var argument in invocationArguments)
            {
                var parameter = RoslynHelper.DetermineParameter(argument, context.SemanticModel, true, context.CancellationToken);
                if (parameter.Name == messageTemplateName)
                {
                    string messageTemplate;

                    // is it a simple string literal?
                    if (argument.Expression is LiteralExpressionSyntax literal)
                    {
                        stringText     = literal.Token.Text;
                        exactPositions = true;

                        messageTemplate = literal.Token.ValueText;
                    }
                    else
                    {
                        // can we at least get a computed constant value for it?
                        var constantValue = context.SemanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                        if (!constantValue.HasValue || !(constantValue.Value is string constString))
                        {
                            INamedTypeSymbol StringType() => context.SemanticModel.Compilation.GetTypeByMetadataName("System.String");

                            if (context.SemanticModel.GetSymbolInfo(argument.Expression, context.CancellationToken).Symbol is IFieldSymbol field && field.Name == "Empty" && field.Type == StringType())
                            {
                                constString = "";
                            }
                            else
                            {
                                context.ReportDiagnostic(Diagnostic.Create(ConstantMessageTemplateRule, argument.Expression.GetLocation(), argument.Expression.ToString()));
                                continue;
                            }
                        }

                        // we can't map positions back from the computed string into the real positions
                        exactPositions  = false;
                        messageTemplate = constString;
                    }

                    literalSpan = argument.Expression.GetLocation().SourceSpan;

                    var messageTemplateDiagnostics = AnalyzingMessageTemplateParser.Analyze(messageTemplate);
                    foreach (var templateDiagnostic in messageTemplateDiagnostics)
                    {
                        if (templateDiagnostic is PropertyToken property)
                        {
                            properties.Add(property);
                            continue;
                        }

                        if (templateDiagnostic is MessageTemplateDiagnostic diagnostic)
                        {
                            hasErrors = true;
                            ReportDiagnostic(ref context, ref literalSpan, stringText, exactPositions, TemplateRule, diagnostic);
                        }
                    }

                    var messageTemplateArgumentIndex = invocationArguments.IndexOf(argument);
                    arguments = invocationArguments.Skip(messageTemplateArgumentIndex + 1).Select(x =>
                    {
                        var location = x.GetLocation().SourceSpan;
                        return(new SourceArgument {
                            Argument = x, StartIndex = location.Start, Length = location.Length
                        });
                    }).ToList();

                    break;
                }
            }
Example #6
0
        private static LoggerConfiguration GetLoggerConfigurationFromSyntax(CodeRefactoringContext context, SemanticModel semanticModel, List <MemberAccessExpressionSyntax> configurationProperties)
        {
            var configuration = new LoggerConfiguration();

            foreach (var property in configurationProperties)
            {
                var invokedMethod = property.Ancestors().FirstOrDefault() as MemberAccessExpressionSyntax;

                if (String.IsNullOrEmpty(invokedMethod?.Name?.ToString()))
                {
                    continue;
                }

                string configAction = property.Name.ToString();
                if (configAction == "MinimumLevel")
                {
                    string value;
                    var    logLevel = invokedMethod.Name.ToString();
                    if (logLevel == "Is")
                    {
                        // Ask roslyn what's the constant argument value passed to this method
                        var argument = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments.FirstOrDefault();
                        if (argument == null)
                        {
                            continue;
                        }

                        var parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, context.CancellationToken);
                        if (parameter == null)
                        {
                            continue;
                        }

                        var accessExpression = argument?.Expression as MemberAccessExpressionSyntax;
                        if (accessExpression == null)
                        {
                            continue;
                        }

                        var constValue = semanticModel.GetConstantValue(accessExpression, context.CancellationToken);
                        if (!constValue.HasValue)
                        {
                            continue;
                        }

                        long enumIntegralValue;
                        try
                        {
                            enumIntegralValue = Convert.ToInt64(constValue.Value);
                        }
                        catch
                        {
                            continue;
                        }

                        // Roslyn returns enum constant values as integers, convert it back to the enum member name
                        var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == enumIntegralValue);
                        value = enumMember.Name;
                    }
                    else if (logLevel == "Override")
                    {
                        var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

                        string key   = null;
                        string level = null;
                        foreach (var argument in arguments)
                        {
                            var parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, context.CancellationToken);
                            if (parameter == null)
                            {
                                continue;
                            }

                            if (parameter.Name == "source")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    continue;
                                }

                                key = constValue.Value?.ToString();
                            }
                            else if (parameter.Name == "minimumLevel")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    continue;
                                }

                                var enumMember = parameter.Type.GetMembers().OfType <IFieldSymbol>().FirstOrDefault(x => Convert.ToInt64(x.ConstantValue) == Convert.ToInt64(constValue.Value));
                                level = enumMember.Name;
                            }
                        }

                        if (key != null && level != null)
                        {
                            configuration.MinimumLevelOverrides[key] = level;
                        }
                        continue;
                    }
                    else if (LogLevels.Contains(logLevel))
                    {
                        value = logLevel;
                    }
                    else
                    {
                        continue;
                    }

                    configuration.MinimumLevel = value;
                }
                else if (configAction == "Enrich")
                {
                    if (invokedMethod.Name.ToString() == "WithProperty")
                    {
                        var arguments = (invokedMethod?.Parent as InvocationExpressionSyntax)?.ArgumentList?.Arguments ?? default(SeparatedSyntaxList <ArgumentSyntax>);

                        string key   = null;
                        string value = null;
                        foreach (var argument in arguments)
                        {
                            var parameter = RoslynHelper.DetermineParameter(argument, semanticModel, false, context.CancellationToken);
                            if (parameter == null)
                            {
                                continue;
                            }

                            if (parameter.Name == "name")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    continue;
                                }

                                key = constValue.Value?.ToString();
                            }
                            else if (parameter.Name == "value")
                            {
                                var constValue = semanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                                if (!constValue.HasValue)
                                {
                                    continue;
                                }

                                value = constValue.Value?.ToString();
                            }
                        }

                        if (key != null && value != null)
                        {
                            configuration.EnrichWithProperty[key] = value;
                        }
                    }
                    else
                    {
                        var method = GetExtensibleMethod(semanticModel, invokedMethod, context.CancellationToken);
                        if (method != null)
                        {
                            configuration.Enrich.Add(method);
                        }
                    }
                }
                else if (configAction == "WriteTo")
                {
                    var method = GetExtensibleMethod(semanticModel, invokedMethod, context.CancellationToken);
                    if (method != null)
                    {
                        configuration.WriteTo.Add(method);
                    }
                }
                else if (configAction == "AuditTo")
                {
                    var method = GetExtensibleMethod(semanticModel, invokedMethod, context.CancellationToken);
                    if (method != null)
                    {
                        configuration.AuditTo.Add(method);
                    }
                }
            }

            return(configuration);
        }
Example #7
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 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);
        }