An Entity Framework specific view of the current semantic model Symbols of interest to Entity Framework are stashed for easy lookup
 public static Dictionary<string, ContextualLinqParameter> BuildContext(IEnumerable<ParameterSyntax> parameters, SyntaxNodeAnalysisContext context, EFUsageContext efContext)
 {
     var cparams = new Dictionary<string, ContextualLinqParameter>();
     foreach (var p in parameters)
     {
         string name = p.Identifier.ValueText;
         var si = context.SemanticModel.GetSymbolInfo(p);
         var type = si.Symbol?.TryGetType();
         if (type != null)
             cparams[name] = new ContextualLinqParameter(name, type);
         else
             cparams[name] = new ContextualLinqParameter(name);
     }
     return cparams;
 }
        /// <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 AnalyzeLinqExpression(SyntaxNodeAnalysisContext context)
        {
            var efContext = new EFUsageContext(context);

            //Found nothing, don't bother continuing
            if (!efContext.Build())
            {
                return;
            }

            //Check if the lambda is part of an IQueryable call chain. If so, check that it only contains valid
            //EF LINQ constructs (initializers, entity members, entity navigation properties)
            var query  = context.Node as QueryExpressionSyntax;
            var lambda = context.Node as LambdaExpressionSyntax;

            if (query != null)
            {
                AnalyzeQueryExpression(context, efContext, query);
            }
            else if (lambda != null)
            {
                AnalyzeLambdaInLinqExpression(context, efContext, lambda);
            }
        }
        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;
        }
        private static void AnalyzeLinqExpression(SyntaxNodeAnalysisContext context)
        {
            var efContext = new EFUsageContext(context);

            //Found nothing, don't bother continuing
            if (!efContext.Build())
                return;

            //Check if the lambda is part of an IQueryable call chain. If so, check that it only contains valid
            //EF LINQ constructs (initializers, entity members, entity navigation properties)
            var query = context.Node as QueryExpressionSyntax;
            var lambda = context.Node as LambdaExpressionSyntax;
            if (query != null)
            {
                AnalyzeQueryExpression(context, efContext, query);
            }
            else if (lambda != null)
            {
                AnalyzeLambdaInLinqExpression(context, efContext, lambda);
            }
        }
 private static bool IsSupportedLinqToEntitiesMethod(InvocationExpressionSyntax node, MemberAccessExpressionSyntax memberExpr, EFCodeFirstClassInfo rootQueryableType, EFUsageContext efContext, SyntaxNodeAnalysisContext context) => CanonicalMethodNames.IsKnownMethod(node, memberExpr, rootQueryableType, efContext, context);
        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;
 }
        internal static bool SymbolCanBeTracedBackToDbContext(ISymbol sym, SyntaxNodeAnalysisContext context, EFUsageContext efContext, EFCodeFirstClassInfo clsInfo)
        {
            bool bTraced = false;

            var assignments = sym.DeclaringSyntaxReferences
                              .Where(decl => decl.GetSyntax()?.Kind() == SyntaxKind.EqualsValueClause)
                              .Cast <EqualsValueClauseSyntax>()
                              .Concat(sym.DeclaringSyntaxReferences.SelectMany(decl => decl.GetSyntax()?.DescendantNodes().OfType <EqualsValueClauseSyntax>()));

            //Find applicable assignments where:
            //var myVar = $SOME_EXPR;
            var applicableAssignments = assignments.Select(asn => asn.Parent)
                                        .OfType <VariableDeclaratorSyntax>()
                                        .Where(decl => decl.Identifier.ValueText == sym.Name);

            if (applicableAssignments.Any())
            {
                //Check what the RHS is
                foreach (var asn in applicableAssignments)
                {
                    var eq = asn.DescendantNodes()
                             .OfType <EqualsValueClauseSyntax>()
                             .FirstOrDefault();
                    if (eq != null)
                    {
                        switch (eq.Value.Kind())
                        {
                        //var myVar = $SOME_METHOD()
                        case SyntaxKind.InvocationExpression:
                        {
                            var invoc  = (InvocationExpressionSyntax)eq.Value;
                            var method = invoc.Expression as IdentifierNameSyntax;
                            if (method != null)
                            {
                                return(DoesMethodReturnDbSet(method, context, efContext, clsInfo));
                            }
                        }
                        break;

                        case SyntaxKind.SimpleMemberAccessExpression:
                        {
                            var member = (MemberAccessExpressionSyntax)eq.Value;
                            EFCodeFirstClassInfo cls;
                            bool isDbContext = MemberAccessIsAccessingDbContext(member, context, efContext, out cls);
                            return(isDbContext && (cls == clsInfo));
                        }
                        break;
                        }
                    }
                }
            }

            return(bTraced);
        }
        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);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Validates the given lambda to see if it is a valid LINQ to Entities expression
        /// </summary>
        /// <param name="lambda">The lambda syntax node</param>
        /// <param name="rootQueryableType">The type of the IQueryable instance where a known LINQ operator is invoked on with this lambda</param>
        /// <param name="context">The analysis context</param>
        /// <param name="efContext">The EF-specific view of the semantic model</param>
        /// <param name="treatAsWarning">If true, instructs any diagnostic reports to be flagged as warnings instead of errors. This is normally true when the analyzer cannot fully determine that the LINQ expression is made against an actual DbSet</param>
        public static void ValidateLinqToEntitiesExpression(LambdaExpressionSyntax lambda, EFCodeFirstClassInfo rootQueryableType, SyntaxNodeAnalysisContext context, EFUsageContext efContext, bool treatAsWarning = false)
        {
            var descendants    = lambda.DescendantNodes();
            var parameterNodes = ContextualLinqParameter.BuildContext(descendants.OfType <ParameterSyntax>(), context, efContext);

            ValidateLinqToEntitiesUsageInSyntaxNodes(descendants, rootQueryableType, context, efContext, parameterNodes, treatAsWarning);
        }
        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);
        }
        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;
        }
        public static Dictionary <string, ContextualLinqParameter> BuildContext(QueryExpressionSyntax query, SyntaxNodeAnalysisContext context, EFUsageContext efContext)
        {
            var cparams = new Dictionary <string, ContextualLinqParameter>();

            //From x
            var fromExpr = query.FromClause.Identifier;
            //in <expr>
            var    inExpr = query.FromClause.Expression;
            string name   = fromExpr.ValueText;

            var memberExpr = inExpr as MemberAccessExpressionSyntax;

            if (memberExpr != null)
            {
                EFCodeFirstClassInfo cls;
                if (LinqExpressionValidator.MemberAccessIsAccessingDbContext(memberExpr, context, efContext, out cls))
                {
                    cparams[name] = new ContextualLinqParameter(name, cls);
                }
            }

            //Still not set, just set as a contextual parameter with no known type
            if (!cparams.ContainsKey(name))
            {
                cparams[name] = new ContextualLinqParameter(name);
            }

            return(cparams);
        }
        public static Dictionary <string, ContextualLinqParameter> BuildContext(IEnumerable <ParameterSyntax> parameters, SyntaxNodeAnalysisContext context, EFUsageContext efContext)
        {
            var cparams = new Dictionary <string, ContextualLinqParameter>();

            foreach (var p in parameters)
            {
                string name = p.Identifier.ValueText;
                var    si   = context.SemanticModel.GetSymbolInfo(p);
                var    type = si.Symbol?.TryGetType();
                if (type != null)
                {
                    cparams[name] = new ContextualLinqParameter(name, type);
                }
                else
                {
                    cparams[name] = new ContextualLinqParameter(name);
                }
            }
            return(cparams);
        }
        /// <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);
            }
        }
Пример #17
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 bool DoesMethodReturnDbSet(IdentifierNameSyntax methodIdent, SyntaxNodeAnalysisContext context, EFUsageContext efContext, EFCodeFirstClassInfo clsInfo)
        {
            var method = methodIdent.GetDeclaringMethod(context);

            //TODO: Handle cases of lambda/Func<T, ...> invocation
            if (method != null)
            {
                var returnStatements = method.DescendantNodes().OfType <ReturnStatementSyntax>();
                //It has to be all so that we can be conclusive that all points of return a DbSet<T>
                bool returnsDbSet = returnStatements.All(ret => ReturnStatementTracesBackToDbSet(ret, context, clsInfo));
                return(returnsDbSet);
            }
            return(false);
        }
 /// <summary>
 /// Validates the given lambda to see if it is a valid LINQ to Entities expression
 /// </summary>
 /// <param name="lambda">The lambda syntax node</param>
 /// <param name="rootQueryableType">The type of the IQueryable instance where a known LINQ operator is invoked on with this lambda</param>
 /// <param name="context">The analysis context</param>
 /// <param name="efContext">The EF-specific view of the semantic model</param>
 /// <param name="treatAsWarning">If true, instructs any diagnostic reports to be flagged as warnings instead of errors. This is normally true when the analyzer cannot fully determine that the LINQ expression is made against an actual DbSet</param>
 public static void ValidateLinqToEntitiesExpression(LambdaExpressionSyntax lambda, EFCodeFirstClassInfo rootQueryableType, SyntaxNodeAnalysisContext context, EFUsageContext efContext, bool treatAsWarning = false)
 {
     var descendants = lambda.DescendantNodes();
     var parameterNodes = ContextualLinqParameter.BuildContext(descendants.OfType<ParameterSyntax>(), context, efContext);
     ValidateLinqToEntitiesUsageInSyntaxNodes(descendants, rootQueryableType, context, efContext, parameterNodes, treatAsWarning);
 }
 private static bool IsSupportedLinqToEntitiesMethod(InvocationExpressionSyntax node, MemberAccessExpressionSyntax memberExpr, EFCodeFirstClassInfo rootQueryableType, EFUsageContext efContext, SyntaxNodeAnalysisContext context) => CanonicalMethodNames.IsKnownMethod(node, memberExpr, rootQueryableType, efContext, context);
        internal static bool SymbolCanBeTracedBackToDbContext(ISymbol sym, SyntaxNodeAnalysisContext context, EFUsageContext efContext, EFCodeFirstClassInfo clsInfo)
        {
            bool bTraced = false;

            var assignments = sym.DeclaringSyntaxReferences
                                 .Where(decl => decl.GetSyntax()?.Kind() == SyntaxKind.EqualsValueClause)
                                 .Cast<EqualsValueClauseSyntax>()
                                 .Concat(sym.DeclaringSyntaxReferences.SelectMany(decl => decl.GetSyntax()?.DescendantNodes().OfType<EqualsValueClauseSyntax>()));
            
            //Find applicable assignments where:
            //var myVar = $SOME_EXPR;
            var applicableAssignments = assignments.Select(asn => asn.Parent)
                                                   .OfType<VariableDeclaratorSyntax>()
                                                   .Where(decl => decl.Identifier.ValueText == sym.Name);

            if (applicableAssignments.Any())
            {
                //Check what the RHS is
                foreach (var asn in applicableAssignments)
                {
                    var eq = asn.DescendantNodes()
                                .OfType<EqualsValueClauseSyntax>()
                                .FirstOrDefault();
                    if (eq != null)
                    {
                        switch (eq.Value.Kind())
                        {
                            //var myVar = $SOME_METHOD()
                            case SyntaxKind.InvocationExpression:
                                {
                                    var invoc = (InvocationExpressionSyntax)eq.Value;
                                    var method = invoc.Expression as IdentifierNameSyntax;
                                    if (method != null)
                                    {
                                        return DoesMethodReturnDbSet(method, context, efContext, clsInfo);
                                    }
                                }
                                break;
                            case SyntaxKind.SimpleMemberAccessExpression:
                                {
                                    var member = (MemberAccessExpressionSyntax)eq.Value;
                                    EFCodeFirstClassInfo cls;
                                    bool isDbContext = MemberAccessIsAccessingDbContext(member, context, efContext, out cls);
                                    return isDbContext && (cls == clsInfo);
                                }
                                break;
                        }
                    }
                }
            }

            return bTraced;
        }
        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 bool DoesMethodReturnDbSet(IdentifierNameSyntax methodIdent, SyntaxNodeAnalysisContext context, EFUsageContext efContext, EFCodeFirstClassInfo clsInfo)
 {
     var method = methodIdent.GetDeclaringMethod(context);
     //TODO: Handle cases of lambda/Func<T, ...> invocation
     if (method != null)
     {
         var returnStatements = method.DescendantNodes().OfType<ReturnStatementSyntax>();
         //It has to be all so that we can be conclusive that all points of return a DbSet<T>
         bool returnsDbSet = returnStatements.All(ret => ReturnStatementTracesBackToDbSet(ret, context, clsInfo));
         return returnsDbSet;
     }
     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);
                }
            }
        }
        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);
        }
        private static void AnalyzeQueryExpression(SyntaxNodeAnalysisContext context, EFUsageContext efContext, QueryExpressionSyntax query)
        {
            //I can't believe how much easier this is compared to Extension Method syntax! Then again
            //query syntax does mean its own dedicated set of C# keywords, which would means its own
            //dedicated set of syntax node types

            //First item on checklist, find out our root queryable
            EFCodeFirstClassInfo cls;
            bool isConnectedToDbContext = GetRootEntityTypeFromLinqQuery(query.FromClause.Expression, context, efContext, out cls);
            if (cls != null)
            {
                bool treatAsWarning = !isConnectedToDbContext;

                var paramNodes = ContextualLinqParameter.BuildContext(query, context, efContext);
                var descendants = query.Body.DescendantNodes();

                LinqExpressionValidator.ValidateLinqToEntitiesUsageInSyntaxNodes(descendants, cls, context, efContext, paramNodes, treatAsWarning);
            }
        }
        private static void AnalyzeQueryExpression(SyntaxNodeAnalysisContext context, EFUsageContext efContext, QueryExpressionSyntax query)
        {
            //I can't believe how much easier this is compared to Extension Method syntax! Then again
            //query syntax does mean its own dedicated set of C# keywords, which would means its own
            //dedicated set of syntax node types


            //First item on checklist, find out our root queryable
            EFCodeFirstClassInfo cls;
            bool isConnectedToDbContext = GetRootEntityTypeFromLinqQuery(query.FromClause.Expression, context, efContext, out cls);

            if (cls != null)
            {
                bool treatAsWarning = !isConnectedToDbContext;

                var paramNodes  = ContextualLinqParameter.BuildContext(query, context, efContext);
                var descendants = query.Body.DescendantNodes();

                LinqExpressionValidator.ValidateLinqToEntitiesUsageInSyntaxNodes(descendants, cls, context, efContext, paramNodes, 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);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        public static Dictionary<string, ContextualLinqParameter> BuildContext(QueryExpressionSyntax query, SyntaxNodeAnalysisContext context, EFUsageContext efContext)
        {
            var cparams = new Dictionary<string, ContextualLinqParameter>();

            //From x
            var fromExpr = query.FromClause.Identifier;
            //in <expr>
            var inExpr = query.FromClause.Expression;
            string name = fromExpr.ValueText;

            var memberExpr = inExpr as MemberAccessExpressionSyntax;
            if (memberExpr != null)
            {
                EFCodeFirstClassInfo cls;
                if (LinqExpressionValidator.MemberAccessIsAccessingDbContext(memberExpr, context, efContext, out cls))
                {
                    cparams[name] = new ContextualLinqParameter(name, cls);
                }
            }

            //Still not set, just set as a contextual parameter with no known type
            if (!cparams.ContainsKey(name))
                cparams[name] = new ContextualLinqParameter(name);

            return cparams;
        }