private static bool IsSystemConsoleInvocation(
            SyntaxNodeAnalysisContext context,
            InvocationExpressionSyntax invocationSyntax)
        {
            // Get the method member access (Console.WriteLine or Console.Error.WriteLine)
            if (!(invocationSyntax.Expression is MemberAccessExpressionSyntax memberAccessSyntax))
            {
                return(false);
            }

            // Get the semantic model for the invoked method
            if (!(context.SemanticModel.GetSymbolInfo(memberAccessSyntax).Symbol is IMethodSymbol methodSymbol))
            {
                return(false);
            }

            // Check if contained within System.Console
            if (KnownSymbols.IsSystemConsole(methodSymbol.ContainingType))
            {
                return(true);
            }

            // In case with Console.Error.WriteLine that wouldn't work, we need to check parent member access too
            if (!(memberAccessSyntax.Expression is MemberAccessExpressionSyntax parentMemberAccessSyntax))
            {
                return(false);
            }

            // Get the semantic model for the parent member
            if (!(context.SemanticModel.GetSymbolInfo(parentMemberAccessSyntax).Symbol is IPropertySymbol propertySymbol))
            {
                return(false);
            }

            // Check if contained within System.Console
            if (KnownSymbols.IsSystemConsole(propertySymbol.ContainingType))
            {
                return(true);
            }

            return(false);
        }
        private static bool IsSystemConsoleInvocation(
            SyntaxNodeAnalysisContext context,
            InvocationExpressionSyntax invocationSyntax)
        {
            if (invocationSyntax.Expression is MemberAccessExpressionSyntax memberAccessSyntax &&
                context.SemanticModel.GetSymbolInfo(memberAccessSyntax).Symbol is IMethodSymbol methodSymbol)
            {
                // Direct call to System.Console (e.g. System.Console.WriteLine())
                if (KnownSymbols.IsSystemConsole(methodSymbol.ContainingType))
                {
                    return(true);
                }

                // Indirect call to System.Console (e.g. System.Console.Error.WriteLine())
                if (memberAccessSyntax.Expression is MemberAccessExpressionSyntax parentMemberAccessSyntax &&
                    context.SemanticModel.GetSymbolInfo(parentMemberAccessSyntax).Symbol is IPropertySymbol propertySymbol)
                {
                    return(KnownSymbols.IsSystemConsole(propertySymbol.ContainingType));
                }
            }

            return(false);
        }
Example #3
0
        private static void CheckCommandParameterProperties(
            SymbolAnalysisContext context,
            IReadOnlyList <IPropertySymbol> properties)
        {
            var parameters = properties
                             .Select(p =>
            {
                var attribute = p
                                .GetAttributes()
                                .First(a => KnownSymbols.IsCommandParameterAttribute(a.AttributeClass));

                var order = attribute
                            .ConstructorArguments
                            .Select(a => a.Value)
                            .FirstOrDefault() as int?;

                var name = attribute
                           .NamedArguments
                           .Where(a => a.Key == "Name")
                           .Select(a => a.Value.Value)
                           .FirstOrDefault() as string;

                var converter = attribute
                                .NamedArguments
                                .Where(a => a.Key == "Converter")
                                .Select(a => a.Value.Value)
                                .Cast <ITypeSymbol?>()
                                .FirstOrDefault();

                var validators = attribute
                                 .NamedArguments
                                 .Where(a => a.Key == "Validators")
                                 .SelectMany(a => a.Value.Values)
                                 .Select(c => c.Value)
                                 .Cast <ITypeSymbol>()
                                 .ToArray();

                return(new
                {
                    Property = p,
                    Order = order,
                    Name = name,
                    Converter = converter,
                    Validators = validators
                });
            })
                             .ToArray();

            // Duplicate order
            var duplicateOrderParameters = parameters
                                           .Where(p => p.Order != null)
                                           .GroupBy(p => p.Order)
                                           .Where(g => g.Count() > 1)
                                           .SelectMany(g => g.AsEnumerable())
                                           .ToArray();

            foreach (var parameter in duplicateOrderParameters)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0021, parameter.Property.Locations.First()
                                             ));
            }

            // Duplicate name
            var duplicateNameParameters = parameters
                                          .Where(p => !string.IsNullOrWhiteSpace(p.Name))
                                          .GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
                                          .Where(g => g.Count() > 1)
                                          .SelectMany(g => g.AsEnumerable())
                                          .ToArray();

            foreach (var parameter in duplicateNameParameters)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0022, parameter.Property.Locations.First()
                                             ));
            }

            // Multiple non-scalar
            var nonScalarParameters = parameters
                                      .Where(p => !IsScalarType(p.Property.Type))
                                      .ToArray();

            if (nonScalarParameters.Length > 1)
            {
                foreach (var parameter in nonScalarParameters)
                {
                    context.ReportDiagnostic(Diagnostic.Create(
                                                 DiagnosticDescriptors.CliFx0023, parameter.Property.Locations.First()
                                                 ));
                }
            }

            // Non-last non-scalar
            var nonLastNonScalarParameter = parameters
                                            .OrderByDescending(a => a.Order)
                                            .Skip(1)
                                            .LastOrDefault(p => !IsScalarType(p.Property.Type));

            if (nonLastNonScalarParameter != null)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0024, nonLastNonScalarParameter.Property.Locations.First()
                                             ));
            }

            // Invalid converter
            var invalidConverterParameters = parameters
                                             .Where(p =>
                                                    p.Converter != null &&
                                                    !p.Converter.AllInterfaces.Any(KnownSymbols.IsArgumentValueConverterInterface))
                                             .ToArray();

            foreach (var parameter in invalidConverterParameters)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0025, parameter.Property.Locations.First()
                                             ));
            }

            // Invalid validators
            var invalidValidatorsParameters = parameters
                                              .Where(p => !p.Validators.All(v => v.AllInterfaces.Any(KnownSymbols.IsArgumentValueValidatorInterface)))
                                              .ToArray();

            foreach (var parameter in invalidValidatorsParameters)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0026, parameter.Property.Locations.First()
                                             ));
            }
        }
Example #4
0
 private static bool IsScalarType(ITypeSymbol typeSymbol) =>
 KnownSymbols.IsSystemString(typeSymbol) ||
 !typeSymbol.AllInterfaces.Select(i => i.ConstructedFrom)
 .Any(KnownSymbols.IsSystemCollectionsGenericIEnumerable);
Example #5
0
        private static void CheckCommandOptionProperties(
            SymbolAnalysisContext context,
            IReadOnlyList <IPropertySymbol> properties)
        {
            var options = properties
                          .Select(p =>
            {
                var attribute = p
                                .GetAttributes()
                                .First(a => KnownSymbols.IsCommandOptionAttribute(a.AttributeClass));

                var name = attribute
                           .ConstructorArguments
                           .Where(a => KnownSymbols.IsSystemString(a.Type))
                           .Select(a => a.Value)
                           .FirstOrDefault() as string;

                var shortName = attribute
                                .ConstructorArguments
                                .Where(a => KnownSymbols.IsSystemChar(a.Type))
                                .Select(a => a.Value)
                                .FirstOrDefault() as char?;

                var envVarName = attribute
                                 .NamedArguments
                                 .Where(a => a.Key == "EnvironmentVariableName")
                                 .Select(a => a.Value.Value)
                                 .FirstOrDefault() as string;

                var converter = attribute
                                .NamedArguments
                                .Where(a => a.Key == "Converter")
                                .Select(a => a.Value.Value)
                                .Cast <ITypeSymbol>()
                                .FirstOrDefault();

                var validators = attribute
                                 .NamedArguments
                                 .Where(a => a.Key == "Validators")
                                 .SelectMany(a => a.Value.Values)
                                 .Select(c => c.Value)
                                 .Cast <ITypeSymbol>()
                                 .ToArray();

                return(new
                {
                    Property = p,
                    Name = name,
                    ShortName = shortName,
                    EnvironmentVariableName = envVarName,
                    Converter = converter,
                    Validators = validators
                });
            })
                          .ToArray();

            // No name
            var noNameOptions = options
                                .Where(o => string.IsNullOrWhiteSpace(o.Name) && o.ShortName == null)
                                .ToArray();

            foreach (var option in noNameOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0041, option.Property.Locations.First()
                                             ));
            }

            // Too short name
            var invalidNameLengthOptions = options
                                           .Where(o => !string.IsNullOrWhiteSpace(o.Name) && o.Name.Length <= 1)
                                           .ToArray();

            foreach (var option in invalidNameLengthOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0042, option.Property.Locations.First()
                                             ));
            }

            // Duplicate name
            var duplicateNameOptions = options
                                       .Where(p => !string.IsNullOrWhiteSpace(p.Name))
                                       .GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
                                       .Where(g => g.Count() > 1)
                                       .SelectMany(g => g.AsEnumerable())
                                       .ToArray();

            foreach (var option in duplicateNameOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0043, option.Property.Locations.First()
                                             ));
            }

            // Duplicate name
            var duplicateShortNameOptions = options
                                            .Where(p => p.ShortName != null)
                                            .GroupBy(p => p.ShortName)
                                            .Where(g => g.Count() > 1)
                                            .SelectMany(g => g.AsEnumerable())
                                            .ToArray();

            foreach (var option in duplicateShortNameOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0044, option.Property.Locations.First()
                                             ));
            }

            // Duplicate environment variable name
            var duplicateEnvironmentVariableNameOptions = options
                                                          .Where(p => !string.IsNullOrWhiteSpace(p.EnvironmentVariableName))
                                                          .GroupBy(p => p.EnvironmentVariableName, StringComparer.Ordinal)
                                                          .Where(g => g.Count() > 1)
                                                          .SelectMany(g => g.AsEnumerable())
                                                          .ToArray();

            foreach (var option in duplicateEnvironmentVariableNameOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0045, option.Property.Locations.First()
                                             ));
            }

            // Invalid converter
            var invalidConverterOptions = options
                                          .Where(o =>
                                                 o.Converter != null &&
                                                 !o.Converter.AllInterfaces.Any(KnownSymbols.IsArgumentValueConverterInterface))
                                          .ToArray();

            foreach (var option in invalidConverterOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0046, option.Property.Locations.First()
                                             ));
            }

            // Invalid validators
            var invalidValidatorsOptions = options
                                           .Where(o => !o.Validators.All(v => v.AllInterfaces.Any(KnownSymbols.IsArgumentValueValidatorInterface)))
                                           .ToArray();

            foreach (var option in invalidValidatorsOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0047, option.Property.Locations.First()
                                             ));
            }

            // Non-letter first character in name
            var nonLetterFirstCharacterInNameOptions = options
                                                       .Where(o => !string.IsNullOrWhiteSpace(o.Name) && !char.IsLetter(o.Name[0]))
                                                       .ToArray();

            foreach (var option in nonLetterFirstCharacterInNameOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0048, option.Property.Locations.First()
                                             ));
            }

            // Non-letter short name
            var nonLetterShortNameOptions = options
                                            .Where(o => o.ShortName != null && !char.IsLetter(o.ShortName.Value))
                                            .ToArray();

            foreach (var option in nonLetterShortNameOptions)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                                             DiagnosticDescriptors.CliFx0049, option.Property.Locations.First()
                                             ));
            }
        }
        private static void CheckCommandOptionProperties(
            SymbolAnalysisContext context,
            IReadOnlyList <IPropertySymbol> properties)
        {
            var options = properties
                          .Select(p =>
            {
                var attribute = p
                                .GetAttributes()
                                .First(a => KnownSymbols.IsCommandOptionAttribute(a.AttributeClass));

                var name = attribute
                           .ConstructorArguments
                           .Where(a => KnownSymbols.IsSystemString(a.Type))
                           .Select(a => a.Value)
                           .FirstOrDefault() as string;

                var shortName = attribute
                                .ConstructorArguments
                                .Where(a => KnownSymbols.IsSystemChar(a.Type))
                                .Select(a => a.Value)
                                .FirstOrDefault() as char?;

                var envVarName = attribute
                                 .NamedArguments
                                 .Where(a => a.Key == "EnvironmentVariableName")
                                 .Select(a => a.Value.Value)
                                 .FirstOrDefault() as string;

                return(new
                {
                    Property = p,
                    Name = name,
                    ShortName = shortName,
                    EnvironmentVariableName = envVarName
                });
            })
                          .ToArray();

            // No name
            var noNameOptions = options
                                .Where(o => string.IsNullOrWhiteSpace(o.Name) && o.ShortName == null)
                                .ToArray();

            foreach (var option in noNameOptions)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(DiagnosticDescriptors.CliFx0041, option.Property.Locations.First()));
            }

            // Too short name
            var invalidNameLengthOptions = options
                                           .Where(o => !string.IsNullOrWhiteSpace(o.Name) && o.Name.Length <= 1)
                                           .ToArray();

            foreach (var option in invalidNameLengthOptions)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(DiagnosticDescriptors.CliFx0042, option.Property.Locations.First()));
            }

            // Duplicate name
            var duplicateNameOptions = options
                                       .Where(p => !string.IsNullOrWhiteSpace(p.Name))
                                       .GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
                                       .Where(g => g.Count() > 1)
                                       .SelectMany(g => g.AsEnumerable())
                                       .ToArray();

            foreach (var option in duplicateNameOptions)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(DiagnosticDescriptors.CliFx0043, option.Property.Locations.First()));
            }

            // Duplicate name
            var duplicateShortNameOptions = options
                                            .Where(p => p.ShortName != null)
                                            .GroupBy(p => p.ShortName)
                                            .Where(g => g.Count() > 1)
                                            .SelectMany(g => g.AsEnumerable())
                                            .ToArray();

            foreach (var option in duplicateShortNameOptions)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(DiagnosticDescriptors.CliFx0044, option.Property.Locations.First()));
            }

            // Duplicate environment variable name
            var duplicateEnvironmentVariableNameOptions = options
                                                          .Where(p => !string.IsNullOrWhiteSpace(p.EnvironmentVariableName))
                                                          .GroupBy(p => p.EnvironmentVariableName, StringComparer.Ordinal)
                                                          .Where(g => g.Count() > 1)
                                                          .SelectMany(g => g.AsEnumerable())
                                                          .ToArray();

            foreach (var option in duplicateEnvironmentVariableNameOptions)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(DiagnosticDescriptors.CliFx0045, option.Property.Locations.First()));
            }
        }