private static bool TryGetParameterType(ParameterPair pair, out string typeName)
        {
            if (pair.Template?.Type is TextAndLocation templateType &&
                pair.Method is IParameterSymbol parameter)
            {
                switch (templateType.Text)
                {
                // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.2#route-constraint-reference
                case "bool" when parameter.Type != KnownSymbol.Boolean:
                case "decimal" when parameter.Type != KnownSymbol.Decimal:
                case "double" when parameter.Type != KnownSymbol.Float:
                case "float" when parameter.Type != KnownSymbol.Double:
                case "int" when parameter.Type != KnownSymbol.Int32:
                case "long" when parameter.Type != KnownSymbol.Int64:
                    typeName = templateType.Text;
                    return(true);

                case "datetime" when parameter.Type != KnownSymbol.DateTime:
                    typeName = "System.DateTime";
                    return(true);

                case "guid" when parameter.Type != KnownSymbol.Guid:
                    typeName = "System.Guid";
                    return(true);

                case "alpha" when parameter.Type != KnownSymbol.String:
                    typeName = "string";
                    return(true);
                }
            }

            typeName = null;
            return(false);
        }
        private static bool HasWrongType(ParameterPair pair, out string correctType, out Location constraintLocation, out string correctConstraint)
        {
            if (pair.Route is TemplateParameter templateParameter &&
                templateParameter.Constraints is ImmutableArray <RouteConstraint> constraints &&
                pair.Symbol is IParameterSymbol parameterSymbol)
            {
                foreach (var constraint in constraints)
                {
                    // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.2#route-constraint-reference
                    if (TryGetType(constraint.Span, out var type))
                    {
                        correctType        = parameterSymbol.Type == type ? null : type.Alias ?? type.FullName;
                        constraintLocation = constraint.Span.GetLocation();
                        correctConstraint  = GetCorrectConstraintType(constraint);
                        return(correctType != null);
                    }

                    if (constraint.Span.Equals("?", StringComparison.Ordinal) &&
                        parameterSymbol.Type.IsValueType &&
                        parameterSymbol.Type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T)
                    {
                        correctType        = parameterSymbol.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) + "?";
                        constraintLocation = constraint.Span.GetLocation();
                        correctConstraint  = string.Empty;
                        return(true);
                    }
                }

                if (!constraints.TryFirst(x => x.Span.Equals("?", StringComparison.Ordinal), out _) &&
                    parameterSymbol.Type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T &&
                    parameterSymbol.Type is INamedTypeSymbol namedType &&
                    namedType.TypeArguments.TrySingle(out var typeArg))
                {
                    correctType        = typeArg.ToString();
                    constraintLocation = templateParameter.Name.GetLocation();
                    correctConstraint  = $"{templateParameter.Name}?";
                    return(true);
                }
            }

            correctType        = null;
            constraintLocation = null;
            correctConstraint  = null;
            return(false);

            bool TryGetType(Span constraint, out QualifiedType type)
            {
                if (constraint.Equals("bool", StringComparison.Ordinal))
                {
                    type = KnownSymbol.Boolean;
                    return(true);
                }

                if (constraint.Equals("decimal", StringComparison.Ordinal))
                {
                    type = KnownSymbol.Decimal;
                    return(true);
                }

                if (constraint.Equals("double", StringComparison.Ordinal))
                {
                    type = KnownSymbol.Double;
                    return(true);
                }

                if (constraint.Equals("float", StringComparison.Ordinal))
                {
                    type = KnownSymbol.Float;
                    return(true);
                }

                if (constraint.Equals("int", StringComparison.Ordinal))
                {
                    type = KnownSymbol.Int32;
                    return(true);
                }

                if (constraint.Equals("long", StringComparison.Ordinal) ||
                    constraint.StartsWith("min(", StringComparison.OrdinalIgnoreCase) ||
                    constraint.StartsWith("max(", StringComparison.OrdinalIgnoreCase) ||
                    constraint.StartsWith("range(", StringComparison.OrdinalIgnoreCase))
                {
                    type = KnownSymbol.Int64;
                    return(true);
                }

                if (constraint.Equals("datetime", StringComparison.Ordinal))
                {
                    type = KnownSymbol.DateTime;
                    return(true);
                }

                if (constraint.Equals("guid", StringComparison.Ordinal))
                {
                    type = KnownSymbol.Guid;
                    return(true);
                }

                if (constraint.Equals("alpha", StringComparison.OrdinalIgnoreCase) ||
                    constraint.StartsWith("regex(", StringComparison.OrdinalIgnoreCase) ||
                    constraint.StartsWith("length(", StringComparison.OrdinalIgnoreCase) ||
                    constraint.StartsWith("minlength(", StringComparison.OrdinalIgnoreCase) ||
                    constraint.StartsWith("maxlength(", StringComparison.OrdinalIgnoreCase))
                {
                    type = KnownSymbol.String;
                    return(true);
                }

                type = null;
                return(false);
            }

            string GetCorrectConstraintType(RouteConstraint constraint)
            {
                if (constraint.Span.Equals("bool", StringComparison.Ordinal) ||
                    constraint.Span.Equals("decimal", StringComparison.Ordinal) ||
                    constraint.Span.Equals("double", StringComparison.Ordinal) ||
                    constraint.Span.Equals("float", StringComparison.Ordinal) ||
                    constraint.Span.Equals("int", StringComparison.Ordinal) ||
                    constraint.Span.Equals("long", StringComparison.Ordinal) ||
                    constraint.Span.Equals("datetime", StringComparison.Ordinal) ||
                    constraint.Span.Equals("guid", StringComparison.Ordinal))
                {
                    return(parameterSymbol.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)
                           .ToLower());
                }

                return(null);
            }
        }