예제 #1
0
        private static void ValidateArguments(List <Argument> args, SymbolAnalysisContext context)
        {
            HashSet <string> namesOfAllArgs          = new HashSet <string>();
            HashSet <int>    positionsOrRequiredArgs = new HashSet <int>();
            int numberOfPositionalArgs = 0;

            foreach (var item in args)
            {
                if (item is OptionalArgument)

                {
                    OptionalArgument oag = item as OptionalArgument;

                    // Validate that the same name is not used across required and optional arguments.
                    if (namesOfAllArgs.Contains(oag.Name))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DuplicateArgumentNameRule, oag.Symbol.Locations.First(), oag.Name));
                    }

                    namesOfAllArgs.Add(oag.Name);
                }
                else if (item is RequiredArgument)
                {
                    RequiredArgument rag = item as RequiredArgument;
                    numberOfPositionalArgs++;

                    // Validate that the same position is not used twice
                    if (positionsOrRequiredArgs.Contains(rag.Position))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DuplicatePositionalArgumentPositionRule, rag.Symbol.Locations.First(), rag.Position));
                    }

                    // Validate that the same name is not used across required and optional arguments.
                    if (namesOfAllArgs.Contains(rag.Name))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DuplicateArgumentNameRule, rag.Symbol.Locations.First(), rag.Name));
                    }

                    namesOfAllArgs.Add(rag.Name);
                    positionsOrRequiredArgs.Add(rag.Position);
                }
            }

            int checkedPositions = 0;

            //validate that the positional arguments are in a continuous sequence, starting at 0
            for (checkedPositions = 0; checkedPositions < numberOfPositionalArgs; checkedPositions++)
            {
                if (!positionsOrRequiredArgs.Contains(checkedPositions))
                {
                    // at this point, we could not find the required positional argument 'i'
                    // we should give the error at the type level.
                    context.ReportDiagnostic(Diagnostic.Create(RequiredPositionalArgumentNotFound, args.First().Symbol.ContainingType.Locations.First(), numberOfPositionalArgs, checkedPositions));
                    break;
                }
            }
        }
예제 #2
0
        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 ValidateArguments(List <Argument> args, SymbolAnalysisContext context)
        {
            HashSet <string> namesOfAllArgs          = new HashSet <string>();
            HashSet <int>    positionsOrRequiredArgs = new HashSet <int>();
            int numberOfPositionalArgs     = 0;
            int indexOfCollectionArg       = -1;
            RequiredArgument collectionArg = null;

            foreach (var item in args)
            {
                if (item is OptionalArgument)

                {
                    OptionalArgument oag = item as OptionalArgument;

                    // Validate that the same name is not used across required and optional arguments.
                    if (namesOfAllArgs.Contains(oag.Name))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DuplicateArgumentNameRule, oag.Symbol.Locations.First(), oag.Name));
                    }

                    namesOfAllArgs.Add(oag.Name);
                }
                else if (item is RequiredArgument)
                {
                    RequiredArgument rag = item as RequiredArgument;
                    numberOfPositionalArgs++;

                    // Validate that the same position is not used twice
                    if (positionsOrRequiredArgs.Contains(rag.Position))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DuplicatePositionalArgumentPositionRule, rag.Symbol.Locations.First(), rag.Position));
                    }

                    // Validate that the same name is not used across required and optional arguments.
                    if (namesOfAllArgs.Contains(rag.Name))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DuplicateArgumentNameRule, rag.Symbol.Locations.First(), rag.Name));
                    }

                    // is the required collection argument the last one AND do we only have one of them?
                    if (indexOfCollectionArg >= 0 && rag.Position > indexOfCollectionArg)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(OnlyOneRequiredCollection, rag.Symbol.Locations.First(), collectionArg.Name, rag.Name));
                    }

                    // do we have a collection argument specified?
                    if (rag.IsCollection)
                    {
                        indexOfCollectionArg = rag.Position;
                        collectionArg        = rag;
                    }

                    namesOfAllArgs.Add(rag.Name);
                    positionsOrRequiredArgs.Add(rag.Position);
                }
            }

            int checkedPositions = 0;

            //validate that the positional arguments are in a continuous sequence, starting at 0
            for (checkedPositions = 0; checkedPositions < numberOfPositionalArgs; checkedPositions++)
            {
                if (!positionsOrRequiredArgs.Contains(checkedPositions))
                {
                    // at this point, we could not find the required positional argument 'i'
                    // we should give the error at the type level.
                    context.ReportDiagnostic(Diagnostic.Create(RequiredPositionalArgumentNotFound, args.First().Symbol.ContainingType.Locations.First(), numberOfPositionalArgs, checkedPositions));
                    break;
                }
            }

            // Ensure that the required collection argument (if present) is last.
            if (indexOfCollectionArg >= 0 && indexOfCollectionArg != numberOfPositionalArgs - 1)
            {
                context.ReportDiagnostic(Diagnostic.Create(CollectionArgumentShouldBeLast, collectionArg.Symbol.Locations.First(), collectionArg.Name));
            }
        }