/// <summary> /// Get the final name of the target method. If there's an existing method with the right /// message, level, and argument types, we just use that. Otherwise, we create a new method. /// </summary> internal async Task <(string methodName, bool existing)> GetFinalTargetMethodNameAsync( Document targetDoc, ClassDeclarationSyntax targetClass, Document invocationDoc, InvocationExpressionSyntax invocationExpression, FixDetails details, CancellationToken cancellationToken) { var invocationSM = (await invocationDoc.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false)) !; var invocationOp = (invocationSM.GetOperation(invocationExpression, cancellationToken) as IInvocationOperation) !; var docEditor = await DocumentEditor.CreateAsync(targetDoc, cancellationToken).ConfigureAwait(false); var sm = docEditor.SemanticModel; var comp = sm.Compilation; var loggerMessageAttribute = _getTypeByMetadataName2(comp, LoggerMessageAttribute); if (loggerMessageAttribute is null) { // strange that we can't find the attribute, but supply a potential useful value instead return(details.TargetMethodName, false); } var invocationArgList = MakeArgumentList(details, invocationOp); var conflict = false; var count = 2; string methodName; do { methodName = details.TargetMethodName; if (conflict) { methodName = $"{methodName}{count}"; count++; conflict = false; } foreach (var method in targetClass.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType <MethodDeclarationSyntax>()) { var methodSymbol = _getDeclaredSymbol(sm, method, cancellationToken); if (methodSymbol == null) { // hmmm, this shouldn't happen should it? continue; } var matchName = method.Identifier.ToString() == methodName; var matchParams = invocationArgList.Count == methodSymbol.Parameters.Length; if (matchParams) { for (int i = 0; i < invocationArgList.Count; i++) { matchParams = invocationArgList[i].Equals(methodSymbol.Parameters[i].Type, SymbolEqualityComparer.Default); if (!matchParams) { break; } } } if (matchName && matchParams) { conflict = true; } foreach (var mal in method.AttributeLists) { foreach (var ma in mal.Attributes) { var mattrSymbolInfo = sm.GetSymbolInfo(ma, cancellationToken); if (mattrSymbolInfo.Symbol is IMethodSymbol ms) { if (loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) { var arg = ma.ArgumentList !.Arguments[LoggerMessageAttrLevelArg]; var level = (LogLevel)sm.GetConstantValue(arg.Expression, cancellationToken).Value !; arg = ma.ArgumentList.Arguments[LoggerMessageAttrMessageArg]; var message = sm.GetConstantValue(arg.Expression, cancellationToken).ToString(); var matchMessage = message == details.Message; var matchLevel = FixDetails.GetLogLevelName(level) == details.Level; if (matchLevel && matchMessage && matchParams) { // found a match, use this one return(method.Identifier.ToString(), true); } break; } } } } } }while (conflict); return(methodName, false); }