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);
}
}
}