public void Execute(
            GeneratorExecutionContext context)
        {
            LaunchDebugger(context);

            if (context.SyntaxReceiver is not SyntaxReceiver receiver)
            {
                return;
            }

            var knownTypes     = new KnownTypes(context.Compilation);
            var candidateTypes = new List <SourceTypeInfo>();

            foreach (var candidateSyntax in receiver.CandidateSyntaxes)
            {
                var sourceTypeInfo = SourceTypeInfo.TryCreate(
                    context,
                    candidateSyntax,
                    knownTypes,
                    this._options,
                    out var diagnostics);

                context.ReportDiagnostics(diagnostics);

                if (sourceTypeInfo is null)
                {
                    continue;
                }

                candidateTypes.Add(sourceTypeInfo);
            }

            var typeMap = candidateTypes.ToDictionary(
                x => x.TypeSymbol,
                (IEqualityComparer <ITypeSymbol>)SymbolEqualityComparer.Default);

            foreach (var candidateType in candidateTypes.ToArray())
            {
                if (!candidateType.HasComparableAttribute)
                {
                    continue;
                }

                var options = candidateType.GenerateOptions;

                foreach (var member in candidateType.Members)
                {
                    if (options.GenerateGenericComparable ||
                        options.GenerateNonGenericComparable ||
                        options.GenerateOperators ||
                        options.GenerateStructuralComparable)
                    {
                        var memberType = member.Type;

                        if (knownTypes.IsGenericComparable(memberType))
                        {
                            continue;
                        }

                        if (knownTypes.IsNonGenericComparable(memberType))
                        {
                            continue;
                        }

                        if (knownTypes.IsStructuralComparable(memberType))
                        {
                            continue;
                        }

                        if (typeMap.TryGetValue(memberType, out var targetType) &&
                            targetType.HasComparableAttribute)
                        {
                            continue;
                        }

                        if (!knownTypes.TryGetNullableUnderlyingType(memberType, out var underlyingType))
                        {
                            continue;
                        }

                        if (typeMap.TryGetValue(underlyingType, out var targetType2) &&
                            targetType2.HasComparableAttribute)
                        {
                            continue;
                        }

                        var memberLocation = member.Symbol.DeclaringSyntaxReferences
                                             .Select(x => Location.Create(x.SyntaxTree, x.Span))
                                             .ToArray();

                        context.ReportDiagnostic(
                            DiagnosticFactory.MemberIsNotComparable(
                                candidateType.FullName,
                                member.Name,
                                member.TypeName,
                                memberLocation[0],
                                memberLocation.Skip(1)));
                    }
                }
            }

            foreach (var sourceType in candidateTypes)
            {
                var    options  = sourceType.GenerateOptions;
                string fullName = sourceType.FullName;

                GenerateCode(
                    context,
                    new CommonGenerator(sourceType),
                    $"{fullName}_Common.cs");

                if (options.GenerateEqualityContract &&
                    !sourceType.HasEqualityContract &&
                    !sourceType.IsValueType)
                {
                    GenerateCode(
                        context,
                        new EqualityContractGenerator(sourceType),
                        $"{fullName}_EqualityContract.cs");
                }

                if (options.GenerateEquatable &&
                    !sourceType.IsEquatable)
                {
                    GenerateCode(
                        context,
                        new EquatableGenerator(sourceType),
                        $"{fullName}_Equatable.cs");
                }

                if (options.GenerateGenericComparable &&
                    !sourceType.IsGenericComparable)
                {
                    GenerateCode(
                        context,
                        new GenericComparableGenerator(sourceType),
                        $"{fullName}_GenericComparable.cs");
                }

                if (options.GenerateNonGenericComparable &&
                    !sourceType.IsNonGenericComparable)
                {
                    GenerateCode(
                        context,
                        new NonGenericComparableGenerator(sourceType),
                        $"{fullName}_NonGenericComparable.cs");
                }

                if (options.OverrideObjectMethods)
                {
                    if (!sourceType.OverridesObjectEquals)
                    {
                        GenerateCode(
                            context,
                            new ObjectEqualsGenerator(sourceType),
                            $"{fullName}_ObjectEquals.cs");
                    }

                    if (!sourceType.OverridesObjectGetHashCode)
                    {
                        GenerateCode(
                            context,
                            new ObjectGetHashCodeGenerator(sourceType),
                            $"{fullName}_ObjectGetHashCode.cs");
                    }
                }

                if (options.GenerateOperators &&
                    !sourceType.DefinedNullableParameterOperator)
                {
                    GenerateCode(
                        context,
                        new EqualityOperatorsGenerator(sourceType),
                        $"{fullName}_EqualityOperators.cs");

                    if (sourceType.HasComparableAttribute)
                    {
                        GenerateCode(
                            context,
                            new ComparisonOperatorsGenerator(sourceType),
                            $"{fullName}_ComparisonOperators.cs");
                    }
                }
            }
        }
Ejemplo n.º 2
0
        public BaseTypeInfo(
            INamedTypeSymbol typeSymbol,
            KnownTypes knownTypes)
        {
            if (typeSymbol is null)
            {
                throw new ArgumentNullException(nameof(typeSymbol));
            }

            if (knownTypes is null)
            {
                throw new ArgumentNullException(nameof(knownTypes));
            }

            var comparer = SymbolEqualityComparer.Default;

            this.TypeSymbol = typeSymbol;
            this.FullName   = typeSymbol.GetFullName();

            this.IsEquatable            = knownTypes.IsEquatable(typeSymbol);
            this.IsGenericComparable    = knownTypes.IsGenericComparable(typeSymbol);
            this.IsNonGenericComparable = knownTypes.IsNonGenericComparable(typeSymbol);
            this.IsStructuralEquatable  = knownTypes.IsStructuralEquatable(typeSymbol);
            this.IsStructuralComparable = knownTypes.IsStructuralComparable(typeSymbol);

            var objectEquals = knownTypes.Object.GetMembers(nameof(object.Equals))
                               .OfType <IMethodSymbol>()
                               .Single(x =>
                                       x.Parameters.Length == 1 &&
                                       comparer.Equals(x.Parameters[0].Type, knownTypes.Object));

            var objectEqualsOverride =
                typeSymbol.GetOverrideSymbol(objectEquals !, comparer);

            this.OverridesObjectEquals = objectEqualsOverride is not null;

            var objectGetHashCode = knownTypes.Object.GetMembers(nameof(object.GetHashCode))
                                    .OfType <IMethodSymbol>()
                                    .Single(x => x.Parameters.Length == 0);

            var objectGetHashCodeOverride =
                typeSymbol.GetOverrideSymbol(objectGetHashCode !, comparer);

            this.OverridesObjectGetHashCode = objectGetHashCodeOverride is not null;

            var operators =
                typeSymbol.GetMembers()
                .OfType <IMethodSymbol>()
                .Where(x =>
                       x.MethodKind == MethodKind.UserDefinedOperator &&
                       x.Parameters.Length == 2);

            var nullableType = typeSymbol;

            if (typeSymbol.IsValueType)
            {
                nullableType = knownTypes.MakeNullable(typeSymbol);
            }

            var operatorNames = new[]
            {
                "op_Equality",
                "op_Inequality",
                "op_LessThan",
                "op_GreaterThan",
                "op_LessThanOrEqual",
                "op_GreaterThanOrEqual"
            };

            foreach (var op in operators)
            {
                if (!operatorNames.Contains(op.Name, StringComparer.Ordinal))
                {
                    continue;
                }

                var type1 = op.Parameters[0].Type;
                var type2 = op.Parameters[1].Type;

                if (!this.DefinedNullableParameterOperator)
                {
                    bool matchTypes =
                        comparer.Equals(type1, nullableType) &&
                        comparer.Equals(type2, nullableType);

                    this.DefinedNullableParameterOperator = matchTypes;
                }

                if (typeSymbol.IsValueType &&
                    !this.DefinedNonNullableParameterOperator)
                {
                    bool matchTypes =
                        comparer.Equals(type1, typeSymbol) &&
                        comparer.Equals(type2, typeSymbol);

                    this.DefinedNonNullableParameterOperator = matchTypes;
                }
            }

            bool hasEqualityContract = typeSymbol.GetMembers("EqualityContract")
                                       .OfType <IPropertySymbol>()
                                       .Any(x =>
                                            x.IsVirtual &&
                                            comparer.Equals(x.Type, knownTypes.Type));

            this.HasEqualityContract = hasEqualityContract;
        }