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); } }
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); }