protected override void Analyze()

in resharper/resharper-unity/src/Unity/Odin/Feature/Services/Daemon/Analyzers/OdinGroupingAttributesAnalyzer.cs [40:164]


    protected override void Analyze(IClassLikeDeclaration element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
    {
        if (!OdinAttributeUtil.HasOdinSupport(myTechnologyCollector))
            return;

        var classType = element.DeclaredElement;
        if (classType == null)
            return;
        
        bool hasOdinAttribute = false;
        foreach (var member in element.ClassMemberDeclarations)
        {
            hasOdinAttribute |= HasOdinAttribute(member);
            if (hasOdinAttribute)
                break;
        }
        
        if (!hasOdinAttribute)
            return;

        var existingGroup = new Dictionary<string, IClrTypeName>();
        var existingGroupToOriginMember = new Dictionary<string, ITypeMember>();
        var trie = new QualifiedNamesTrie<string>(false, '/');
        
        var grouping = OdinAttributeUtil.CollectGroupInfo(classType);

        var membersWithDefinedGroupWithDifferentAttribute = new LocalHashSet<ITypeMember>();
        foreach (var info in grouping)
        {
            if(string.IsNullOrEmpty(info.GroupPath))
                continue;
            
            trie.Add(info.GroupPath, info.GroupPath);

            var clrName = info.AttributeInstance.GetClrName();
            if (existingGroup.TryGetValue(info.GroupPath, out var attribute))
            {
                if (!attribute.Equals(clrName))
                {
                    membersWithDefinedGroupWithDifferentAttribute.Add(info.Member);
                    membersWithDefinedGroupWithDifferentAttribute.Add(existingGroupToOriginMember[info.GroupPath]);
                }
            }
            else
            {
                existingGroup[info.GroupPath] = clrName;
                existingGroupToOriginMember[info.GroupPath] = info.Member;
            }
        }

        foreach (var member in element.ClassMemberDeclarations)
        {
            var declaredElement = member.DeclaredElement;

            var localList = new LocalList<string>();

            foreach (var memberAttribute in member.Attributes)
            {
                var memberAttributeInstance = memberAttribute.GetAttributeInstance();
                if (!OdinKnownAttributes.LayoutAttributes.TryGetValue(memberAttributeInstance.GetClrName(), out var parameterName))
                    continue;
                
                var groupPath = OdinAttributeUtil.GetMajorGroupPath(memberAttributeInstance);
                if (groupPath == null)
                    continue;

                localList.Add(groupPath);
                
                var sections = groupPath.Split('/');
                var currentSection = new StringBuilder(sections.Length);
                foreach (var section in sections)
                {
                    currentSection.Append(section);
                    var sectionToTest = currentSection.ToString();
                    if (trie.Find(sectionToTest) == null)
                    {
                        var argument = memberAttribute.Arguments.FirstOrDefault(t => parameterName.Equals(t.MatchingParameter?.Element.ShortName));

                        if (argument != null)
                        {
                            if (argument.Expression is ICSharpLiteralExpression expression
                                && expression.IsConstantValue() && expression.ConstantValue.IsString())
                            {
                                var literalStartOffset = expression.GetDocumentRange();
                                var highlightingOffset = new DocumentRange(literalStartOffset.Document,
                                    new TextRange(literalStartOffset.StartOffset.Offset + 1,
                                        literalStartOffset.StartOffset.Offset + 1 + currentSection.Length));
                                consumer.AddHighlighting(new OdinUnknownGroupingPathWarning(highlightingOffset, sectionToTest));
                            }
                        }
                        break;
                    }
                    currentSection.Append('/');
                }
                
                if (membersWithDefinedGroupWithDifferentAttribute.Contains(declaredElement))
                {
                    var expectedAttributeName = existingGroup[groupPath];
                    consumer.AddHighlighting(new OdinMemberWrongGroupingAttributeWarning(memberAttribute, memberAttribute.Name.GetDocumentRange(), expectedAttributeName.ShortName.RemoveEnd("Attribute")));
                }
            }

            if (localList.Count > 0)
            {
                var groupsToVerify = localList.ReadOnlyList().OrderBy().ToList();
                for (int i = 0; i < groupsToVerify.Count - 1; i++)
                {
                    if (!groupsToVerify[i + 1].StartsWith(groupsToVerify[i]))
                    {
                        // Show error in case:
                        // [BoxGroup("A/B")]
                        // [BoxGroup("A/C")]
                        // int x;
                        // Do not show error in case:
                        // [BoxGroup("A")]
                        // [BoxGroup("A/B")]
                        // [BoxGroup("A/B/C")]
                        // int x;
                        consumer.AddHighlighting(new OdinMemberPresentInMultipleGroupsWarning(member.GetNameDocumentRange()));
                        break;
                    }
                }
            }
        }
    }