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)); }
protected override void Analyze(IMemberOwnerDeclaration element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer) { var typeElement = element.DeclaredElement; if (typeElement == null) { return; } if (!Api.IsUnityType(typeElement)) { return; } var project = element.GetProject(); if (project == null) { return; } var unityVersion = Api.GetNormalisedActualVersion(project); var map = new CompactOneToListMap <UnityEventFunction, Candidate>(new UnityEventFunctionKeyComparer()); foreach (var instance in typeElement.GetAllClassMembers <IMethod>()) { var unityEventFunction = Api.GetUnityEventFunction(instance.Member, unityVersion, out var match); if (unityEventFunction != null) { map.AddValue(unityEventFunction, new Candidate(instance.Member, match)); } } foreach (var(function, candidates) in map) { if (candidates.Count == 1) { // Only one function, mark it as a unity function, even if it's not an exact match // We'll let other inspections handle invalid signatures var method = candidates[0].Method; PutEventToCustomData(method, data); AddMethodSignatureInspections(consumer, method, function, candidates[0].Match); } else { var hasExactMatch = false; // All exact matches should be marked as an event function var duplicates = new FrugalLocalList <IMethod>(); foreach (var candidate in candidates) { if (candidate.Match == MethodSignatureMatch.ExactMatch) { hasExactMatch = true; if (Equals(candidate.Method.GetContainingType(), typeElement)) { PutEventToCustomData(candidate.Method, data); duplicates.Add(candidate.Method); } } } // Multiple exact matches should be marked as duplicate/ambiguous if (duplicates.Count > 1) { foreach (var method in duplicates) { foreach (var declaration in method.GetDeclarations()) { consumer.AddHighlighting( new DuplicateEventFunctionWarning((IMethodDeclaration)declaration)); } } } // If there are no exact matches, mark all as unity functions, with inspections // to fix up signature errors if (!hasExactMatch) { foreach (var candidate in candidates) { if (Equals(candidate.Method.GetContainingType(), typeElement)) { var method = candidate.Method; PutEventToCustomData(method, data); AddMethodSignatureInspections(consumer, method, function, candidate.Match); } } } } } }