private static ActionArgument GetActionArgument(ImmutableArray <ISymbol> typeMembers, SymbolAnalysisContext context) { ActionArgument aa = null; foreach (var member in typeMembers) { // Make sure that we are only looking at properties. if (!(member is IPropertySymbol)) { continue; } var attributes = member.GetAttributes(); if (!attributes.Any()) { continue; } foreach (var attribute in attributes) { if (attribute.AttributeClass.Name == "ActionArgumentAttribute" && StringComparer.OrdinalIgnoreCase.Equals("CommandLine", attribute.AttributeClass.ContainingAssembly.Name)) { if (aa != null) { // we already found another action argument attribute context.ReportDiagnostic(Diagnostic.Create(DuplicateActionArgumentRule, member.Locations.First())); } aa = new ActionArgument(); aa.Symbol = member; var memberAsProperty = member as IPropertySymbol; if (memberAsProperty.Type.BaseType?.SpecialType == SpecialType.System_Enum) { var members = (member as IPropertySymbol).Type.GetMembers(); foreach (var item in members) { if (item is IFieldSymbol) { aa.Values.Add(item.Name); } } } } } } return(aa); }
private static Dictionary <string, List <Argument> > CreateArgumentMapForType(INamedTypeSymbol namedTypeSymbol, SymbolAnalysisContext context, out ActionArgument actionArg) { Dictionary <string, List <Argument> > mapGroupAndProps = new Dictionary <string, List <Argument> >(StringComparer.OrdinalIgnoreCase); // find the action arg. var typeMembers = namedTypeSymbol.GetMembers(); actionArg = GetActionArgument(typeMembers, context); // setup the groups available based on the actionArg if (actionArg == null) { // If we don't have an Action attribute, we are going to use the empty string // to represent a single common group for all the properties. mapGroupAndProps.Add("", new List <Argument>()); } else { // If the action attribute has been set (and we could find all the possible values) // then add those as the group values. foreach (var grp in actionArg.Values) { mapGroupAndProps.Add(grp, new List <Argument>()); } } // traverse the properties again and add them to the groups as needed. foreach (var member in typeMembers) { // Make sure that we are only looking at properties. if (!(member is IPropertySymbol)) { continue; } var attributes = member.GetAttributes(); if (!attributes.Any()) { // nothing to do if we don't have any attributes. continue; } // we should skip over the action argument. if (actionArg != null && actionArg.Symbol == member) { continue; } Argument arg = null; List <string> argGroup = new List <string>(); bool isCommon = false; bool isAttributeGroup = false; foreach (var attribute in attributes) { // Do a quick check to make sure the attribute we are looking at is coming from the CommandLine assembly if (!StringComparer.OrdinalIgnoreCase.Equals("CommandLine", attribute.AttributeClass.ContainingAssembly.Name)) { continue; } if (attribute.ConstructorArguments.Length >= 3) { if (attribute.AttributeClass.Name == "RequiredArgumentAttribute") { RequiredArgument ra = new RequiredArgument(); ra.Position = (int)attribute.ConstructorArguments[0].Value; // position ra.Name = attribute.ConstructorArguments[1].Value as string; ra.Description = attribute.ConstructorArguments[2].Value as string; if (attribute.ConstructorArguments.Length == 4) { ra.IsCollection = (bool)attribute.ConstructorArguments[3].Value; } if (arg != null) { // can't have a property be both optional and required context.ReportDiagnostic(Diagnostic.Create(ConflictingPropertyDeclarationRule, member.Locations.First())); } arg = ra; } else if (attribute.AttributeClass.Name == "OptionalArgumentAttribute") { OptionalArgument oa = new OptionalArgument(); oa.DefaultValue = attribute.ConstructorArguments[0].Value; // default value oa.Name = attribute.ConstructorArguments[1].Value as string; oa.Description = attribute.ConstructorArguments[2].Value as string; if (attribute.ConstructorArguments.Length == 4) { oa.IsCollection = (bool)attribute.ConstructorArguments[3].Value; } if (arg != null) { // can't have a property be both optional and required context.ReportDiagnostic(Diagnostic.Create(ConflictingPropertyDeclarationRule, member.Locations.First())); } arg = oa; } } if (attribute.AttributeClass.Name == "CommonArgumentAttribute") { isCommon = true; } if (attribute.AttributeClass.Name == "ArgumentGroupAttribute") { isAttributeGroup = true; argGroup.Add(attribute.ConstructorArguments[0].Value as string); } } // we did not find the Required/Optional attribute on that type. if (arg == null) { // we could not identify an argument because we don't have the required/optional attribute, but we do have the commonattribute/attributeGroup which does not make sense. if (isCommon == true || isAttributeGroup == true) { context.ReportDiagnostic(Diagnostic.Create(CannotSpecifyAGroupForANonPropertyRule, member.Locations.First())); } // we don't understand this argument, nothing to do. continue; } // store the member symbol on the argument object arg.Symbol = member; // add the argument to all the groups if (isCommon == true) { foreach (var key in mapGroupAndProps.Keys) { mapGroupAndProps[key].Add(arg); } // give an error about the action argument being a string and using a common attribute with that. if (actionArg != null && (actionArg.Symbol as IPropertySymbol).Type.BaseType?.SpecialType != SpecialType.System_Enum) { context.ReportDiagnostic(Diagnostic.Create(CommonArgumentAttributeUsedWhenActionArgumentNotEnumRule, arg.Symbol.Locations.First())); } } else { // if we have found an attribute that specifies a group, add it to that. if (argGroup.Count > 0) { // add the current argument to all the argument groups defined. foreach (var item in argGroup) { if (!mapGroupAndProps.TryGetValue(item, out List <Argument> args)) { args = new List <Argument>(); mapGroupAndProps[item] = args; } args.Add(arg); } } else { //add it to the default one. mapGroupAndProps[string.Empty].Add(arg); } } } return(mapGroupAndProps); }
private static void ValidateDefinedProperties(Dictionary <string, List <Argument> > mapGroupArgs, ActionArgument actionArg, SymbolAnalysisContext context) { // for each group, validate the parameters. foreach (var groupName in mapGroupArgs.Keys) { ValidateArguments(mapGroupArgs[groupName], context); } }