GetClassInfo() публичный Метод

Gets the EF class info for the given type symbol. Returns null if no such class found
public GetClassInfo ( ITypeSymbol typeArg ) : EFCodeFirstClassInfo
typeArg ITypeSymbol
Результат EFCodeFirstClassInfo
        private static void ValidateNavigationPropertyAccess(InvocationExpressionSyntax node, SyntaxNodeAnalysisContext context, EFUsageContext efContext, bool treatAsWarning, string memberName, EFCodeFirstClassInfo cls)
        {
            if (node.ArgumentList.Arguments.Count == 1 &&
                node.ArgumentList.Arguments[0].Expression.Kind() != SyntaxKind.SimpleLambdaExpression)
            {
                if (node.ArgumentList.Arguments[0].Expression.Kind() == SyntaxKind.IdentifierName)
                {
                    //Follow the identifier back to its assignment
                    var si = context.SemanticModel.GetSymbolInfo(node.ArgumentList.Arguments[0].Expression);
                    if (si.Symbol?.Kind == SymbolKind.Local)
                    {
                        var type = si.Symbol?.TryGetType() as INamedTypeSymbol;

                        //The variable inside our LINQ sub-operator is a Func<T, bool> where T
                        //is a known entity type
                        if (type != null &&
                            type.MetadataName == $"{EFSpecialIdentifiers.Func}`2" &&
                            efContext.GetClassInfo(type.TypeArguments[0]) != null &&
                            type.TypeArguments[1].MetadataName == EFSpecialIdentifiers.Boolean)
                        {
                            //TODO: Code fix candidate
                            //
                            //In such a case, inject an .AsQueryable() before the LINQ operator call
                            //and add using System.Linq if required and convert the variable from Func<T, bool> to Expression<Func<T, bool>>
                            var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ009 : DiagnosticCodes.EFLINQ008, node.ArgumentList.Arguments[0].Expression.GetLocation(), memberName, cls.Name);
                            context.ReportDiagnostic(diagnostic);
                        }
                    }
                }
            }
        }
        static bool GetRootEntityTypeFromLinqQuery(ExpressionSyntax expr, SyntaxNodeAnalysisContext context, EFUsageContext efContext, out EFCodeFirstClassInfo clsInfo)
        {
            clsInfo = null;

            // from $var in ???
            var memberExpr = expr as MemberAccessExpressionSyntax;
            var ident      = expr as IdentifierNameSyntax;

            if (memberExpr != null) //??? = $ident.$prop
            {
                return(LinqExpressionValidator.MemberAccessIsAccessingDbContext(memberExpr, context, efContext, out clsInfo));
            }
            else if (ident != null)
            {
                var si    = context.SemanticModel.GetSymbolInfo(ident);
                var local = si.Symbol as ILocalSymbol;
                if (local != null)
                {
                    var nts = local.Type as INamedTypeSymbol;
                    if (nts != null && nts.TypeArguments.Length == 1)
                    {
                        var typeArg = nts.TypeArguments[0];
                        clsInfo = efContext.GetClassInfo(typeArg);
                        if (clsInfo != null)
                        {
                            return(LinqExpressionValidator.SymbolCanBeTracedBackToDbContext(local, context, efContext, clsInfo));
                        }
                    }
                }
            }
            return(false);
        }
        public static bool MemberAccessIsAccessingDbContext(MemberAccessExpressionSyntax memberExpr, SyntaxNodeAnalysisContext context, EFUsageContext efContext, out EFCodeFirstClassInfo clsInfo)
        {
            clsInfo = null;
            var compIdent = memberExpr.Expression as IdentifierNameSyntax;
            var prop      = memberExpr.Name;

            if (compIdent != null && prop != null)
            {
                var si   = context.SemanticModel.GetSymbolInfo(compIdent);
                var type = si.Symbol?.TryGetType();
                if (type != null)
                {
                    //$ident is a DbContext
                    if (efContext.DbContexts.Contains(type))
                    {
                        //We're expecting $prop to be a symbol
                        si = context.SemanticModel.GetSymbolInfo(prop);
                        var ps = si.Symbol as IPropertySymbol;
                        if (ps.IsDbSetProperty())
                        {
                            var nts = ps.Type as INamedTypeSymbol;
                            if (nts != null)
                            {
                                var typeArg = nts.TypeArguments[0];
                                clsInfo = efContext.GetClassInfo(typeArg);
                                return(true);
                            }
                        }
                    }
                }
            }
            return(false);
        }
        /// <summary>
        /// Validates the series of syntax nodes for valid usages of LINQ to Entities constructs
        /// </summary>
        /// <param name="descendants"></param>
        /// <param name="rootQueryableType"></param>
        /// <param name="context"></param>
        /// <param name="efContext"></param>
        /// <param name="treatAsWarning"></param>
        public static void ValidateLinqToEntitiesUsageInSyntaxNodes(IEnumerable<SyntaxNode> descendants, EFCodeFirstClassInfo rootQueryableType, SyntaxNodeAnalysisContext context, EFUsageContext efContext, Dictionary<string, ContextualLinqParameter> parameterNodes, bool treatAsWarning)
        {
            var accessNodes = descendants.OfType<MemberAccessExpressionSyntax>();
            var methodCallNodes = descendants.OfType<InvocationExpressionSyntax>();
            
            var stringNodes = descendants.OfType<InterpolatedStringExpressionSyntax>();
            var objCreationNodes = descendants.OfType<ObjectCreationExpressionSyntax>();

            //Easy one, all interpolated strings are invalid, it's just a case of whether to raise an
            //error or warning
            //
            //TODO: Code fix candidate. Offer to replace the interpolated string with a raw concatenated
            //equivalent
            foreach (var node in stringNodes)
            {
                var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ012 : DiagnosticCodes.EFLINQ011, node.GetLocation());
                context.ReportDiagnostic(diagnostic);
            }

            //Another easy one, any object creation expressions inside a known LINQ expression cannot involve an entity type
            foreach (var node in objCreationNodes)
            {
                var objType = node.Type;
                var si = context.SemanticModel.GetSymbolInfo(objType);
                var ts = si.Symbol?.TryGetType();
                if (ts != null)
                {
                    var cls = efContext.GetClassInfo(ts);
                    if (cls != null)
                    {
                        var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ016 : DiagnosticCodes.EFLINQ015, node.Type.GetLocation(), cls.Name);
                        context.ReportDiagnostic(diagnostic);
                    }
                }
            }

            //Check for property accesses on read-only properties, expression-bodied members
            foreach (var node in accessNodes)
            {
                ValidateMemberAccessInLinqExpression(node, rootQueryableType, context, parameterNodes, treatAsWarning);
            }

            foreach (var node in methodCallNodes)
            {
                ValidateMethodCallInLinqExpression(node, rootQueryableType, context, efContext, parameterNodes, treatAsWarning);
            }
        }
        /// <summary>
        /// Validates the series of syntax nodes for valid usages of LINQ to Entities constructs
        /// </summary>
        /// <param name="descendants"></param>
        /// <param name="rootQueryableType"></param>
        /// <param name="context"></param>
        /// <param name="efContext"></param>
        /// <param name="treatAsWarning"></param>
        public static void ValidateLinqToEntitiesUsageInSyntaxNodes(IEnumerable <SyntaxNode> descendants, EFCodeFirstClassInfo rootQueryableType, SyntaxNodeAnalysisContext context, EFUsageContext efContext, Dictionary <string, ContextualLinqParameter> parameterNodes, bool treatAsWarning)
        {
            var accessNodes     = descendants.OfType <MemberAccessExpressionSyntax>();
            var methodCallNodes = descendants.OfType <InvocationExpressionSyntax>();

            var stringNodes      = descendants.OfType <InterpolatedStringExpressionSyntax>();
            var objCreationNodes = descendants.OfType <ObjectCreationExpressionSyntax>();

            //Easy one, all interpolated strings are invalid, it's just a case of whether to raise an
            //error or warning
            //
            //TODO: Code fix candidate. Offer to replace the interpolated string with a raw concatenated
            //equivalent
            foreach (var node in stringNodes)
            {
                var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ012 : DiagnosticCodes.EFLINQ011, node.GetLocation());
                context.ReportDiagnostic(diagnostic);
            }

            //Another easy one, any object creation expressions inside a known LINQ expression cannot involve an entity type
            foreach (var node in objCreationNodes)
            {
                var objType = node.Type;
                var si      = context.SemanticModel.GetSymbolInfo(objType);
                var ts      = si.Symbol?.TryGetType();
                if (ts != null)
                {
                    var cls = efContext.GetClassInfo(ts);
                    if (cls != null)
                    {
                        var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ016 : DiagnosticCodes.EFLINQ015, node.Type.GetLocation(), cls.Name);
                        context.ReportDiagnostic(diagnostic);
                    }
                }
            }

            //Check for property accesses on read-only properties, expression-bodied members
            foreach (var node in accessNodes)
            {
                ValidateMemberAccessInLinqExpression(node, rootQueryableType, context, parameterNodes, treatAsWarning);
            }

            foreach (var node in methodCallNodes)
            {
                ValidateMethodCallInLinqExpression(node, rootQueryableType, context, efContext, parameterNodes, treatAsWarning);
            }
        }
        private static void AnalyzeLambdaInLinqExpression(SyntaxNodeAnalysisContext context, EFUsageContext efContext, LambdaExpressionSyntax lambda)
        {
            var lambdaAssign = lambda.Parent as EqualsValueClauseSyntax;
            var arg = lambda.Parent as ArgumentSyntax;
            if (arg != null) //The lambda in question is being passed as an argument
            {
                var parent = arg.Parent;
                while (parent != null && !(parent is ArgumentListSyntax))
                {
                    parent = parent.Parent;
                }

                if (parent != null) //Which should be part of an ArgumentList
                {
                    var argList = parent;
                    var invoc = argList?.Parent as InvocationExpressionSyntax;
                    if (invoc != null) //Which should be part of an invocation
                    {
                        var memberExpr = invoc.Expression as MemberAccessExpressionSyntax;
                        if (memberExpr != null)
                        {
                            if (CanonicalMethodNames.IsLinqOperator(memberExpr?.Name?.Identifier.ValueText))
                            {
                                var si = context.SemanticModel.GetSymbolInfo(memberExpr.Expression);

                                var lts = si.Symbol as ILocalSymbol;
                                var pts = si.Symbol as IPropertySymbol;
                                //Is this method called on a property?
                                if (pts != null)
                                {
                                    var nts = pts.Type as INamedTypeSymbol;
                                    if (nts != null)
                                    {
                                        //Like a DbSet<T>?
                                        if (nts.IsDbSet())
                                        {
                                            //That is part of a class derived from DbContext?
                                            if (pts?.ContainingType?.BaseType?.Name == EFSpecialIdentifiers.DbContext)
                                            {
                                                var typeArg = nts.TypeArguments[0];
                                                //Let's give our method some assistance, by checking what T actually is
                                                var clsInfo = efContext.GetClassInfo(typeArg);
                                                if (clsInfo != null)
                                                {
                                                    //Okay now let's see if this lambda is valid in the EF context
                                                    LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext);
                                                }
                                            }
                                        }
                                    }
                                }
                                else if (lts != null) //The linq method was called on a local variable
                                {
                                    var nts = lts.Type as INamedTypeSymbol;
                                    if (nts != null && nts.TypeArguments.Length == 1)
                                    {
                                        //This is some generic type with one type argument
                                        var typeArg = nts.TypeArguments[0];
                                        var clsInfo = efContext.GetClassInfo(typeArg);
                                        if (clsInfo != null)
                                        {
                                            if (nts.IsDbSet())
                                            {
                                                //TODO: Should still actually check that it is ultimately assigned
                                                //from a DbSet<T> property of a DbContext derived class

                                                LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext);
                                            }
                                            else if (nts.IsQueryable())
                                            {
                                                bool treatAsWarning = !LinqExpressionValidator.SymbolCanBeTracedBackToDbContext(lts, context, efContext, clsInfo);
                                                LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext, treatAsWarning);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if (lambdaAssign != null) //The lambda in question is being assigned
            {
                var localLambdaDecl = lambdaAssign?.Parent?.Parent?.Parent as LocalDeclarationStatementSyntax;
                if (localLambdaDecl != null)
                {
                    var declType = localLambdaDecl?.Declaration?.Type as GenericNameSyntax;
                    if (declType != null)
                    {
                        //Is Expression<T>
                        if (declType.Identifier.ValueText == EFSpecialIdentifiers.Expression && declType.TypeArgumentList.Arguments.Count == 1)
                        {
                            //The T is Func<TInput, TOutput>
                            var exprTypeArg = declType.TypeArgumentList.Arguments[0] as GenericNameSyntax;
                            if (exprTypeArg != null &&
                                exprTypeArg.Identifier.ValueText == EFSpecialIdentifiers.Func &&
                                exprTypeArg.TypeArgumentList.Arguments.Count == 2)
                            {
                                var inputType = exprTypeArg.TypeArgumentList.Arguments[0] as IdentifierNameSyntax;
                                var outputType = exprTypeArg.TypeArgumentList.Arguments[1] as PredefinedTypeSyntax;
                                //The TOutput in Func<TInput, TOutput> is bool
                                if (inputType != null && outputType != null && outputType.Keyword.ValueText == EFSpecialIdentifiers.BooleanShort)
                                {
                                    var si = context.SemanticModel.GetSymbolInfo(inputType);
                                    var ts = efContext.EntityTypes.FirstOrDefault(t => t == si.Symbol);
                                    if (ts != null)
                                    {
                                        var clsInfo = efContext.GetClassInfo(ts);
                                        if (clsInfo != null)
                                        {
                                            LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        static bool GetRootEntityTypeFromLinqQuery(ExpressionSyntax expr, SyntaxNodeAnalysisContext context, EFUsageContext efContext, out EFCodeFirstClassInfo clsInfo)
        {
            clsInfo = null;

            // from $var in ???
            var memberExpr = expr as MemberAccessExpressionSyntax;
            var ident = expr as IdentifierNameSyntax;
            if (memberExpr != null) //??? = $ident.$prop
            {
                return LinqExpressionValidator.MemberAccessIsAccessingDbContext(memberExpr, context, efContext, out clsInfo);
            }
            else if (ident != null)
            {
                var si = context.SemanticModel.GetSymbolInfo(ident);
                var local = si.Symbol as ILocalSymbol;
                if (local != null)
                {
                    var nts = local.Type as INamedTypeSymbol;
                    if (nts != null && nts.TypeArguments.Length == 1)
                    {
                        var typeArg = nts.TypeArguments[0];
                        clsInfo = efContext.GetClassInfo(typeArg);
                        if (clsInfo != null)
                        {
                            return LinqExpressionValidator.SymbolCanBeTracedBackToDbContext(local, context, efContext, clsInfo);
                        }
                    }
                }
            }
            return false;
        }
        internal static void ValidateMethodCallInLinqExpression(InvocationExpressionSyntax node, EFCodeFirstClassInfo rootQueryableType, SyntaxNodeAnalysisContext context, EFUsageContext efContext, Dictionary<string, ContextualLinqParameter> parameterNodes, bool treatAsWarning)
        {
            string methodName = null;
            var memberExpr = node.Expression as MemberAccessExpressionSyntax;
            var identExpr = node.Expression as IdentifierNameSyntax;
            if (memberExpr != null)
            {
                methodName = memberExpr?.Name?.Identifier.ValueText;

                //This is a LINQ operator (Where, Select, etc)
                if (CanonicalMethodNames.IsLinqOperator(methodName))
                {
                    var expr = memberExpr.Expression as MemberAccessExpressionSyntax;
                    if (expr != null) //a.b.<linq operator>()
                    {
                        //What is b?
                        var member = expr.Name as IdentifierNameSyntax;
                        if (member != null)
                        {
                            string memberName = member.Identifier.ValueText;
                            var applicableClasses = efContext.GetClassForProperty(memberName)
                                                             .Where(c => c.HasProperty(memberName));
                            
                            if (applicableClasses.Count() > 1)
                            {
                                //See if semantic model can help us disambiguate
                                var si = context.SemanticModel.GetSymbolInfo(expr.Expression);
                                var type = si.Symbol?.TryGetType();
                                if (type != null)
                                {
                                    var cls = efContext.GetClassInfo(type);
                                    //There is only one class with this property and it is confirmed to be a collection
                                    //navigation property
                                    if (cls != null && cls.IsCollectionNavigationProperty(memberName))
                                    {
                                        ValidateNavigationPropertyAccess(node, context, efContext, treatAsWarning, memberName, cls);
                                    }
                                }
                                else
                                {
                                    //This potential navigation property resolves to multiple classes, see if we can resolve to a
                                    //single one via contextual variables
                                    var inst = expr.Expression as IdentifierNameSyntax;
                                    if (inst != null)
                                    {
                                        string name = inst.Identifier.ValueText;
                                        ContextualLinqParameter cparam;
                                        if (parameterNodes.TryGetValue(name, out cparam) && 
                                            cparam.ParameterType == ContextualLinqParameterType.Queryable &&
                                            applicableClasses.Any(c => c.ClassType == cparam.QueryableType.ClassType))
                                        {
                                            //TODO: Code fix candidate
                                            //
                                            //In such a case, inject an .AsQueryable() before the LINQ operator call
                                            //and add using System.Linq if required
                                            var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ009 : DiagnosticCodes.EFLINQ008, member.GetLocation(), memberName, cparam.QueryableType.Name);
                                            context.ReportDiagnostic(diagnostic);
                                        }
                                        else
                                        {
                                            //TODO: Code fix candidate
                                            //
                                            //In such a case, inject an .AsQueryable() before the LINQ operator call
                                            //and add using System.Linq if required
                                            var diagnostic = Diagnostic.Create(DiagnosticCodes.EFLINQ010, member.GetLocation(), memberName);
                                            context.ReportDiagnostic(diagnostic);
                                        }
                                    }
                                    else
                                    {
                                        //TODO: Code fix candidate
                                        //
                                        //In such a case, inject an .AsQueryable() before the LINQ operator call
                                        //and add using System.Linq if required
                                        var diagnostic = Diagnostic.Create(DiagnosticCodes.EFLINQ010, member.GetLocation(), memberName);
                                        context.ReportDiagnostic(diagnostic);
                                    }
                                }
                            }
                            else
                            {
                                var cls = applicableClasses.FirstOrDefault();
                                //There is only one class with this property and it is confirmed to be a collection
                                //navigation property
                                if (cls != null && cls.IsCollectionNavigationProperty(memberName))
                                {
                                    ValidateNavigationPropertyAccess(node, context, efContext, treatAsWarning, memberName, cls);
                                }
                            }
                        }
                        //TODO: If not, check that the preceding member is IQueryable<T> and that T is a known
                        //entity type
                    }
                }
                else
                {
                    //TODO: AsQueryable() shouldn't be a blanket exception.
                    //We obviously should check what precedes it
                    if (methodName != EFSpecialIdentifiers.AsQueryable)
                    {
                        bool bValid = IsSupportedLinqToEntitiesMethod(node, memberExpr, rootQueryableType, efContext, context);
                        if (!bValid)
                        {
                            var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ007 : DiagnosticCodes.EFLINQ004, node.GetLocation(), methodName);
                            context.ReportDiagnostic(diagnostic);
                        }
                    }
                }
            }
            else if (identExpr != null) //A non-instance (static) method call, most certainly illegal
            {
                if (!CanonicalMethodNames.IsKnownMethod(identExpr))
                {
                    methodName = identExpr.Identifier.ValueText;
                    var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ006 : DiagnosticCodes.EFLINQ003, node.GetLocation(), methodName);
                    context.ReportDiagnostic(diagnostic);
                }
            }
        }
        private static void ValidateNavigationPropertyAccess(InvocationExpressionSyntax node, SyntaxNodeAnalysisContext context, EFUsageContext efContext, bool treatAsWarning, string memberName, EFCodeFirstClassInfo cls)
        {
            if (node.ArgumentList.Arguments.Count == 1 &&
                node.ArgumentList.Arguments[0].Expression.Kind() != SyntaxKind.SimpleLambdaExpression)
            {
                if (node.ArgumentList.Arguments[0].Expression.Kind() == SyntaxKind.IdentifierName)
                {
                    //Follow the identifier back to its assignment
                    var si = context.SemanticModel.GetSymbolInfo(node.ArgumentList.Arguments[0].Expression);
                    if (si.Symbol?.Kind == SymbolKind.Local)
                    {
                        var type = si.Symbol?.TryGetType() as INamedTypeSymbol;

                        //The variable inside our LINQ sub-operator is a Func<T, bool> where T
                        //is a known entity type
                        if (type != null &&
                            type.MetadataName == $"{EFSpecialIdentifiers.Func}`2" &&
                            efContext.GetClassInfo(type.TypeArguments[0]) != null &&
                            type.TypeArguments[1].MetadataName == EFSpecialIdentifiers.Boolean)
                        {
                            //TODO: Code fix candidate
                            //
                            //In such a case, inject an .AsQueryable() before the LINQ operator call
                            //and add using System.Linq if required and convert the variable from Func<T, bool> to Expression<Func<T, bool>>
                            var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ009 : DiagnosticCodes.EFLINQ008, node.ArgumentList.Arguments[0].Expression.GetLocation(), memberName, cls.Name);
                            context.ReportDiagnostic(diagnostic);
                        }
                    }
                }
            }
        }
 public static bool MemberAccessIsAccessingDbContext(MemberAccessExpressionSyntax memberExpr, SyntaxNodeAnalysisContext context, EFUsageContext efContext, out EFCodeFirstClassInfo clsInfo)
 {
     clsInfo = null;
     var compIdent = memberExpr.Expression as IdentifierNameSyntax;
     var prop = memberExpr.Name;
     if (compIdent != null && prop != null)
     {
         var si = context.SemanticModel.GetSymbolInfo(compIdent);
         var type = si.Symbol?.TryGetType();
         if (type != null)
         {
             //$ident is a DbContext
             if (efContext.DbContexts.Contains(type))
             {
                 //We're expecting $prop to be a symbol
                 si = context.SemanticModel.GetSymbolInfo(prop);
                 var ps = si.Symbol as IPropertySymbol;
                 if (ps.IsDbSetProperty())
                 {
                     var nts = ps.Type as INamedTypeSymbol;
                     if (nts != null)
                     {
                         var typeArg = nts.TypeArguments[0];
                         clsInfo = efContext.GetClassInfo(typeArg);
                         return true;
                     }
                 }
             }
         }
     }
     return false;
 }
Пример #11
0
        internal static bool IsKnownMethod(InvocationExpressionSyntax node, MemberAccessExpressionSyntax memberExpr, EFCodeFirstClassInfo rootQueryableType, EFUsageContext efContext, SyntaxNodeAnalysisContext context)
        {
            var member     = memberExpr.Expression as MemberAccessExpressionSyntax;
            var identifier = memberExpr.Expression as IdentifierNameSyntax;

            if (identifier != null)
            {
                //If it's a supported type, then any static method under it (predicated by identifier
                //not being null, which hints at a static call. ie. If we get here, this is a static method
                //call) is considered supported
                if (IsSupportedType(identifier.Identifier.ValueText))
                {
                    return(true);
                }
            }

            string methodName = memberExpr?.Name?.Identifier.ValueText;

            if (_methods.ContainsKey(methodName))
            {
                var mi = _methods[methodName];
                if (mi.IsStub)
                {
                    return(true);
                }

                //TODO: Based on the given syntax nodes, validate it against the known signatures and/or
                //the allowable types
                return(true);
            }

            //The method is an instance method on some member
            if (member != null)
            {
                var si = context.SemanticModel.GetSymbolInfo(member.Name);
                //Is this on a property member?
                var pts = si.Symbol as IPropertySymbol;
                if (pts != null)
                {
                    //Of a type that we know is an EF class?
                    var cls = efContext.GetClassInfo(pts.ContainingType);
                    if (cls != null)
                    {
                        //Is the property of a type that is EF whole heartedly
                        //supports?
                        if (IsSupportedType(pts.Type.MetadataName))
                        {
                            return(true);
                        }
                    }
                }
            }

            //Last chance, does this method have a special [DbFunction] attribute that
            //tells EF that there will be a DB server-side equivalent function?
            var symInfo = context.SemanticModel.GetSymbolInfo(memberExpr.Name);
            var miSym   = symInfo.Symbol as IMethodSymbol;

            if (miSym != null)
            {
                if (miSym.GetAttributes().Any(a => a.AttributeClass.MetadataName == "DbFunctionAttribute"))
                {
                    return(true);
                }
            }

            return(false);
        }
        private static void AnalyzeLambdaInLinqExpression(SyntaxNodeAnalysisContext context, EFUsageContext efContext, LambdaExpressionSyntax lambda)
        {
            var lambdaAssign = lambda.Parent as EqualsValueClauseSyntax;
            var arg          = lambda.Parent as ArgumentSyntax;

            if (arg != null) //The lambda in question is being passed as an argument
            {
                var parent = arg.Parent;
                while (parent != null && !(parent is ArgumentListSyntax))
                {
                    parent = parent.Parent;
                }

                if (parent != null) //Which should be part of an ArgumentList
                {
                    var argList = parent;
                    var invoc   = argList?.Parent as InvocationExpressionSyntax;
                    if (invoc != null) //Which should be part of an invocation
                    {
                        var memberExpr = invoc.Expression as MemberAccessExpressionSyntax;
                        if (memberExpr != null)
                        {
                            if (CanonicalMethodNames.IsLinqOperator(memberExpr?.Name?.Identifier.ValueText))
                            {
                                var si = context.SemanticModel.GetSymbolInfo(memberExpr.Expression);

                                var lts = si.Symbol as ILocalSymbol;
                                var pts = si.Symbol as IPropertySymbol;
                                //Is this method called on a property?
                                if (pts != null)
                                {
                                    var nts = pts.Type as INamedTypeSymbol;
                                    if (nts != null)
                                    {
                                        //Like a DbSet<T>?
                                        if (nts.IsDbSet())
                                        {
                                            //That is part of a class derived from DbContext?
                                            if (pts?.ContainingType?.BaseType?.Name == EFSpecialIdentifiers.DbContext)
                                            {
                                                var typeArg = nts.TypeArguments[0];
                                                //Let's give our method some assistance, by checking what T actually is
                                                var clsInfo = efContext.GetClassInfo(typeArg);
                                                if (clsInfo != null)
                                                {
                                                    //Okay now let's see if this lambda is valid in the EF context
                                                    LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext);
                                                }
                                            }
                                        }
                                    }
                                }
                                else if (lts != null) //The linq method was called on a local variable
                                {
                                    var nts = lts.Type as INamedTypeSymbol;
                                    if (nts != null && nts.TypeArguments.Length == 1)
                                    {
                                        //This is some generic type with one type argument
                                        var typeArg = nts.TypeArguments[0];
                                        var clsInfo = efContext.GetClassInfo(typeArg);
                                        if (clsInfo != null)
                                        {
                                            if (nts.IsDbSet())
                                            {
                                                //TODO: Should still actually check that it is ultimately assigned
                                                //from a DbSet<T> property of a DbContext derived class

                                                LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext);
                                            }
                                            else if (nts.IsQueryable())
                                            {
                                                bool treatAsWarning = !LinqExpressionValidator.SymbolCanBeTracedBackToDbContext(lts, context, efContext, clsInfo);
                                                LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext, treatAsWarning);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if (lambdaAssign != null) //The lambda in question is being assigned
            {
                var localLambdaDecl = lambdaAssign?.Parent?.Parent?.Parent as LocalDeclarationStatementSyntax;
                if (localLambdaDecl != null)
                {
                    var declType = localLambdaDecl?.Declaration?.Type as GenericNameSyntax;
                    if (declType != null)
                    {
                        //Is Expression<T>
                        if (declType.Identifier.ValueText == EFSpecialIdentifiers.Expression && declType.TypeArgumentList.Arguments.Count == 1)
                        {
                            //The T is Func<TInput, TOutput>
                            var exprTypeArg = declType.TypeArgumentList.Arguments[0] as GenericNameSyntax;
                            if (exprTypeArg != null &&
                                exprTypeArg.Identifier.ValueText == EFSpecialIdentifiers.Func &&
                                exprTypeArg.TypeArgumentList.Arguments.Count == 2)
                            {
                                var inputType  = exprTypeArg.TypeArgumentList.Arguments[0] as IdentifierNameSyntax;
                                var outputType = exprTypeArg.TypeArgumentList.Arguments[1] as PredefinedTypeSyntax;
                                //The TOutput in Func<TInput, TOutput> is bool
                                if (inputType != null && outputType != null && outputType.Keyword.ValueText == EFSpecialIdentifiers.BooleanShort)
                                {
                                    var si = context.SemanticModel.GetSymbolInfo(inputType);
                                    var ts = efContext.EntityTypes.FirstOrDefault(t => t == si.Symbol);
                                    if (ts != null)
                                    {
                                        var clsInfo = efContext.GetClassInfo(ts);
                                        if (clsInfo != null)
                                        {
                                            LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        internal static void ValidateMethodCallInLinqExpression(InvocationExpressionSyntax node, EFCodeFirstClassInfo rootQueryableType, SyntaxNodeAnalysisContext context, EFUsageContext efContext, Dictionary <string, ContextualLinqParameter> parameterNodes, bool treatAsWarning)
        {
            string methodName = null;
            var    memberExpr = node.Expression as MemberAccessExpressionSyntax;
            var    identExpr  = node.Expression as IdentifierNameSyntax;

            if (memberExpr != null)
            {
                methodName = memberExpr?.Name?.Identifier.ValueText;

                //This is a LINQ operator (Where, Select, etc)
                if (CanonicalMethodNames.IsLinqOperator(methodName))
                {
                    var expr = memberExpr.Expression as MemberAccessExpressionSyntax;
                    if (expr != null) //a.b.<linq operator>()
                    {
                        //What is b?
                        var member = expr.Name as IdentifierNameSyntax;
                        if (member != null)
                        {
                            string memberName        = member.Identifier.ValueText;
                            var    applicableClasses = efContext.GetClassForProperty(memberName)
                                                       .Where(c => c.HasProperty(memberName));

                            if (applicableClasses.Count() > 1)
                            {
                                //See if semantic model can help us disambiguate
                                var si   = context.SemanticModel.GetSymbolInfo(expr.Expression);
                                var type = si.Symbol?.TryGetType();
                                if (type != null)
                                {
                                    var cls = efContext.GetClassInfo(type);
                                    //There is only one class with this property and it is confirmed to be a collection
                                    //navigation property
                                    if (cls != null && cls.IsCollectionNavigationProperty(memberName))
                                    {
                                        ValidateNavigationPropertyAccess(node, context, efContext, treatAsWarning, memberName, cls);
                                    }
                                }
                                else
                                {
                                    //This potential navigation property resolves to multiple classes, see if we can resolve to a
                                    //single one via contextual variables
                                    var inst = expr.Expression as IdentifierNameSyntax;
                                    if (inst != null)
                                    {
                                        string name = inst.Identifier.ValueText;
                                        ContextualLinqParameter cparam;
                                        if (parameterNodes.TryGetValue(name, out cparam) &&
                                            cparam.ParameterType == ContextualLinqParameterType.Queryable &&
                                            applicableClasses.Any(c => c.ClassType == cparam.QueryableType.ClassType))
                                        {
                                            //TODO: Code fix candidate
                                            //
                                            //In such a case, inject an .AsQueryable() before the LINQ operator call
                                            //and add using System.Linq if required
                                            var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ009 : DiagnosticCodes.EFLINQ008, member.GetLocation(), memberName, cparam.QueryableType.Name);
                                            context.ReportDiagnostic(diagnostic);
                                        }
                                        else
                                        {
                                            //TODO: Code fix candidate
                                            //
                                            //In such a case, inject an .AsQueryable() before the LINQ operator call
                                            //and add using System.Linq if required
                                            var diagnostic = Diagnostic.Create(DiagnosticCodes.EFLINQ010, member.GetLocation(), memberName);
                                            context.ReportDiagnostic(diagnostic);
                                        }
                                    }
                                    else
                                    {
                                        //TODO: Code fix candidate
                                        //
                                        //In such a case, inject an .AsQueryable() before the LINQ operator call
                                        //and add using System.Linq if required
                                        var diagnostic = Diagnostic.Create(DiagnosticCodes.EFLINQ010, member.GetLocation(), memberName);
                                        context.ReportDiagnostic(diagnostic);
                                    }
                                }
                            }
                            else
                            {
                                var cls = applicableClasses.FirstOrDefault();
                                //There is only one class with this property and it is confirmed to be a collection
                                //navigation property
                                if (cls != null && cls.IsCollectionNavigationProperty(memberName))
                                {
                                    ValidateNavigationPropertyAccess(node, context, efContext, treatAsWarning, memberName, cls);
                                }
                            }
                        }
                        //TODO: If not, check that the preceding member is IQueryable<T> and that T is a known
                        //entity type
                    }
                }
                else
                {
                    //TODO: AsQueryable() shouldn't be a blanket exception.
                    //We obviously should check what precedes it
                    if (methodName != EFSpecialIdentifiers.AsQueryable)
                    {
                        bool bValid = IsSupportedLinqToEntitiesMethod(node, memberExpr, rootQueryableType, efContext, context);
                        if (!bValid)
                        {
                            var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ007 : DiagnosticCodes.EFLINQ004, node.GetLocation(), methodName);
                            context.ReportDiagnostic(diagnostic);
                        }
                    }
                }
            }
            else if (identExpr != null) //A non-instance (static) method call, most certainly illegal
            {
                if (!CanonicalMethodNames.IsKnownMethod(identExpr))
                {
                    methodName = identExpr.Identifier.ValueText;
                    var diagnostic = Diagnostic.Create(treatAsWarning ? DiagnosticCodes.EFLINQ006 : DiagnosticCodes.EFLINQ003, node.GetLocation(), methodName);
                    context.ReportDiagnostic(diagnostic);
                }
            }
        }
        internal static bool IsKnownMethod(InvocationExpressionSyntax node, MemberAccessExpressionSyntax memberExpr, EFCodeFirstClassInfo rootQueryableType, EFUsageContext efContext, SyntaxNodeAnalysisContext context)
        {
            var member = memberExpr.Expression as MemberAccessExpressionSyntax;
            var identifier = memberExpr.Expression as IdentifierNameSyntax;
            if (identifier != null)
            {
                //If it's a supported type, then any static method under it (predicated by identifier 
                //not being null, which hints at a static call. ie. If we get here, this is a static method
                //call) is considered supported
                if (IsSupportedType(identifier.Identifier.ValueText))
                    return true;
            }

            string methodName = memberExpr?.Name?.Identifier.ValueText;

            if (_methods.ContainsKey(methodName))
            {
                var mi = _methods[methodName];
                if (mi.IsStub)
                    return true;
                
                //TODO: Based on the given syntax nodes, validate it against the known signatures and/or
                //the allowable types
                return true;
            }

            //The method is an instance method on some member
            if (member != null)
            {
                var si = context.SemanticModel.GetSymbolInfo(member.Name);
                //Is this on a property member?
                var pts = si.Symbol as IPropertySymbol;
                if (pts != null)
                {
                    //Of a type that we know is an EF class?
                    var cls = efContext.GetClassInfo(pts.ContainingType);
                    if (cls != null)
                    {
                        //Is the property of a type that is EF whole heartedly
                        //supports?
                        if (IsSupportedType(pts.Type.MetadataName))
                            return true;
                    }
                }
            }

            //Last chance, does this method have a special [DbFunction] attribute that
            //tells EF that there will be a DB server-side equivalent function?
            var symInfo = context.SemanticModel.GetSymbolInfo(memberExpr.Name);
            var miSym = symInfo.Symbol as IMethodSymbol;
            if (miSym != null)
            {
                if (miSym.GetAttributes().Any(a => a.AttributeClass.MetadataName == "DbFunctionAttribute"))
                    return true;
            }

            return false;
        }