private static void AnalyzeArgument(
            SyntaxNodeAnalysisContext context,
            ArgumentSyntax argument,
            ISymbol constantAttribute
            )
        {
            // Get the associated parameter
            var parameter = argument.DetermineParameter(
                context.SemanticModel,
                allowParams: false
                );

            // Parameter is somehow null, so do nothing
            if (parameter == null)
            {
                return;
            }

            // Parameter is not [Constant], so do nothing
            if (!HasAttribute(parameter, constantAttribute))
            {
                return;
            }

            // Argument is a constant value, so do nothing
            if (context.SemanticModel.GetConstantValue(argument.Expression).HasValue)
            {
                return;
            }

            // Argument was defined as [Constant] already, so trust it
            var argumentSymbol = context.SemanticModel.GetSymbolInfo(argument.Expression).Symbol;

            if (argumentSymbol != null && HasAttribute(argumentSymbol, constantAttribute))
            {
                return;
            }

            // Argument is not constant, so report it
            context.ReportDiagnostic(
                Diagnostic.Create(
                    descriptor: Diagnostics.NonConstantPassedToConstantParameter,
                    location: argument.GetLocation(),
                    messageArgs: parameter.Name
                    )
                );
        }
        private static void AddCastAccordingToParameterType(
            CodeRefactoringContext context,
            ArgumentSyntax argument,
            SemanticModel semanticModel)
        {
            ITypeSymbol typeSymbol = semanticModel.GetTypeInfo(argument.Expression).ConvertedType;

            if (typeSymbol == null)
            {
                return;
            }

            IParameterSymbol parameterSymbol = argument.DetermineParameter(
                semanticModel,
                allowParams: false,
                allowCandidate: true,
                cancellationToken: context.CancellationToken);

            if (parameterSymbol == null)
            {
                return;
            }

            if (typeSymbol.Equals(parameterSymbol.Type))
            {
                return;
            }

            context.RegisterRefactoring(
                $"Add cast to '{parameterSymbol.Type.ToDisplayString(TypeSyntaxRefactoring.SymbolDisplayFormat)}'",
                cancellationToken =>
            {
                return(AddCastRefactoring.RefactorAsync(
                           context.Document,
                           argument.Expression,
                           parameterSymbol.Type,
                           cancellationToken));
            });
        }
        private static ArgumentSyntax AddParameterName(
            ArgumentSyntax argument,
            SemanticModel semanticModel,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (argument.NameColon == null || argument.NameColon.IsMissing)
            {
                IParameterSymbol parameterSymbol = argument.DetermineParameter(
                    semanticModel,
                    allowParams: false,
                    cancellationToken: cancellationToken);

                if (parameterSymbol != null)
                {
                    return(argument
                           .WithNameColon(
                               NameColon(parameterSymbol.ToDisplayString(_symbolDisplayFormat))
                               .WithTrailingTrivia(Space))
                           .WithTriviaFrom(argument));
                }
            }

            return(argument);
        }
        public static void AddOrRemoveArgumentName(
            CodeRefactoringContext context,
            ArgumentSyntax argument,
            SemanticModel semanticModel)
        {
            if (argument == null)
            {
                throw new ArgumentNullException(nameof(argument));
            }

            if (semanticModel == null)
            {
                throw new ArgumentNullException(nameof(semanticModel));
            }

            if (argument.NameColon == null)
            {
                IParameterSymbol parameterSymbol = argument.DetermineParameter(
                    semanticModel,
                    allowParams: false,
                    cancellationToken: context.CancellationToken);

                if (parameterSymbol != null)
                {
                    context.RegisterRefactoring(
                        "Add parameter name",
                        cancellationToken => AddParameterNameAsync(context.Document, argument, parameterSymbol, cancellationToken));
                }
            }
            else if (!argument.NameColon.IsMissing)
            {
                context.RegisterRefactoring(
                    "Remove parameter name",
                    cancellationToken => RemoveParameterNameAsync(context.Document, argument, cancellationToken));
            }
        }
        public static ArgumentSyntax AddParameterName(
            ArgumentSyntax argument,
            SemanticModel semanticModel,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (argument == null)
            {
                throw new ArgumentNullException(nameof(argument));
            }

            if (semanticModel == null)
            {
                throw new ArgumentNullException(nameof(semanticModel));
            }

            if (argument.NameColon != null && !argument.NameColon.IsMissing)
            {
                return(argument);
            }

            IParameterSymbol parameterSymbol = argument.DetermineParameter(
                semanticModel,
                allowParams: false,
                cancellationToken: cancellationToken);

            if (parameterSymbol == null)
            {
                return(argument);
            }

            return(argument
                   .WithNameColon(
                       NameColon(parameterSymbol.ToDisplayString(_symbolDisplayFormat))
                       .WithTrailingTrivia(Space))
                   .WithTriviaFrom(argument));
        }
        private static void AnalyzeArgument(
            SyntaxNodeAnalysisContext context,
            ISymbol statelessFuncAttribute,
            ImmutableHashSet <ISymbol> statelessFuncs
            )
        {
            ArgumentSyntax syntax = context.Node as ArgumentSyntax;

            SemanticModel model = context.SemanticModel;

            IParameterSymbol param = syntax.DetermineParameter(model);

            if (param == null)
            {
                return;
            }

            ImmutableArray <AttributeData> paramAttributes = param.GetAttributes();

            if (!paramAttributes.Any(a => a.AttributeClass == statelessFuncAttribute))
            {
                return;
            }

            ExpressionSyntax argument = syntax.Expression;

            /**
             * Even though we haven't specifically accounted for its
             * source if it's a StatelessFunc<T> we're reasonably
             * certain its been analyzed.
             */
            ISymbol type = model.GetTypeInfo(argument).Type;

            if (type != null && IsStatelessFunc(type, statelessFuncs))
            {
                return;
            }

            Diagnostic diag;
            SyntaxKind kind = argument.Kind();

            switch (kind)
            {
            // this is the case when a method reference is used
            // eg Func<string, int> func = int.Parse
            case SyntaxKind.SimpleMemberAccessExpression:
                if (IsStaticMemberAccess(context, argument))
                {
                    return;
                }

                // non-static member access means that state could
                // be used / held.
                // TODO: Look for [Immutable] on the member's type
                // to determine if the non-static member is safe
                diag = Diagnostic.Create(
                    Diagnostics.StatelessFuncIsnt,
                    argument.GetLocation(),
                    $"{ argument.ToString() } is not static"
                    );
                break;

            // this is the case when a "delegate" is used
            // eg delegate( int x, int y ) { return x + y; }
            case SyntaxKind.AnonymousMethodExpression:
            // this is the case when the left hand side of the
            // lambda has parens
            // eg () => 1, (x, y) => x + y
            case SyntaxKind.ParenthesizedLambdaExpression:
            // this is the case when the left hand side of the
            // lambda does not have parens
            // eg x => x + 1
            case SyntaxKind.SimpleLambdaExpression:
                bool hasCaptures = TryGetCaptures(
                    context,
                    argument,
                    out ImmutableArray <ISymbol> captures
                    );
                if (!hasCaptures)
                {
                    return;
                }

                string captured = string.Join(", ", captures.Select(c => c.Name));
                diag = Diagnostic.Create(
                    Diagnostics.StatelessFuncIsnt,
                    argument.GetLocation(),
                    $"Captured variable(s): { captured }"
                    );
                break;

            // this is the case where an expression is invoked,
            // which returns a Func<T>
            // eg ( () => { return () => 1 } )()
            case SyntaxKind.InvocationExpression:
                // we are rejecting this because it is tricky to
                // analyze properly, but also a bit ugly and should
                // never really be necessary
                diag = Diagnostic.Create(
                    Diagnostics.StatelessFuncIsnt,
                    argument.GetLocation(),
                    $"Invocations are not allowed: { argument.ToString() }"
                    );

                break;

            /**
             * This is the case where a variable is passed in
             * eg Foo( f )
             * Where f might be a local variable, a parameter, or field
             *
             * class C<T> {
             *   StatelessFunc<T> m_f;
             *
             *   void P( StatelessFunc<T> f ) { Foo( f ); }
             *   void Q( [StatelessFunc] Func<T> f ) { Foo( f ); }
             *   void R() { StatelessFunc<T> f; Foo( f ); }
             *   void S() { Foo( m_f ); }
             *   void T() : this( StaticMemberMethod ) {}
             * }
             */
            case SyntaxKind.IdentifierName:
                /**
                 * If it's a local parameter marked with [StatelessFunc] we're reasonably
                 * certain it was analyzed on the caller side.
                 */
                if (IsParameterMarkedStateless(
                        model,
                        statelessFuncAttribute,
                        argument as IdentifierNameSyntax,
                        context.CancellationToken
                        ))
                {
                    return;
                }

                if (IsStaticMemberAccess(
                        context,
                        argument as IdentifierNameSyntax
                        ))
                {
                    return;
                }

                /**
                 * If it's any other variable. We're not sure
                 */
                diag = Diagnostic.Create(
                    Diagnostics.StatelessFuncIsnt,
                    argument.GetLocation(),
                    $"Unable to determine if { argument.ToString() } is stateless."
                    );

                break;

            default:
                // we need StatelessFunc<T> to be ultra safe, so we'll
                // reject usages we do not understand yet
                diag = Diagnostic.Create(
                    Diagnostics.StatelessFuncIsnt,
                    argument.GetLocation(),
                    $"Unable to determine safety of { argument.ToString() }. This is an unexpectes usage of StatelessFunc<T>"
                    );

                break;
            }

            context.ReportDiagnostic(diag);
        }
        /// <summary>
        /// Get the arguments which are unnamed and not "params"
        /// </summary>
        private static IEnumerable <ArgParamBinding> GetUnnamedArgs(
            SemanticModel model,
            ArgumentListSyntax args
            )
        {
            for (int idx = 0; idx < args.Arguments.Count; idx++)
            {
                ArgumentSyntax arg = args.Arguments[idx];

                // Ignore args that already have names
                if (arg.NameColon != null)
                {
                    continue;
                }

                IParameterSymbol param = arg.DetermineParameter(
                    model,

                    // Don't map params arguments. It's okay that they are
                    // unnamed. Some things like ImmutableArray.Create() could
                    // take a large number of args and we don't want anything to
                    // be named. Named params really suck so we may still
                    // encourage it but APIs that take params and many other
                    // args would suck anyway.
                    allowParams: false
                    );

                // Not sure if this can happen but it'd be hard to name this
                // param so ignore it.
                if (param == null)
                {
                    continue;
                }

                // IParameterSymbol.Name is documented to be possibly empty in
                // which case it is "unnamed", so ignore it.
                if (param.Name == "")
                {
                    continue;
                }

                // C# allows us to create variables with the same names as reserved keywords,
                // as long as we prefix with @ (e.g. @int is a valid identifier)
                // So any parameters which are reserved must have the @ prefix
                string     paramName;
                SyntaxKind paramNameKind = SyntaxFacts.GetKeywordKind(param.Name);
                if (SyntaxFacts.GetReservedKeywordKinds().Any(reservedKind => reservedKind == paramNameKind))
                {
                    paramName = "@" + param.Name;
                }
                else
                {
                    paramName = param.Name;
                }

                string psuedoName = GetPsuedoName(arg);
                if (psuedoName != null)
                {
                    bool matchesParamName = string.Equals(
                        psuedoName,
                        param.Name,
                        StringComparison.OrdinalIgnoreCase
                        );

                    if (matchesParamName)
                    {
                        continue;
                    }
                }

                yield return(new ArgParamBinding(
                                 position: idx,
                                 paramName: paramName,        // Use the verbatim parameter name if applicable
                                 syntax: arg
                                 ));
            }
        }