public override void Populate(CSharpGeneratorContext context)
        {
            if (!context.Project.IsUnityProject())
            {
                return;
            }

            if (!(context.ClassDeclaration.DeclaredElement is IClass typeElement))
            {
                return;
            }

            var unityVersion   = myUnityVersion.GetActualVersion(context.Project);
            var eventFunctions = myUnityApi.GetEventFunctions(typeElement, unityVersion)
                                 .Where(f => typeElement.Methods.All(m => f.Match(m) == EventFunctionMatch.NoMatch)).ToArray();

            var classDeclaration = context.ClassDeclaration;
            var factory          = CSharpElementFactory.GetInstance(classDeclaration);
            var methods          = eventFunctions
                                   .Select(e => e.CreateDeclaration(factory, classDeclaration))
                                   .Select(d => d.DeclaredElement)
                                   .Where(m => m != null);
            // Make sure we only add a method once (e.g. EditorWindow derives from ScriptableObject
            // and both declare the OnDestroy event function)
            var elements = methods.Select(m => new GeneratorDeclaredElement <IMethod>(m))
                           .Distinct(m => m.TestDescriptor);

            context.ProvidedElements.AddRange(elements);
        }
        public IEnumerable <PropertyDescriptor> GetPropertyDescriptors(IProjectItem projectItem)
        {
            var project = (IProject)projectItem;

            yield return(new ReSharperPropertyDescriptor <string, IProject>(myLifetime, myLocks,
                                                                            name: "UnityVersion",
                                                                            defaultValue: "Unspecified",
                                                                            displayName: "Unity Version",
                                                                            description: "The version of Unity being targeted by the project. Used by ReSharper to validate APIs.",
                                                                            projectItem: project,
                                                                            getValueAction: p =>
            {
                var s = myUnityVersion.GetActualVersion(project).ToString(2);
                var n = myUnityApi.GetNormalisedActualVersion(project).ToString(2);
                if (s == n)
                {
                    return s;
                }
                return $"{s} (using API info for {n})";
            },
                                                                            setValueAction: (p, value) => { }));
        }
        public override void Populate(CSharpGeneratorContext context)
        {
            if (!context.Project.IsUnityProject())
            {
                return;
            }

            if (!(context.ClassDeclaration.DeclaredElement is IClass typeElement))
            {
                return;
            }

            // CompactOneToListMap is optimised for the typical use case of only one item per key
            var existingMethods = new CompactOneToListMap <string, IMethod>();

            foreach (var typeMemberInstance in typeElement.GetAllClassMembers <IMethod>())
            {
                existingMethods.AddValue(typeMemberInstance.Member.ShortName, typeMemberInstance.Member);
            }

            var groupingTypeLookup = new Dictionary <IClrTypeName, ITypeElement>();

            var factory  = CSharpElementFactory.GetInstance(context.ClassDeclaration);
            var elements = new List <GeneratorDeclaredElement>();

            var unityVersion   = myUnityVersion.GetActualVersion(context.Project);
            var eventFunctions = myUnityApi.GetEventFunctions(typeElement, unityVersion);

            foreach (var eventFunction in eventFunctions.OrderBy(e => e.Name, new UnityEventFunctionComparer()))
            {
                // Note that we handle grouping, but it's off by default, and Rider doesn't save and restore the last
                // used grouping value. We can set EnforceGrouping, but that's a bit too much
                // https://youtrack.jetbrains.com/issue/RIDER-25194
                if (!groupingTypeLookup.TryGetValue(eventFunction.TypeName, out var groupingType))
                {
                    groupingType = myKnownTypesCache.GetByClrTypeName(eventFunction.TypeName, context.PsiModule)
                                   .GetTypeElement();
                    groupingTypeLookup.Add(eventFunction.TypeName, groupingType);
                }

                var makeVirtual  = false;
                var accessRights = AccessRights.PRIVATE;

                var exactMatch = existingMethods[eventFunction.Name]
                                 .FirstOrDefault(m => eventFunction.Match(m) == MethodSignatureMatch.ExactMatch);
                if (exactMatch != null)
                {
                    // Exact match. Only offer to implement if it's virtual and in a base class
                    if (!exactMatch.IsVirtual)
                    {
                        continue;
                    }

                    var containingType = exactMatch.GetContainingType();
                    if (Equals(containingType, typeElement))
                    {
                        continue;
                    }

                    makeVirtual  = true;
                    accessRights = exactMatch.GetAccessRights();
                    groupingType = containingType;
                }

                var newMethodDeclaration = eventFunction
                                           .CreateDeclaration(factory, myKnownTypesCache, context.ClassDeclaration, accessRights, makeVirtual);
                if (makeVirtual)
                {
                    // Make the parameter names are the same as the overridden method, or the "redundant override"
                    // inspection doesn't kick in
                    var overrideParameters = exactMatch.Parameters;
                    var newParameters      = newMethodDeclaration.ParameterDeclarations;
                    for (var i = 0; i < overrideParameters.Count; i++)
                    {
                        newParameters[i].SetName(overrideParameters[i].ShortName);
                    }
                }

                var newMethod = newMethodDeclaration.DeclaredElement;
                Assertion.AssertNotNull(newMethod, "newMethod != null");

                elements.Add(new GeneratorDeclaredElement(newMethod, newMethod.IdSubstitution, groupingType));
            }

            context.ProvidedElements.AddRange(elements.Distinct(m => m.TestDescriptor));
        }