//  This is called extremely frequently
        // Analyze the method signature to validate binding attributes + types on the parameters.
        private void AnalyzeMethodDeclarationNode(SyntaxNodeAnalysisContext context)
        {
            if (_tooling == null) // Not yet initialized
            {
                return;
            }
            var methodDecl = (MethodDeclarationSyntax)context.Node;
            var methodName = methodDecl.Identifier.ValueText;

            if (!HasFunctionNameAttribute(context, methodDecl))
            {
                return;
            }

            // Go through
            var parameterList = methodDecl.ParameterList;

            foreach (ParameterSyntax parameterSyntax in parameterList.Parameters)
            {
                // No symbol for the parameter; just the parameter's type
                // Lazily do this - only do this if we're actually looking at a webjobs parameter.
                Type parameterType = null;

                // Now validate each parameter in the method.
                foreach (var attrListSyntax in parameterSyntax.AttributeLists)
                {
                    foreach (AttributeSyntax attributeSyntax in attrListSyntax.Attributes)
                    {
                        var sym = context.SemanticModel.GetSymbolInfo(attributeSyntax);

                        var sym2 = sym.Symbol;
                        if (sym2 == null)
                        {
                            return; // compilation error
                        }

                        try
                        {
                            // Major call to instantiate a reflection Binding attribute from a symbol.
                            // Need this so we can pass the attribute to the WebJob's binding engine.
                            // throws if fails to instantiate
                            Attribute attribute = Helpers.MakeAttr(_tooling, context.SemanticModel, attributeSyntax);
                            if (attribute == null)
                            {
                                continue;
                            }

                            // At this point, we know we're looking at a webjobs parameter.
                            if (parameterType == null)
                            {
                                parameterType = Helpers.GetParameterType(context, parameterSyntax);
                                if (parameterType == null)
                                {
                                    return; // errors in signature
                                }
                            }

                            // Report errors from invalid attribute properties.
                            ValidateAttribute(context, attribute, attributeSyntax);

                            // This is the major call into the WebJobs' binding engine to check for binding errors.
                            // Returns null if success, else a list of possible fixes.
                            var diagnosticHelper      = new DiagnosticHelper(_tooling);
                            ErrorSuggestions[] errors = diagnosticHelper.CheckBindingErrors(attribute, parameterType);

                            if (errors != null && errors.Length > 0)
                            {
                                var diagnostic = ErrorList.IllegalBindingType(
                                    parameterSyntax,
                                    attribute,
                                    parameterType,
                                    errors);

                                context.ReportDiagnostic(diagnostic);
                            }
                        }
                        catch (Exception e)
                        {
                            return;
                        }
                    }
                }
            }
        }
        // Validate an individual property on the attribute
        // propInfo is a property on the attribute.
        private void ValidateAttributeProperty(SyntaxNodeAnalysisContext context, Attribute attribute, PropertyInfo propInfo, AttributeArgumentSyntax attributeSyntax)
        {
            var value = propInfo.GetValue(attribute);
            var propertyAttributes = propInfo.GetCustomAttributes();

            // First validate [AutoResolve] and [AppSetting].
            // Then do validators.
            bool       isAutoResolve      = false;
            bool       isAppSetting       = false;
            MethodInfo validator          = null;
            Attribute  validatorAttribute = null;

            foreach (Attribute propertyAttribute in propertyAttributes)
            {
                // AutoResolve and AppSetting are exclusive.
                if (propertyAttribute.GetType() == typeof(Microsoft.Azure.WebJobs.Description.AutoResolveAttribute))
                {
                    isAutoResolve = true;
                }
                if (propertyAttribute.GetType() == typeof(Microsoft.Azure.WebJobs.Description.AppSettingAttribute))
                {
                    isAppSetting = true;
                }

                if (validator == null)
                {
                    validator          = propertyAttribute.GetType().GetMethod("Validate", new Type[] { typeof(object), typeof(string) });
                    validatorAttribute = propertyAttribute;
                }
            }

            // Now apply error checks in order.
            if (isAutoResolve)
            {
                // Value should parse with { } and %%
                try
                {
                    if (value is string valueStr)
                    {
                        var template = Microsoft.Azure.WebJobs.Host.Bindings.Path.BindingTemplate.FromString(valueStr);
                        if (template.HasParameters)
                        {
                            // The validator runs after the { } and %% are substituted.
                            // But {} and %% may be illegal characters, so we can't validate with them.
                            // So skip validation.
                            // TODO - could we have some "dummy" substitution so that we can still do validation?
                            return;
                        }
                    }
                }
                catch (FormatException e)
                {
                    // Parse error
                    var diagnostic = ErrorList.BadBindingExpressionSyntax(attributeSyntax, propInfo, value, e);
                    context.ReportDiagnostic(diagnostic);
                    return;
                }
            }
            else if (isAppSetting)
            {
                // TODO - validate the appsetting. In local.json? etc?
            }

            if (validator != null)
            {
                // Run Validators.
                //  If this is an autoresolve/appsetting, technically we should do the runtime substitution
                // for the %appsetting% and {key} tokens.

                // We'd like to get all attributes deriving from ValidationAttribute.
                // But that's net20, and the analyzer is net451, so we can't reference the right type.
                // Need to do a dynamic dispatch to ValidationAttribute.Validate(object,string).

                try
                {
                    //attr.Validate(value, propInfo.Name);
                    validator.Invoke(validatorAttribute, new object[] { value, propInfo.Name });
                }
                catch (TargetInvocationException te)
                {
                    var ex         = te.InnerException;
                    var diagnostic = ErrorList.FailedValidation(attributeSyntax, propInfo, value, ex);
                    context.ReportDiagnostic(diagnostic);
                }
            }
        }