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;
}
}
}
}
}