예제 #1
0
        private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            var invocation = (InvocationExpressionSyntax)context.Node;

            var symbolInfo = context.SemanticModel.GetSymbolInfo(invocation);

            var symbol = symbolInfo.Symbol as IMethodSymbol;

            // Are we an extension method?
            if (symbol?.MethodKind != MethodKind.ReducedExtension)
            {
                return;
            }

            // Are we extending IDbConnection
            if (!(symbol?.ReceiverType.Name.EndsWith("IDbConnection")).GetValueOrDefault())
            {
                return;
            }

            // Is our first argument a string aka SQL? Is the string a constant value (aka, does it contain anything besides string literals and const references)?
            var isNotConstant = false;
            var sqlArgument   = invocation.ArgumentList.Arguments.FirstOrDefault();

            if (sqlArgument == null)
            {
                return;
            }

            var sqlLiteral = SemanticStringHelpers.GetResolvedString(context, sqlArgument.Expression, ref isNotConstant);

            if (string.IsNullOrWhiteSpace(sqlLiteral))
            {
                return;
            }

            // Do we have a select statement?
            if (sqlLiteral.IndexOf("select", StringComparison.OrdinalIgnoreCase) < 0)
            {
                return;
            }

            var genericTypeSyntax = ((invocation.Expression as MemberAccessExpressionSyntax)?.Name as GenericNameSyntax)?.TypeArgumentList.Arguments.FirstOrDefault();

            if (genericTypeSyntax == null)
            {
                return;
            }

            var genericTypeInfo     = context.SemanticModel.GetTypeInfo(genericTypeSyntax);
            var genericTypeArgument = genericTypeInfo.ConvertedType;

            if (genericTypeArgument == null)
            {
                return;
            }

            bool isValid;
            var  selectNames = SqlHelpers.GetSqlSelectValues(sqlLiteral, out isValid);

            if (!isValid)
            {
                return;
            }

            // If we're expecting a scalar type, but returning multiple things, that's wrong.
            if (TypeHelpers.IsPrimitiveType(genericTypeArgument) || TypeHelpers.IsString(genericTypeArgument))
            {
                if (selectNames.Count == 1)
                {
                    return;
                }

                var diagnostic = Diagnostic.Create(
                    ScalarRule,
                    sqlArgument.GetLocation(),
                    selectNames.Count);
                context.ReportDiagnostic(diagnostic);
            }
            else
            {
                var availableProperties = TypeHelpers.GetMembers(genericTypeArgument);

                if (selectNames.Any(
                        name => !availableProperties.Contains(name, StringComparer.CurrentCultureIgnoreCase)))
                {
                    var diagnostic = Diagnostic.Create(
                        PropertiesRule,
                        sqlArgument.GetLocation(),
                        string.Join(", ", selectNames.Where(name => !availableProperties.Contains(name, StringComparer.CurrentCultureIgnoreCase))),
                        genericTypeArgument.Name);
                    context.ReportDiagnostic(diagnostic);
                }
            }
        }