Exemple #1
0
        /// <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);
        }