// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Analysis;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
///
/// Traverses the DOM and resolves expressions.
///
///
/// The ResolveVisitor does two jobs at the same time: it tracks the resolve context (properties on CSharpResolver)
/// and it resolves the expressions visited.
/// To allow using the context tracking without having to resolve every expression in the file (e.g. when you want to resolve
/// only a single node deep within the DOM), you can use the interface.
/// The navigator allows you to switch the between scanning mode and resolving mode.
/// In scanning mode, the context is tracked (local variables registered etc.), but nodes are not resolved.
/// While scanning, the navigator will get asked about every node that the resolve visitor is about to enter.
/// This allows the navigator whether to keep scanning, whether switch to resolving mode, or whether to completely skip the
/// subtree rooted at that node.
///
/// In resolving mode, the context is tracked and nodes will be resolved.
/// The resolve visitor may decide that it needs to resolve other nodes as well in order to resolve the current node.
/// In this case, those nodes will be resolved automatically, without asking the navigator interface.
/// For child nodes that are not essential to resolving, the resolve visitor will switch back to scanning mode (and thus will
/// ask the navigator for further instructions).
///
/// Moreover, there is the ResolveAll mode - it works similar to resolving mode, but will not switch back to scanning mode.
/// The whole subtree will be resolved without notifying the navigator.
///
sealed class ResolveVisitor : IAstVisitor
{
// The ResolveVisitor is also responsible for handling lambda expressions.
static readonly ResolveResult errorResult = ErrorResolveResult.UnknownError;
CSharpResolver resolver;
/// Resolve result of the current LINQ query.
/// We do not have to put this into the stored state (resolver) because
/// query expressions are always resolved in a single operation.
ResolveResult currentQueryResult;
readonly CSharpUnresolvedFile unresolvedFile;
readonly Dictionary resolveResultCache = new Dictionary();
readonly Dictionary resolverBeforeDict = new Dictionary();
readonly Dictionary resolverAfterDict = new Dictionary();
readonly Dictionary conversionDict = new Dictionary();
internal struct ConversionWithTargetType
{
public readonly Conversion Conversion;
public readonly IType TargetType;
public ConversionWithTargetType(Conversion conversion, IType targetType)
{
this.Conversion = conversion;
this.TargetType = targetType;
}
}
IResolveVisitorNavigator navigator;
bool resolverEnabled;
List undecidedLambdas;
internal CancellationToken cancellationToken;
#region Constructor
static readonly IResolveVisitorNavigator skipAllNavigator = new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Skip, null);
///
/// Creates a new ResolveVisitor instance.
///
public ResolveVisitor(CSharpResolver resolver, CSharpUnresolvedFile unresolvedFile)
{
if (resolver == null)
throw new ArgumentNullException("resolver");
this.resolver = resolver;
this.unresolvedFile = unresolvedFile;
this.navigator = skipAllNavigator;
}
internal void SetNavigator(IResolveVisitorNavigator navigator)
{
this.navigator = navigator ?? skipAllNavigator;
}
ResolveResult voidResult {
get {
return new ResolveResult(resolver.Compilation.FindType(KnownTypeCode.Void));
}
}
#endregion
#region ResetContext
///
/// Resets the visitor to the stored position, runs the action, and then reverts the visitor to the previous position.
///
void ResetContext(CSharpResolver storedContext, Action action)
{
var oldResolverEnabled = this.resolverEnabled;
var oldResolver = this.resolver;
var oldQueryResult = this.currentQueryResult;
try {
this.resolverEnabled = false;
this.resolver = storedContext;
this.currentQueryResult = null;
action();
} finally {
this.resolverEnabled = oldResolverEnabled;
this.resolver = oldResolver;
this.currentQueryResult = oldQueryResult;
}
}
#endregion
#region Scan / Resolve
///
/// Scans the AST rooted at the given node.
///
public void Scan(AstNode node)
{
if (node == null || node.IsNull)
return;
switch (node.NodeType) {
case NodeType.Token:
case NodeType.Whitespace:
return; // skip tokens, identifiers, comments, etc.
}
// don't Scan again if the node was already resolved
if (resolveResultCache.ContainsKey(node)) {
// Restore state change caused by this node:
CSharpResolver newResolver;
if (resolverAfterDict.TryGetValue(node, out newResolver))
resolver = newResolver;
return;
}
var mode = navigator.Scan(node);
switch (mode) {
case ResolveVisitorNavigationMode.Skip:
if (node is VariableDeclarationStatement || node is SwitchSection) {
// Enforce scanning of variable declarations.
goto case ResolveVisitorNavigationMode.Scan;
}
StoreCurrentState(node);
break;
case ResolveVisitorNavigationMode.Scan:
bool oldResolverEnabled = resolverEnabled;
var oldResolver = resolver;
resolverEnabled = false;
StoreCurrentState(node);
ResolveResult result = node.AcceptVisitor(this);
if (result != null) {
// If the node was resolved, store the result even though it wasn't requested.
// This is necessary so that Visit-methods that decide to always resolve are
// guaranteed to get called only once.
// This is used for lambda registration.
StoreResult(node, result);
if (resolver != oldResolver) {
// The node changed the resolver state:
resolverAfterDict.Add(node, resolver);
}
cancellationToken.ThrowIfCancellationRequested();
}
resolverEnabled = oldResolverEnabled;
break;
case ResolveVisitorNavigationMode.Resolve:
Resolve(node);
break;
default:
throw new InvalidOperationException("Invalid value for ResolveVisitorNavigationMode");
}
}
///
/// Equivalent to 'Scan', but also resolves the node at the same time.
/// This method should be only used if the CSharpResolver passed to the ResolveVisitor was manually set
/// to the correct state.
/// Otherwise, use resolver.Scan(syntaxTree); var result = resolver.GetResolveResult(node);
/// instead.
/// --
/// This method now is internal, because it is difficult to use correctly.
/// Users of the public API should use Scan()+GetResolveResult() instead.
///
internal ResolveResult Resolve(AstNode node)
{
if (node == null || node.IsNull)
return errorResult;
bool oldResolverEnabled = resolverEnabled;
resolverEnabled = true;
ResolveResult result;
if (!resolveResultCache.TryGetValue(node, out result)) {
cancellationToken.ThrowIfCancellationRequested();
StoreCurrentState(node);
var oldResolver = resolver;
result = node.AcceptVisitor(this) ?? errorResult;
StoreResult(node, result);
if (resolver != oldResolver) {
// The node changed the resolver state:
resolverAfterDict.Add(node, resolver);
}
}
resolverEnabled = oldResolverEnabled;
return result;
}
IType ResolveType(AstType type)
{
return Resolve(type).Type;
}
void StoreCurrentState(AstNode node)
{
// It's possible that we re-visit an expression that we scanned over earlier,
// so we might have to overwrite an existing state.
#if DEBUG
CSharpResolver oldResolver;
if (resolverBeforeDict.TryGetValue(node, out oldResolver)) {
Debug.Assert(oldResolver.LocalVariables.SequenceEqual(resolver.LocalVariables));
}
#endif
resolverBeforeDict[node] = resolver;
}
void StoreResult(AstNode node, ResolveResult result)
{
Debug.Assert(result != null);
if (node.IsNull)
return;
Log.WriteLine("Resolved '{0}' to {1}", node, result);
Debug.Assert(!CSharpAstResolver.IsUnresolvableNode(node));
// The state should be stored before the result is.
Debug.Assert(resolverBeforeDict.ContainsKey(node));
// Don't store results twice.
Debug.Assert(!resolveResultCache.ContainsKey(node));
// Don't use ConversionResolveResult as a result, because it can get
// confused with an implicit conversion.
Debug.Assert(!(result is ConversionResolveResult) || result is CastResolveResult);
resolveResultCache[node] = result;
if (navigator != null)
navigator.Resolved(node, result);
}
void ScanChildren(AstNode node)
{
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Scan(child);
}
}
#endregion
#region Process Conversions
sealed class AnonymousFunctionConversion : Conversion
{
public readonly IType ReturnType;
public readonly ExplicitlyTypedLambda ExplicitlyTypedLambda;
public readonly LambdaTypeHypothesis Hypothesis;
readonly bool isValid;
public AnonymousFunctionConversion(IType returnType, LambdaTypeHypothesis hypothesis, bool isValid)
{
if (returnType == null)
throw new ArgumentNullException("returnType");
this.ReturnType = returnType;
this.Hypothesis = hypothesis;
this.isValid = isValid;
}
public AnonymousFunctionConversion(IType returnType, ExplicitlyTypedLambda explicitlyTypedLambda, bool isValid)
{
if (returnType == null)
throw new ArgumentNullException("returnType");
this.ReturnType = returnType;
this.ExplicitlyTypedLambda = explicitlyTypedLambda;
this.isValid = isValid;
}
public override bool IsValid {
get { return isValid; }
}
public override bool IsImplicit {
get { return true; }
}
public override bool IsAnonymousFunctionConversion {
get { return true; }
}
}
///
/// Convert 'rr' to the target type using the specified conversion.
///
void ProcessConversion(Expression expr, ResolveResult rr, Conversion conversion, IType targetType)
{
AnonymousFunctionConversion afc = conversion as AnonymousFunctionConversion;
if (afc != null) {
Log.WriteLine("Processing conversion of anonymous function to " + targetType + "...");
Log.Indent();
if (afc.Hypothesis != null)
afc.Hypothesis.MergeInto(this, afc.ReturnType);
if (afc.ExplicitlyTypedLambda != null)
afc.ExplicitlyTypedLambda.ApplyReturnType(this, afc.ReturnType);
Log.Unindent();
}
if (expr != null && !expr.IsNull && conversion != Conversion.IdentityConversion) {
navigator.ProcessConversion(expr, rr, conversion, targetType);
conversionDict[expr] = new ConversionWithTargetType(conversion, targetType);
}
}
void ImportConversions(ResolveVisitor childVisitor)
{
foreach (var pair in childVisitor.conversionDict) {
conversionDict.Add(pair.Key, pair.Value);
navigator.ProcessConversion(pair.Key, resolveResultCache[pair.Key], pair.Value.Conversion, pair.Value.TargetType);
}
}
///
/// Convert 'rr' to the target type.
///
void ProcessConversion(Expression expr, ResolveResult rr, IType targetType)
{
if (expr == null || expr.IsNull)
return;
ProcessConversion(expr, rr, resolver.conversions.ImplicitConversion(rr, targetType), targetType);
}
///
/// Resolves the specified expression and processes the conversion to targetType.
///
void ResolveAndProcessConversion(Expression expr, IType targetType)
{
if (targetType.Kind == TypeKind.Unknown) {
// no need to resolve the expression right now
Scan(expr);
} else {
ProcessConversion(expr, Resolve(expr), targetType);
}
}
void ProcessConversionResult(Expression expr, ConversionResolveResult rr)
{
if (rr != null && !(rr is CastResolveResult))
ProcessConversion(expr, rr.Input, rr.Conversion, rr.Type);
}
void ProcessConversionResults(IEnumerable expr, IEnumerable conversionResolveResults)
{
Debug.Assert(expr.Count() == conversionResolveResults.Count());
using (var e1 = expr.GetEnumerator()) {
using (var e2 = conversionResolveResults.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
ProcessConversionResult(e1.Current, e2.Current as ConversionResolveResult);
}
}
}
}
void MarkUnknownNamedArguments(IEnumerable arguments)
{
foreach (var nae in arguments.OfType()) {
StoreCurrentState(nae);
StoreResult(nae, new NamedArgumentResolveResult(nae.Name, resolveResultCache[nae.Expression]));
}
}
void ProcessInvocationResult(Expression target, IEnumerable arguments, ResolveResult invocation)
{
if (invocation is CSharpInvocationResolveResult || invocation is DynamicInvocationResolveResult) {
int i = 0;
IList argumentsRR;
if (invocation is CSharpInvocationResolveResult) {
var csi = (CSharpInvocationResolveResult)invocation;
if (csi.IsExtensionMethodInvocation) {
Debug.Assert(arguments.Count() + 1 == csi.Arguments.Count);
ProcessConversionResult(target, csi.Arguments[0] as ConversionResolveResult);
i = 1;
} else {
Debug.Assert(arguments.Count() == csi.Arguments.Count);
}
argumentsRR = csi.Arguments;
}
else {
argumentsRR = ((DynamicInvocationResolveResult)invocation).Arguments;
}
foreach (Expression arg in arguments) {
ResolveResult argRR = argumentsRR[i++];
NamedArgumentExpression nae = arg as NamedArgumentExpression;
NamedArgumentResolveResult nrr = argRR as NamedArgumentResolveResult;
Debug.Assert((nae == null) == (nrr == null));
if (nae != null && nrr != null) {
StoreCurrentState(nae);
StoreResult(nae, nrr);
ProcessConversionResult(nae.Expression, nrr.Argument as ConversionResolveResult);
} else {
ProcessConversionResult(arg, argRR as ConversionResolveResult);
}
}
}
else {
MarkUnknownNamedArguments(arguments);
}
}
#endregion
#region GetResolveResult
///
/// Gets the resolve result for the specified node.
/// If the node was not resolved by the navigator, this method will resolve it.
///
public ResolveResult GetResolveResult(AstNode node)
{
Debug.Assert(!CSharpAstResolver.IsUnresolvableNode(node));
MergeUndecidedLambdas();
ResolveResult result;
if (resolveResultCache.TryGetValue(node, out result))
return result;
AstNode parent;
CSharpResolver storedResolver = GetPreviouslyScannedContext(node, out parent);
ResetContext(
storedResolver,
delegate {
navigator = new NodeListResolveVisitorNavigator(node);
Debug.Assert(!resolverEnabled);
Scan(parent);
navigator = skipAllNavigator;
});
MergeUndecidedLambdas();
return resolveResultCache[node];
}
CSharpResolver GetPreviouslyScannedContext(AstNode node, out AstNode parent)
{
parent = node;
CSharpResolver storedResolver;
while (!resolverBeforeDict.TryGetValue(parent, out storedResolver)) {
AstNode tmp = parent.Parent;
if (tmp == null)
throw new InvalidOperationException("Could not find a resolver state for any parent of the specified node. Are you trying to resolve a node that is not a descendant of the CSharpAstResolver's root node?");
if (tmp.NodeType == NodeType.Whitespace)
return resolver; // special case: resolve expression within preprocessor directive
parent = tmp;
}
return storedResolver;
}
///
/// Gets the resolver state in front of the specified node.
/// If the node was not visited by a previous scanning process, the
/// AST will be scanned again to determine the state.
///
public CSharpResolver GetResolverStateBefore(AstNode node)
{
MergeUndecidedLambdas();
CSharpResolver r;
if (resolverBeforeDict.TryGetValue(node, out r))
return r;
AstNode parent;
CSharpResolver storedResolver = GetPreviouslyScannedContext(node, out parent);
ResetContext(
storedResolver,
delegate {
navigator = new NodeListResolveVisitorNavigator(new[] { node }, scanOnly: true);
Debug.Assert(!resolverEnabled);
// parent might already be resolved if 'node' is an unresolvable node
Scan(parent);
navigator = skipAllNavigator;
});
MergeUndecidedLambdas();
while (node != null) {
if (resolverBeforeDict.TryGetValue(node, out r))
return r;
node = node.Parent;
}
return null;
}
public CSharpResolver GetResolverStateAfter(AstNode node)
{
// Resolve the node to fill the resolverAfterDict
GetResolveResult(node);
CSharpResolver result;
if (resolverAfterDict.TryGetValue(node, out result))
return result;
else
return GetResolverStateBefore(node);
}
public ConversionWithTargetType GetConversionWithTargetType(Expression expr)
{
GetResolverStateBefore(expr);
ResolveParentForConversion(expr);
ConversionWithTargetType result;
if (conversionDict.TryGetValue(expr, out result)) {
return result;
} else {
ResolveResult rr = GetResolveResult(expr);
return new ConversionWithTargetType(Conversion.IdentityConversion, rr.Type);
}
}
#endregion
#region Track UsingScope
ResolveResult IAstVisitor.VisitSyntaxTree(SyntaxTree unit)
{
CSharpResolver previousResolver = resolver;
try {
if (unresolvedFile != null) {
resolver = resolver.WithCurrentUsingScope(unresolvedFile.RootUsingScope.Resolve(resolver.Compilation));
} else {
var cv = new TypeSystemConvertVisitor(unit.FileName ?? string.Empty);
ApplyVisitorToUsings(cv, unit.Children);
PushUsingScope(cv.UnresolvedFile.RootUsingScope);
}
ScanChildren(unit);
return voidResult;
} finally {
resolver = previousResolver;
}
}
void ApplyVisitorToUsings(TypeSystemConvertVisitor visitor, IEnumerable children)
{
foreach (var child in children) {
if (child is ExternAliasDeclaration || child is UsingDeclaration || child is UsingAliasDeclaration) {
child.AcceptVisitor(visitor);
}
}
}
void PushUsingScope(UsingScope usingScope)
{
usingScope.Freeze();
resolver = resolver.WithCurrentUsingScope(new ResolvedUsingScope(resolver.CurrentTypeResolveContext, usingScope));
}
ResolveResult IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration)
{
CSharpResolver previousResolver = resolver;
try {
var nsName = namespaceDeclaration.NamespaceName;
AstNode child = namespaceDeclaration.FirstChild;
for (; child != null && child.Role != Roles.LBrace; child = child.NextSibling) {
Scan(child);
}
if (unresolvedFile != null) {
resolver = resolver.WithCurrentUsingScope(unresolvedFile.GetUsingScope(namespaceDeclaration.StartLocation).Resolve(resolver.Compilation));
} else {
// string fileName = namespaceDeclaration.GetRegion().FileName ?? string.Empty;
// Fetch parent using scope
// Create root using scope if necessary
if (resolver.CurrentUsingScope == null)
PushUsingScope(new UsingScope());
// Create child using scope
DomRegion region = namespaceDeclaration.GetRegion();
var identifiers = namespaceDeclaration.Identifiers.ToList();
// For all but the last identifier:
UsingScope usingScope;
for (int i = 0; i < identifiers.Count - 1; i++) {
usingScope = new UsingScope(resolver.CurrentUsingScope.UnresolvedUsingScope, identifiers[i]);
usingScope.Region = region;
PushUsingScope(usingScope);
}
// Last using scope:
usingScope = new UsingScope(resolver.CurrentUsingScope.UnresolvedUsingScope, identifiers.Last());
usingScope.Region = region;
var cv = new TypeSystemConvertVisitor(new CSharpUnresolvedFile(), usingScope);
ApplyVisitorToUsings(cv, namespaceDeclaration.Children);
PushUsingScope(usingScope);
}
for (; child != null; child = child.NextSibling) {
Scan(child);
}
// merge undecided lambdas before leaving the using scope so that
// the resolver can make better use of its cache
MergeUndecidedLambdas();
if (resolver.CurrentUsingScope != null && resolver.CurrentUsingScope.Namespace != null)
return new NamespaceResolveResult(resolver.CurrentUsingScope.Namespace);
else
return null;
} finally {
resolver = previousResolver;
}
}
#endregion
#region Track CurrentTypeDefinition
ResolveResult VisitTypeOrDelegate(AstNode typeDeclaration, string name, int typeParameterCount)
{
CSharpResolver previousResolver = resolver;
try {
ITypeDefinition newTypeDefinition = null;
if (resolver.CurrentTypeDefinition != null) {
int totalTypeParameterCount = resolver.CurrentTypeDefinition.TypeParameterCount + typeParameterCount;
foreach (ITypeDefinition nestedType in resolver.CurrentTypeDefinition.NestedTypes) {
if (nestedType.Name == name && nestedType.TypeParameterCount == totalTypeParameterCount) {
newTypeDefinition = nestedType;
break;
}
}
} else if (resolver.CurrentUsingScope != null) {
newTypeDefinition = resolver.CurrentUsingScope.Namespace.GetTypeDefinition(name, typeParameterCount);
}
if (newTypeDefinition != null)
resolver = resolver.WithCurrentTypeDefinition(newTypeDefinition);
ScanChildren(typeDeclaration);
// merge undecided lambdas before leaving the type definition so that
// the resolver can make better use of its cache
MergeUndecidedLambdas();
return newTypeDefinition != null ? new TypeResolveResult(newTypeDefinition) : errorResult;
} finally {
resolver = previousResolver;
}
}
ResolveResult IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
return VisitTypeOrDelegate(typeDeclaration, typeDeclaration.Name, typeDeclaration.TypeParameters.Count);
}
ResolveResult IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration)
{
return VisitTypeOrDelegate(delegateDeclaration, delegateDeclaration.Name, delegateDeclaration.TypeParameters.Count);
}
#endregion
#region Track CurrentMember
ResolveResult IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration)
{
return VisitFieldOrEventDeclaration(fieldDeclaration, SymbolKind.Field);
}
ResolveResult IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration)
{
return VisitFieldOrEventDeclaration(fixedFieldDeclaration, SymbolKind.Field);
}
ResolveResult IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration)
{
return VisitFieldOrEventDeclaration(eventDeclaration, SymbolKind.Event);
}
ResolveResult VisitFieldOrEventDeclaration(EntityDeclaration fieldOrEventDeclaration, SymbolKind symbolKind)
{
//int initializerCount = fieldOrEventDeclaration.GetChildrenByRole(Roles.Variable).Count;
CSharpResolver oldResolver = resolver;
for (AstNode node = fieldOrEventDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == Roles.Variable || node.Role == FixedFieldDeclaration.VariableRole) {
IMember member;
if (unresolvedFile != null) {
member = GetMemberFromLocation(node);
} else {
string name = ((VariableInitializer)node).Name;
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, symbolKind, name);
}
resolver = resolver.WithCurrentMember(member);
Scan(node);
resolver = oldResolver;
} else {
Scan(node);
}
}
return voidResult;
}
IMember GetMemberFromLocation(AstNode node)
{
ITypeDefinition typeDef = resolver.CurrentTypeDefinition;
if (typeDef == null)
return null;
TextLocation location = TypeSystemConvertVisitor.GetStartLocationAfterAttributes(node);
return typeDef.GetMembers(
delegate (IUnresolvedMember m) {
if (m.UnresolvedFile != unresolvedFile)
return false;
DomRegion region = m.Region;
return !region.IsEmpty && region.Begin <= location && region.End > location;
},
GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
).FirstOrDefault();
}
ResolveResult IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer)
{
// Within the variable initializer, the newly declared variable is not yet available:
var resolverWithVariable = resolver;
if (variableInitializer.Parent is VariableDeclarationStatement)
resolver = resolver.PopLastVariable();
ArrayInitializerExpression aie = variableInitializer.Initializer as ArrayInitializerExpression;
if (resolverEnabled || aie != null) {
ResolveResult result = errorResult;
if (variableInitializer.Parent is FieldDeclaration || variableInitializer.Parent is EventDeclaration) {
if (resolver.CurrentMember != null) {
result = new MemberResolveResult(null, resolver.CurrentMember, false);
}
} else {
string identifier = variableInitializer.Name;
foreach (IVariable v in resolverWithVariable.LocalVariables) {
if (v.Name == identifier) {
result = new LocalResolveResult(v);
break;
}
}
}
ArrayType arrayType = result.Type as ArrayType;
if (aie != null && arrayType != null) {
StoreCurrentState(aie);
List initializerElements = new List();
int[] sizes = new int[arrayType.Dimensions];
UnpackArrayInitializer(initializerElements, sizes, aie, 0, true);
ResolveResult[] initializerElementResults = new ResolveResult[initializerElements.Count];
for (int i = 0; i < initializerElementResults.Length; i++) {
initializerElementResults[i] = Resolve(initializerElements[i]);
}
var arrayCreation = resolver.ResolveArrayCreation(arrayType.ElementType, sizes, initializerElementResults);
StoreResult(aie, arrayCreation);
ProcessConversionResults(initializerElements, arrayCreation.InitializerElements);
} else if (variableInitializer.Parent is FixedStatement) {
var initRR = Resolve(variableInitializer.Initializer);
PointerType pointerType;
if (initRR.Type.Kind == TypeKind.Array) {
pointerType = new PointerType(((ArrayType)initRR.Type).ElementType);
} else if (ReflectionHelper.GetTypeCode(initRR.Type) == TypeCode.String) {
pointerType = new PointerType(resolver.Compilation.FindType(KnownTypeCode.Char));
} else {
pointerType = null;
ProcessConversion(variableInitializer.Initializer, initRR, result.Type);
}
if (pointerType != null) {
var conversion = resolver.conversions.ImplicitConversion(pointerType, result.Type);
if (conversion.IsIdentityConversion)
conversion = Conversion.ImplicitPointerConversion;
ProcessConversion(variableInitializer.Initializer, initRR, conversion, result.Type);
}
} else {
ResolveAndProcessConversion(variableInitializer.Initializer, result.Type);
}
resolver = resolverWithVariable;
return result;
} else {
Scan(variableInitializer.Initializer);
resolver = resolverWithVariable;
return null;
}
}
ResolveResult IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer)
{
if (resolverEnabled) {
ResolveResult result = errorResult;
if (resolver.CurrentMember != null) {
result = new MemberResolveResult(null, resolver.CurrentMember, false);
}
ResolveAndProcessConversion(fixedVariableInitializer.CountExpression, resolver.Compilation.FindType(KnownTypeCode.Int32));
return result;
} else {
ScanChildren(fixedVariableInitializer);
return null;
}
}
ResolveResult VisitMethodMember(EntityDeclaration memberDeclaration)
{
CSharpResolver oldResolver = resolver;
try {
IMember member = null;
if (unresolvedFile != null) {
member = GetMemberFromLocation(memberDeclaration);
}
if (member == null) {
// Re-discover the method:
SymbolKind symbolKind = memberDeclaration.SymbolKind;
var parameterTypes = TypeSystemConvertVisitor.GetParameterTypes(memberDeclaration.GetChildrenByRole(Roles.Parameter), InterningProvider.Dummy);
if (symbolKind == SymbolKind.Constructor) {
string name = memberDeclaration.HasModifier(Modifiers.Static) ? ".cctor" : ".ctor";
member = AbstractUnresolvedMember.Resolve(
resolver.CurrentTypeResolveContext, symbolKind, name,
parameterTypeReferences: parameterTypes);
} else if (symbolKind == SymbolKind.Destructor) {
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, symbolKind, "Finalize");
} else {
string[] typeParameterNames = memberDeclaration.GetChildrenByRole(Roles.TypeParameter).Select(tp => tp.Name).ToArray();
AstType explicitInterfaceAstType = memberDeclaration.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole);
ITypeReference explicitInterfaceType = null;
if (!explicitInterfaceAstType.IsNull) {
explicitInterfaceType = explicitInterfaceAstType.ToTypeReference();
}
member = AbstractUnresolvedMember.Resolve(
resolver.CurrentTypeResolveContext, symbolKind, memberDeclaration.Name,
explicitInterfaceType, typeParameterNames, parameterTypes);
}
}
resolver = resolver.WithCurrentMember(member);
ScanChildren(memberDeclaration);
if (member != null)
return new MemberResolveResult(null, member, false);
else
return errorResult;
} finally {
resolver = oldResolver;
}
}
ResolveResult IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
return VisitMethodMember(methodDeclaration);
}
ResolveResult IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
return VisitMethodMember(operatorDeclaration);
}
ResolveResult IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
return VisitMethodMember(constructorDeclaration);
}
ResolveResult IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{
return VisitMethodMember(destructorDeclaration);
}
// handle properties/indexers
ResolveResult VisitPropertyMember(EntityDeclaration propertyOrIndexerDeclaration)
{
CSharpResolver oldResolver = resolver;
try {
IMember member;
if (unresolvedFile != null) {
member = GetMemberFromLocation(propertyOrIndexerDeclaration);
} else {
// Re-discover the property:
string name = propertyOrIndexerDeclaration.Name;
var parameterTypeReferences = TypeSystemConvertVisitor.GetParameterTypes(propertyOrIndexerDeclaration.GetChildrenByRole(Roles.Parameter), InterningProvider.Dummy);
AstType explicitInterfaceAstType = propertyOrIndexerDeclaration.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole);
ITypeReference explicitInterfaceType = null;
if (!explicitInterfaceAstType.IsNull) {
explicitInterfaceType = explicitInterfaceAstType.ToTypeReference();
}
member = AbstractUnresolvedMember.Resolve(
resolver.CurrentTypeResolveContext, propertyOrIndexerDeclaration.SymbolKind, name,
explicitInterfaceType, parameterTypeReferences: parameterTypeReferences);
}
// We need to use the property as current member so that indexer parameters can be resolved correctly.
resolver = resolver.WithCurrentMember(member);
var resolverWithPropertyAsMember = resolver;
for (AstNode node = propertyOrIndexerDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == PropertyDeclaration.GetterRole && member is IProperty) {
resolver = resolver.WithCurrentMember(((IProperty)member).Getter);
Scan(node);
resolver = resolverWithPropertyAsMember;
} else if (node.Role == PropertyDeclaration.SetterRole && member is IProperty) {
resolver = resolver.WithCurrentMember(((IProperty)member).Setter);
Scan(node);
resolver = resolverWithPropertyAsMember;
} else {
Scan(node);
}
}
if (member != null)
return new MemberResolveResult(null, member, false);
else
return errorResult;
} finally {
resolver = oldResolver;
}
}
ResolveResult IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
return VisitPropertyMember(propertyDeclaration);
}
ResolveResult IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration)
{
return VisitPropertyMember(indexerDeclaration);
}
ResolveResult IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration)
{
CSharpResolver oldResolver = resolver;
try {
IMember member;
if (unresolvedFile != null) {
member = GetMemberFromLocation(eventDeclaration);
} else {
string name = eventDeclaration.Name;
AstType explicitInterfaceAstType = eventDeclaration.PrivateImplementationType;
if (explicitInterfaceAstType.IsNull) {
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, SymbolKind.Event, name);
} else {
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, SymbolKind.Event, name,
explicitInterfaceAstType.ToTypeReference());
}
}
resolver = resolver.WithCurrentMember(member);
var resolverWithEventAsMember = resolver;
for (AstNode node = eventDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == CustomEventDeclaration.AddAccessorRole && member is IEvent) {
resolver = resolver.WithCurrentMember(((IEvent)member).AddAccessor);
Scan(node);
resolver = resolverWithEventAsMember;
} else if (node.Role == CustomEventDeclaration.RemoveAccessorRole && member is IEvent) {
resolver = resolver.WithCurrentMember(((IEvent)member).RemoveAccessor);
Scan(node);
resolver = resolverWithEventAsMember;
} else {
Scan(node);
}
}
if (member != null)
return new MemberResolveResult(null, member, false);
else
return errorResult;
} finally {
resolver = oldResolver;
}
}
ResolveResult IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration)
{
ScanChildren(parameterDeclaration);
if (resolverEnabled) {
string name = parameterDeclaration.Name;
if (parameterDeclaration.Parent is DocumentationReference) {
// create a dummy parameter
IType type = ResolveType(parameterDeclaration.Type);
switch (parameterDeclaration.ParameterModifier) {
case ParameterModifier.Ref:
case ParameterModifier.Out:
type = new ByReferenceType(type);
break;
}
return new LocalResolveResult(new DefaultParameter(
type, name,
isRef: parameterDeclaration.ParameterModifier == ParameterModifier.Ref,
isOut: parameterDeclaration.ParameterModifier == ParameterModifier.Out,
isParams: parameterDeclaration.ParameterModifier == ParameterModifier.Params));
}
// Look in lambda parameters:
foreach (IParameter p in resolver.LocalVariables.OfType()) {
if (p.Name == name)
return new LocalResolveResult(p);
}
IParameterizedMember pm = resolver.CurrentMember as IParameterizedMember;
if (pm == null && resolver.CurrentTypeDefinition != null) {
// Also consider delegate parameters:
pm = resolver.CurrentTypeDefinition.GetDelegateInvokeMethod();
// pm will be null if the current type isn't a delegate
}
if (pm != null) {
foreach (IParameter p in pm.Parameters) {
if (p.Name == name) {
return new LocalResolveResult(p);
}
}
}
return errorResult;
} else {
return null;
}
}
ResolveResult IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration)
{
ScanChildren(typeParameterDeclaration);
if (resolverEnabled) {
string name = typeParameterDeclaration.Name;
IMethod m = resolver.CurrentMember as IMethod;
if (m != null) {
foreach (var tp in m.TypeParameters) {
if (tp.Name == name)
return new TypeResolveResult(tp);
}
}
if (resolver.CurrentTypeDefinition != null) {
var typeParameters = resolver.CurrentTypeDefinition.TypeParameters;
// look backwards so that TPs in the current type take precedence over those copied from outer types
for (int i = typeParameters.Count - 1; i >= 0; i--) {
if (typeParameters[i].Name == name)
return new TypeResolveResult(typeParameters[i]);
}
}
return errorResult;
} else {
return null;
}
}
ResolveResult IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration)
{
CSharpResolver oldResolver = resolver;
try {
// Scan enum member attributes before setting resolver.CurrentMember, so that
// enum values used as attribute arguments have the correct type.
// (within an enum member, all other enum members are treated as having their underlying type)
foreach (var attributeSection in enumMemberDeclaration.Attributes)
Scan(attributeSection);
IMember member = null;
if (unresolvedFile != null) {
member = GetMemberFromLocation(enumMemberDeclaration);
} else if (resolver.CurrentTypeDefinition != null) {
string name = enumMemberDeclaration.Name;
member = resolver.CurrentTypeDefinition.GetFields(f => f.Name == name, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault();
}
resolver = resolver.WithCurrentMember(member);
if (resolverEnabled && resolver.CurrentTypeDefinition != null) {
ResolveAndProcessConversion(enumMemberDeclaration.Initializer, resolver.CurrentTypeDefinition.EnumUnderlyingType);
if (resolverEnabled && member != null)
return new MemberResolveResult(null, member, false);
else
return errorResult;
} else {
Scan(enumMemberDeclaration.Initializer);
return null;
}
} finally {
resolver = oldResolver;
}
}
#endregion
#region Track CheckForOverflow
ResolveResult IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression)
{
CSharpResolver oldResolver = resolver;
try {
resolver = resolver.WithCheckForOverflow(true);
if (resolverEnabled) {
return Resolve(checkedExpression.Expression);
} else {
ScanChildren(checkedExpression);
return null;
}
} finally {
resolver = oldResolver;
}
}
ResolveResult IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression)
{
CSharpResolver oldResolver = resolver;
try {
resolver = resolver.WithCheckForOverflow(false);
if (resolverEnabled) {
return Resolve(uncheckedExpression.Expression);
} else {
ScanChildren(uncheckedExpression);
return null;
}
} finally {
resolver = oldResolver;
}
}
ResolveResult IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement)
{
CSharpResolver oldResolver = resolver;
try {
resolver = resolver.WithCheckForOverflow(true);
ScanChildren(checkedStatement);
return voidResult;
} finally {
resolver = oldResolver;
}
}
ResolveResult IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement)
{
CSharpResolver oldResolver = resolver;
try {
resolver = resolver.WithCheckForOverflow(false);
ScanChildren(uncheckedStatement);
return voidResult;
} finally {
resolver = oldResolver;
}
}
#endregion
#region Visit AnonymousTypeCreateExpression
static string GetAnonymousTypePropertyName(Expression expr, out Expression resolveExpr)
{
if (expr is NamedExpression) {
var namedArgExpr = (NamedExpression)expr;
resolveExpr = namedArgExpr.Expression;
return namedArgExpr.Name;
}
// no name given, so it's a projection initializer
if (expr is MemberReferenceExpression) {
resolveExpr = expr;
return ((MemberReferenceExpression)expr).MemberName;
}
if (expr is IdentifierExpression) {
resolveExpr = expr;
return ((IdentifierExpression)expr).Identifier;
}
resolveExpr = null;
return null;
}
class AnonymousTypeMember
{
public readonly Expression Expression;
public readonly ResolveResult Initializer;
public AnonymousTypeMember(Expression expression, ResolveResult initializer)
{
this.Expression = expression;
this.Initializer = initializer;
}
}
ResolveResult IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression)
{
// 7.6.10.6 Anonymous object creation expressions
List unresolvedProperties = new List();
List members = new List();
foreach (var expr in anonymousTypeCreateExpression.Initializers) {
Expression resolveExpr;
var name = GetAnonymousTypePropertyName(expr, out resolveExpr);
if (resolveExpr != null) {
var initRR = Resolve(resolveExpr);
var returnTypeRef = initRR.Type.ToTypeReference();
var property = new DefaultUnresolvedProperty();
property.Name = name;
property.Accessibility = Accessibility.Public;
property.ReturnType = returnTypeRef;
property.Getter = new DefaultUnresolvedMethod {
Name = "get_" + name,
Accessibility = Accessibility.Public,
ReturnType = returnTypeRef,
SymbolKind = SymbolKind.Accessor,
AccessorOwner = property
};
unresolvedProperties.Add(property);
members.Add(new AnonymousTypeMember(expr, initRR));
} else {
Scan(expr);
}
}
var anonymousType = new AnonymousType(resolver.Compilation, unresolvedProperties);
var properties = anonymousType.GetProperties().ToList();
Debug.Assert(properties.Count == members.Count);
List assignments = new List();
for (int i = 0; i < members.Count; i++) {
ResolveResult lhs = new MemberResolveResult(new InitializedObjectResolveResult(anonymousType), properties[i]);
ResolveResult rhs = members[i].Initializer;
ResolveResult assignment = resolver.ResolveAssignment(AssignmentOperatorType.Assign, lhs, rhs);
var ne = members[i].Expression as NamedExpression;
if (ne != null) {
StoreCurrentState(ne);
// ne.Expression was already resolved by the first loop
StoreResult(ne, lhs);
}
assignments.Add(assignment);
}
var anonymousCtor = DefaultResolvedMethod.GetDummyConstructor(resolver.Compilation, anonymousType);
return new InvocationResolveResult(null, anonymousCtor, initializerStatements: assignments);
}
#endregion
#region Visit Expressions
ResolveResult IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression)
{
int dimensions = arrayCreateExpression.Arguments.Count;
IEnumerable sizeArgumentExpressions;
ResolveResult[] sizeArguments;
IEnumerable additionalArraySpecifiers;
if (dimensions == 0) {
var firstSpecifier = arrayCreateExpression.AdditionalArraySpecifiers.FirstOrDefault();
if (firstSpecifier != null) {
dimensions = firstSpecifier.Dimensions;
additionalArraySpecifiers = arrayCreateExpression.AdditionalArraySpecifiers.Skip(1);
} else {
// No array specifiers (neither with nor without size) - can happen if there are syntax errors.
// Dimensions must be at least one; otherwise 'new ArrayType' will crash.
dimensions = 1;
additionalArraySpecifiers = arrayCreateExpression.AdditionalArraySpecifiers;
}
sizeArguments = null;
sizeArgumentExpressions = null;
} else {
sizeArgumentExpressions = arrayCreateExpression.Arguments;
sizeArguments = new ResolveResult[dimensions];
int pos = 0;
foreach (var node in sizeArgumentExpressions)
sizeArguments[pos++] = Resolve(node);
additionalArraySpecifiers = arrayCreateExpression.AdditionalArraySpecifiers;
}
int[] sizes;
List initializerElements;
ResolveResult[] initializerElementResults;
if (arrayCreateExpression.Initializer.IsNull) {
sizes = null;
initializerElements = null;
initializerElementResults = null;
} else {
StoreCurrentState(arrayCreateExpression.Initializer);
initializerElements = new List();
sizes = new int[dimensions];
UnpackArrayInitializer(initializerElements, sizes, arrayCreateExpression.Initializer, 0, true);
initializerElementResults = new ResolveResult[initializerElements.Count];
for (int i = 0; i < initializerElementResults.Length; i++) {
initializerElementResults[i] = Resolve(initializerElements[i]);
}
StoreResult(arrayCreateExpression.Initializer, voidResult);
}
IType elementType;
if (arrayCreateExpression.Type.IsNull) {
elementType = null;
} else {
elementType = ResolveType(arrayCreateExpression.Type);
foreach (var spec in additionalArraySpecifiers.Reverse()) {
elementType = new ArrayType(resolver.Compilation, elementType, spec.Dimensions);
}
}
ArrayCreateResolveResult acrr;
if (sizeArguments != null) {
acrr = resolver.ResolveArrayCreation(elementType, sizeArguments, initializerElementResults);
} else if (sizes != null) {
acrr = resolver.ResolveArrayCreation(elementType, sizes, initializerElementResults);
} else {
// neither size arguments nor an initializer exist -> error
return new ErrorResolveResult(new ArrayType(resolver.Compilation, elementType ?? SpecialType.UnknownType, dimensions));
}
if (sizeArgumentExpressions != null)
ProcessConversionResults(sizeArgumentExpressions, acrr.SizeArguments);
if (acrr.InitializerElements != null)
ProcessConversionResults(initializerElements, acrr.InitializerElements);
return acrr;
}
void UnpackArrayInitializer(List elementList, int[] sizes, ArrayInitializerExpression initializer, int dimension, bool resolveNestedInitializersToVoid)
{
Debug.Assert(dimension < sizes.Length);
int elementCount = 0;
if (dimension + 1 < sizes.Length) {
foreach (var node in initializer.Elements) {
ArrayInitializerExpression aie = node as ArrayInitializerExpression;
if (aie != null) {
if (resolveNestedInitializersToVoid) {
StoreCurrentState(aie);
StoreResult(aie, voidResult);
}
UnpackArrayInitializer(elementList, sizes, aie, dimension + 1, resolveNestedInitializersToVoid);
} else {
elementList.Add(node);
}
elementCount++;
}
} else {
foreach (var expr in initializer.Elements) {
elementList.Add(expr);
elementCount++;
}
}
if (sizes[dimension] == 0) // 0 = uninitialized
sizes[dimension] = elementCount;
else if (sizes[dimension] != elementCount)
sizes[dimension] = -1; // -1 = error
}
ResolveResult IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression)
{
// Array initializers are handled by their parent expression.
ScanChildren(arrayInitializerExpression);
return errorResult;
}
ResolveResult IAstVisitor.VisitAsExpression(AsExpression asExpression)
{
if (resolverEnabled) {
ResolveResult input = Resolve(asExpression.Expression);
var targetType = ResolveType(asExpression.Type);
return new CastResolveResult(targetType, input, Conversion.TryCast, resolver.CheckForOverflow);
} else {
ScanChildren(asExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression)
{
if (resolverEnabled) {
Expression left = assignmentExpression.Left;
Expression right = assignmentExpression.Right;
ResolveResult leftResult = Resolve(left);
ResolveResult rightResult = Resolve(right);
ResolveResult rr = resolver.ResolveAssignment(assignmentExpression.Operator, leftResult, rightResult);
ProcessConversionsInBinaryOperatorResult(left, right, rr);
return rr;
} else {
ScanChildren(assignmentExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression)
{
if (resolverEnabled) {
return resolver.ResolveBaseReference();
} else {
ScanChildren(baseReferenceExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
{
if (resolverEnabled) {
Expression left = binaryOperatorExpression.Left;
Expression right = binaryOperatorExpression.Right;
ResolveResult leftResult = Resolve(left);
ResolveResult rightResult = Resolve(right);
ResolveResult rr = resolver.ResolveBinaryOperator(binaryOperatorExpression.Operator, leftResult, rightResult);
ProcessConversionsInBinaryOperatorResult(left, right, rr);
return rr;
} else {
ScanChildren(binaryOperatorExpression);
return null;
}
}
ResolveResult ProcessConversionsInBinaryOperatorResult(Expression left, Expression right, ResolveResult rr)
{
OperatorResolveResult orr = rr as OperatorResolveResult;
if (orr != null && orr.Operands.Count == 2) {
ProcessConversionResult(left, orr.Operands[0] as ConversionResolveResult);
ProcessConversionResult(right, orr.Operands[1] as ConversionResolveResult);
} else {
InvocationResolveResult irr = rr as InvocationResolveResult;
if (irr != null && irr.Arguments.Count == 2) {
ProcessConversionResult(left, irr.Arguments[0] as ConversionResolveResult);
ProcessConversionResult(right, irr.Arguments[1] as ConversionResolveResult);
}
}
return rr;
}
ResolveResult IAstVisitor.VisitCastExpression(CastExpression castExpression)
{
if (resolverEnabled) {
var targetType = ResolveType(castExpression.Type);
var expr = castExpression.Expression;
var rr = resolver.ResolveCast(targetType, Resolve(expr));
var crr = rr as ConversionResolveResult;
if (crr != null) {
Debug.Assert(!(crr is CastResolveResult));
ProcessConversion(expr, crr.Input, crr.Conversion, targetType);
rr = new CastResolveResult(crr);
}
return rr;
} else {
ScanChildren(castExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression)
{
if (resolverEnabled) {
Expression condition = conditionalExpression.Condition;
Expression trueExpr = conditionalExpression.TrueExpression;
Expression falseExpr = conditionalExpression.FalseExpression;
ResolveResult rr = resolver.ResolveConditional(Resolve(condition), Resolve(trueExpr), Resolve(falseExpr));
OperatorResolveResult corr = rr as OperatorResolveResult;
if (corr != null && corr.Operands.Count == 3) {
ProcessConversionResult(condition, corr.Operands[0] as ConversionResolveResult);
ProcessConversionResult(trueExpr, corr.Operands[1] as ConversionResolveResult);
ProcessConversionResult(falseExpr, corr.Operands[2] as ConversionResolveResult);
}
return rr;
} else {
ScanChildren(conditionalExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression)
{
if (resolverEnabled) {
return resolver.ResolveDefaultValue(ResolveType(defaultValueExpression.Type));
} else {
ScanChildren(defaultValueExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression)
{
if (resolverEnabled) {
ResolveResult rr = Resolve(directionExpression.Expression);
return new ByReferenceResolveResult(rr, directionExpression.FieldDirection == FieldDirection.Out);
} else {
ScanChildren(directionExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression)
{
if (resolverEnabled || NeedsResolvingDueToNamedArguments(indexerExpression)) {
Expression target = indexerExpression.Target;
ResolveResult targetResult = Resolve(target);
string[] argumentNames;
ResolveResult[] arguments = GetArguments(indexerExpression.Arguments, out argumentNames);
ResolveResult rr = resolver.ResolveIndexer(targetResult, arguments, argumentNames);
ArrayAccessResolveResult aarr = rr as ArrayAccessResolveResult;
if (aarr != null) {
MarkUnknownNamedArguments(indexerExpression.Arguments);
ProcessConversionResults(indexerExpression.Arguments, aarr.Indexes);
} else {
ProcessInvocationResult(target, indexerExpression.Arguments, rr);
}
return rr;
} else {
ScanChildren(indexerExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitIsExpression(IsExpression isExpression)
{
if (resolverEnabled) {
ResolveResult input = Resolve(isExpression.Expression);
IType targetType = ResolveType(isExpression.Type);
IType booleanType = resolver.Compilation.FindType(KnownTypeCode.Boolean);
return new TypeIsResolveResult(input, targetType, booleanType);
} else {
ScanChildren(isExpression);
return null;
}
}
// NamedArgumentExpression is "identifier: Expression"
ResolveResult IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression)
{
// The parent expression takes care of handling NamedArgumentExpressions
// by calling GetArguments().
// This method gets called only when scanning, or when the named argument is used
// in an invalid context.
if (resolverEnabled) {
return new NamedArgumentResolveResult(namedArgumentExpression.Name, Resolve(namedArgumentExpression.Expression));
} else {
Scan(namedArgumentExpression.Expression);
return null;
}
}
// NamedExpression is "identifier = Expression" in object initializers and attributes
ResolveResult IAstVisitor.VisitNamedExpression(NamedExpression namedExpression)
{
// The parent expression takes care of handling NamedExpression
// by calling HandleObjectInitializer() or HandleNamedExpression().
// This method gets called only when scanning, or when the named expression is used
// in an invalid context.
ScanChildren(namedExpression);
return null;
}
void HandleNamedExpression(NamedExpression namedExpression, List initializerStatements)
{
StoreCurrentState(namedExpression);
Expression rhs = namedExpression.Expression;
ResolveResult lhsRR = resolver.ResolveIdentifierInObjectInitializer(namedExpression.Name);
if (rhs is ArrayInitializerExpression) {
HandleObjectInitializer(lhsRR, (ArrayInitializerExpression)rhs, initializerStatements);
} else {
var rhsRR = Resolve(rhs);
var rr = resolver.ResolveAssignment(AssignmentOperatorType.Assign, lhsRR, rhsRR) as OperatorResolveResult;
if (rr != null) {
ProcessConversionResult(rhs, rr.Operands[1] as ConversionResolveResult);
initializerStatements.Add(rr);
}
}
StoreResult(namedExpression, lhsRR);
}
ResolveResult IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression)
{
return resolver.ResolvePrimitive(null);
}
ResolveResult IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
{
var typeResolveResult = Resolve(objectCreateExpression.Type);
if (typeResolveResult.IsError) {
ScanChildren (objectCreateExpression);
return typeResolveResult;
}
IType type = typeResolveResult.Type;
List initializerStatements = null;
var initializer = objectCreateExpression.Initializer;
if (!initializer.IsNull) {
initializerStatements = new List();
HandleObjectInitializer(new InitializedObjectResolveResult(type), initializer, initializerStatements);
}
string[] argumentNames;
ResolveResult[] arguments = GetArguments(objectCreateExpression.Arguments, out argumentNames);
ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames, false, initializerStatements);
if (arguments.Length == 1 && rr.Type.Kind == TypeKind.Delegate) {
MarkUnknownNamedArguments(objectCreateExpression.Arguments);
// Apply conversion to argument if it directly wraps the argument
// (but not when creating a delegate from a delegate, as then there would be a MGRR for .Invoke in between)
// This is necessary for lambda type inference.
var crr = rr as ConversionResolveResult;
if (crr != null) {
if (objectCreateExpression.Arguments.Count == 1)
ProcessConversionResult(objectCreateExpression.Arguments.Single(), crr);
// wrap the result so that the delegate creation is not handled as a reference
// to the target method - otherwise FindReferencedEntities would produce two results for
// the same delegate creation.
return new CastResolveResult(crr);
} else {
return rr;
}
} else {
// process conversions in all other cases
ProcessInvocationResult(null, objectCreateExpression.Arguments, rr);
return rr;
}
}
void HandleObjectInitializer(ResolveResult initializedObject, ArrayInitializerExpression initializer, List initializerStatements)
{
StoreCurrentState(initializer);
resolver = resolver.PushObjectInitializer(initializedObject);
foreach (Expression element in initializer.Elements) {
ArrayInitializerExpression aie = element as ArrayInitializerExpression;
if (aie != null) {
StoreCurrentState(aie);
// constructor argument list in collection initializer
ResolveResult[] addArguments = new ResolveResult[aie.Elements.Count];
int i = 0;
foreach (var addArgument in aie.Elements) {
addArguments[i++] = Resolve(addArgument);
}
MemberLookup memberLookup = resolver.CreateMemberLookup();
var addRR = memberLookup.Lookup(initializedObject, "Add", EmptyList.Instance, true);
var mgrr = addRR as MethodGroupResolveResult;
if (mgrr != null) {
OverloadResolution or = mgrr.PerformOverloadResolution(resolver.Compilation, addArguments, null, false, false, false, resolver.CheckForOverflow, resolver.conversions);
var invocationRR = or.CreateResolveResult(initializedObject);
StoreResult(aie, invocationRR);
ProcessInvocationResult(null, aie.Elements, invocationRR);
initializerStatements.Add(invocationRR);
} else {
StoreResult(aie, addRR);
}
} else if (element is NamedExpression) {
HandleNamedExpression((NamedExpression)element, initializerStatements);
} else {
// unknown kind of expression
Scan(element);
}
}
resolver = resolver.PopObjectInitializer();
StoreResult(initializer, voidResult);
}
ResolveResult IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression)
{
if (resolverEnabled) {
return Resolve(parenthesizedExpression.Expression);
} else {
Scan(parenthesizedExpression.Expression);
return null;
}
}
ResolveResult IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression)
{
if (resolverEnabled) {
ResolveResult target = Resolve(pointerReferenceExpression.Target);
ResolveResult deferencedTarget = resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, target);
List typeArguments = new List();
foreach (AstType typeArgument in pointerReferenceExpression.TypeArguments) {
typeArguments.Add(ResolveType(typeArgument));
}
return resolver.ResolveMemberAccess(deferencedTarget, pointerReferenceExpression.MemberName,
typeArguments,
GetNameLookupMode(pointerReferenceExpression));
} else {
ScanChildren(pointerReferenceExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression)
{
return resolver.ResolvePrimitive(primitiveExpression.Value);
}
ResolveResult IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression)
{
return resolver.ResolveSizeOf(ResolveType(sizeOfExpression.Type));
}
ResolveResult IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression)
{
ResolveAndProcessConversion(stackAllocExpression.CountExpression, resolver.Compilation.FindType(KnownTypeCode.Int32));
return new ResolveResult(new PointerType(ResolveType(stackAllocExpression.Type)));
}
ResolveResult IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression)
{
return resolver.ResolveThisReference();
}
ResolveResult IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression)
{
if (resolverEnabled) {
return resolver.ResolveTypeOf(ResolveType(typeOfExpression.Type));
} else {
Scan(typeOfExpression.Type);
return null;
}
}
ResolveResult IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression)
{
if (resolverEnabled) {
return Resolve(typeReferenceExpression.Type).ShallowClone();
} else {
Scan(typeReferenceExpression.Type);
return null;
}
}
ResolveResult IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
{
if (resolverEnabled) {
Expression expr = unaryOperatorExpression.Expression;
ResolveResult input = Resolve(expr);
ITypeDefinition inputTypeDef = input.Type.GetDefinition();
if (input.IsCompileTimeConstant && expr is PrimitiveExpression && inputTypeDef != null) {
// Special cases for int.MinValue and long.MinValue
if (inputTypeDef.KnownTypeCode == KnownTypeCode.UInt32 && 2147483648.Equals(input.ConstantValue)) {
return new ConstantResolveResult(resolver.Compilation.FindType(KnownTypeCode.Int32), -2147483648);
} else if (inputTypeDef.KnownTypeCode == KnownTypeCode.UInt64 && 9223372036854775808.Equals(input.ConstantValue)) {
return new ConstantResolveResult(resolver.Compilation.FindType(KnownTypeCode.Int64), -9223372036854775808);
}
}
ResolveResult rr = resolver.ResolveUnaryOperator(unaryOperatorExpression.Operator, input);
OperatorResolveResult uorr = rr as OperatorResolveResult;
if (uorr != null && uorr.Operands.Count == 1) {
ProcessConversionResult(expr, uorr.Operands[0] as ConversionResolveResult);
} else {
InvocationResolveResult irr = rr as InvocationResolveResult;
if (irr != null && irr.Arguments.Count == 1) {
ProcessConversionResult(expr, irr.Arguments[0] as ConversionResolveResult);
}
}
return rr;
} else {
ScanChildren(unaryOperatorExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression)
{
ScanChildren(undocumentedExpression);
IType resultType;
switch (undocumentedExpression.UndocumentedExpressionType) {
case UndocumentedExpressionType.ArgListAccess:
case UndocumentedExpressionType.ArgList:
resultType = resolver.Compilation.FindType(typeof(RuntimeArgumentHandle));
break;
case UndocumentedExpressionType.RefValue:
var tre = undocumentedExpression.Arguments.ElementAtOrDefault(1) as TypeReferenceExpression;
if (tre != null)
resultType = ResolveType(tre.Type);
else
resultType = SpecialType.UnknownType;
break;
case UndocumentedExpressionType.RefType:
resultType = resolver.Compilation.FindType(KnownTypeCode.Type);
break;
case UndocumentedExpressionType.MakeRef:
resultType = resolver.Compilation.FindType(typeof(TypedReference));
break;
default:
throw new InvalidOperationException("Invalid value for UndocumentedExpressionType");
}
return new ResolveResult(resultType);
}
#endregion
#region Visit Identifier/MemberReference/Invocation-Expression
// IdentifierExpression, MemberReferenceExpression and InvocationExpression
// are grouped together because they have to work together for
// "7.6.4.1 Identical simple names and type names" support
List ResolveTypeArguments(IEnumerable typeArguments)
{
List result = new List();
foreach (AstType typeArgument in typeArguments) {
result.Add(ResolveType(typeArgument));
}
return result;
}
///
/// Gets and resolves the arguments; unpacking any NamedArgumentExpressions.
///
///
/// Callers of GetArguments must also call either ProcessConversionsInInvocation or MarkUnknownNamedArguments
/// to ensure the named arguments get resolved.
/// Also, as named arguments get resolved by the parent node, the parent node must not scan
/// into the argument list without being resolved - see NeedsResolvingDueToNamedArguments().
///
ResolveResult[] GetArguments(IEnumerable argumentExpressions, out string[] argumentNames)
{
argumentNames = null;
ResolveResult[] arguments = new ResolveResult[argumentExpressions.Count()];
int i = 0;
foreach (AstNode argument in argumentExpressions) {
NamedArgumentExpression nae = argument as NamedArgumentExpression;
AstNode argumentValue;
if (nae != null) {
if (argumentNames == null)
argumentNames = new string[arguments.Length];
argumentNames[i] = nae.Name;
argumentValue = nae.Expression;
} else {
argumentValue = argument;
}
arguments[i++] = Resolve(argumentValue);
}
return arguments;
}
bool NeedsResolvingDueToNamedArguments(Expression nodeWithArguments)
{
for (AstNode child = nodeWithArguments.FirstChild; child != null; child = child.NextSibling) {
if (child is NamedArgumentExpression)
return true;
}
return false;
}
static NameLookupMode GetNameLookupMode(Expression expr)
{
InvocationExpression ie = expr.Parent as InvocationExpression;
if (ie != null && ie.Target == expr)
return NameLookupMode.InvocationTarget;
else
return NameLookupMode.Expression;
}
///
/// Gets whether 'rr' is considered a static access on the target identifier.
///
/// Resolve Result of the MemberReferenceExpression
/// Resolve Result of the InvocationExpression
bool IsStaticResult(ResolveResult rr, ResolveResult invocationRR)
{
if (rr is TypeResolveResult)
return true;
MemberResolveResult mrr = (rr is MethodGroupResolveResult ? invocationRR : rr) as MemberResolveResult;
return mrr != null && mrr.Member.IsStatic;
}
ResolveResult IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression)
{
// Note: this method is not called when it occurs in a situation where an ambiguity between
// simple names and type names might occur.
if (resolverEnabled) {
var typeArguments = ResolveTypeArguments(identifierExpression.TypeArguments);
var lookupMode = GetNameLookupMode(identifierExpression);
return resolver.LookupSimpleNameOrTypeName(
identifierExpression.Identifier, typeArguments, lookupMode);
} else {
ScanChildren(identifierExpression);
return null;
}
}
ResolveResult IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
{
// target = Resolve(identifierExpression = memberReferenceExpression.Target)
// trr = ResolveType(identifierExpression)
// rr = Resolve(memberReferenceExpression)
IdentifierExpression identifierExpression = memberReferenceExpression.Target as IdentifierExpression;
if (identifierExpression != null && identifierExpression.TypeArguments.Count == 0) {
// Special handling for §7.6.4.1 Identicial simple names and type names
StoreCurrentState(identifierExpression);
ResolveResult target = resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList.Instance);
TypeResolveResult trr;
if (resolver.IsVariableReferenceWithSameType(target, identifierExpression.Identifier, out trr)) {
// It's ambiguous
ResolveResult rr = ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression);
ResolveResult simpleNameRR = IsStaticResult(rr, null) ? trr : target;
Log.WriteLine("Ambiguous simple name '{0}' was resolved to {1}", identifierExpression, simpleNameRR);
StoreResult(identifierExpression, simpleNameRR);
return rr;
} else {
// It's not ambiguous
Log.WriteLine("Simple name '{0}' was resolved to {1}", identifierExpression, target);
StoreResult(identifierExpression, target);
return ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression);
}
} else {
// Regular code path
if (resolverEnabled) {
ResolveResult target = Resolve(memberReferenceExpression.Target);
return ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression);
} else {
ScanChildren(memberReferenceExpression);
return null;
}
}
}
ResolveResult ResolveMemberReferenceOnGivenTarget(ResolveResult target, MemberReferenceExpression memberReferenceExpression)
{
var typeArguments = ResolveTypeArguments(memberReferenceExpression.TypeArguments);
return resolver.ResolveMemberAccess(
target, memberReferenceExpression.MemberName, typeArguments,
GetNameLookupMode(memberReferenceExpression));
}
ResolveResult IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression)
{
// rr = Resolve(invocationExpression)
// target = Resolve(memberReferenceExpression = invocationExpression.Target)
// idRR = Resolve(identifierExpression = memberReferenceExpression.Target)
// trr = ResolveType(identifierExpression)
MemberReferenceExpression mre = invocationExpression.Target as MemberReferenceExpression;
IdentifierExpression identifierExpression = mre != null ? mre.Target as IdentifierExpression : null;
if (identifierExpression != null && identifierExpression.TypeArguments.Count == 0) {
// Special handling for §7.6.4.1 Identicial simple names and type names
StoreCurrentState(identifierExpression);
StoreCurrentState(mre);
ResolveResult idRR = resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList.Instance);
ResolveResult target = ResolveMemberReferenceOnGivenTarget(idRR, mre);
Log.WriteLine("Member reference '{0}' on potentially-ambiguous simple-name was resolved to {1}", mre, target);
StoreResult(mre, target);
TypeResolveResult trr;
if (resolver.IsVariableReferenceWithSameType(idRR, identifierExpression.Identifier, out trr)) {
// It's ambiguous
ResolveResult rr = ResolveInvocationOnGivenTarget(target, invocationExpression);
ResolveResult simpleNameRR = IsStaticResult(target, rr) ? trr : idRR;
Log.WriteLine("Ambiguous simple name '{0}' was resolved to {1}",
identifierExpression, simpleNameRR);
StoreResult(identifierExpression, simpleNameRR);
return rr;
} else {
// It's not ambiguous
Log.WriteLine("Simple name '{0}' was resolved to {1}", identifierExpression, idRR);
StoreResult(identifierExpression, idRR);
return ResolveInvocationOnGivenTarget(target, invocationExpression);
}
} else {
// Regular code path
if (resolverEnabled || NeedsResolvingDueToNamedArguments(invocationExpression)) {
ResolveResult target = Resolve(invocationExpression.Target);
return ResolveInvocationOnGivenTarget(target, invocationExpression);
} else {
ScanChildren(invocationExpression);
return null;
}
}
}
ResolveResult ResolveInvocationOnGivenTarget(ResolveResult target, InvocationExpression invocationExpression)
{
string[] argumentNames;
ResolveResult[] arguments = GetArguments(invocationExpression.Arguments, out argumentNames);
ResolveResult rr = resolver.ResolveInvocation(target, arguments, argumentNames);
ProcessInvocationResult(invocationExpression.Target, invocationExpression.Arguments, rr);
return rr;
}
#endregion
#region Lamdbas / Anonymous Functions
ResolveResult IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
{
return HandleExplicitlyTypedLambda(
anonymousMethodExpression.Parameters, anonymousMethodExpression.Body,
isAnonymousMethod: true,
hasParameterList: anonymousMethodExpression.HasParameterList,
isAsync: anonymousMethodExpression.IsAsync);
}
ResolveResult IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression)
{
bool isExplicitlyTyped = false;
bool isImplicitlyTyped = false;
foreach (var p in lambdaExpression.Parameters) {
isImplicitlyTyped |= p.Type.IsNull;
isExplicitlyTyped |= !p.Type.IsNull;
}
if (isExplicitlyTyped || !isImplicitlyTyped) {
return HandleExplicitlyTypedLambda(
lambdaExpression.Parameters, lambdaExpression.Body,
isAnonymousMethod: false, hasParameterList: true, isAsync: lambdaExpression.IsAsync);
} else {
return new ImplicitlyTypedLambda(lambdaExpression, this);
}
}
#region Explicitly typed
ExplicitlyTypedLambda HandleExplicitlyTypedLambda(
AstNodeCollection parameterDeclarations,
AstNode body, bool isAnonymousMethod, bool hasParameterList, bool isAsync)
{
CSharpResolver oldResolver = resolver;
List parameters = (hasParameterList || parameterDeclarations.Any()) ? new List() : null;
//bool oldIsWithinLambdaExpression = resolver.IsWithinLambdaExpression;
resolver = resolver.WithIsWithinLambdaExpression(true);
foreach (var pd in parameterDeclarations) {
IType type = ResolveType(pd.Type);
if (pd.ParameterModifier == ParameterModifier.Ref || pd.ParameterModifier == ParameterModifier.Out)
type = new ByReferenceType(type);
IParameter p = new DefaultParameter(type, pd.Name,
region: MakeRegion(pd),
isRef: pd.ParameterModifier == ParameterModifier.Ref,
isOut: pd.ParameterModifier == ParameterModifier.Out);
// The parameter declaration must be scanned in the current context (without the new parameter)
// in order to be consistent with the context in which we resolved pd.Type.
StoreCurrentState(pd);
StoreResult(pd, new LocalResolveResult(p));
ScanChildren(pd);
resolver = resolver.AddVariable(p);
parameters.Add(p);
}
var lambda = new ExplicitlyTypedLambda(parameters, isAnonymousMethod, isAsync, resolver, this, body);
// Don't scan the lambda body here - we'll do that later when analyzing the ExplicitlyTypedLambda.
resolver = oldResolver;
return lambda;
}
DomRegion MakeRegion(AstNode node)
{
if (unresolvedFile != null)
return new DomRegion(unresolvedFile.FileName, node.StartLocation, node.EndLocation);
else
return node.GetRegion();
}
sealed class ExplicitlyTypedLambda : LambdaBase
{
readonly IList parameters;
readonly bool isAnonymousMethod;
readonly bool isAsync;
CSharpResolver storedContext;
ResolveVisitor visitor;
AstNode body;
ResolveResult bodyRR;
IType inferredReturnType;
IList returnExpressions;
IList returnValues;
bool isValidAsVoidMethod;
bool isEndpointUnreachable;
// The actual return type is set when the lambda is applied by the conversion.
// For async lambdas, this includes the task type
IType actualReturnType;
internal override bool IsUndecided {
get { return actualReturnType == null; }
}
internal override AstNode LambdaExpression {
get { return body.Parent; }
}
internal override AstNode BodyExpression {
get { return body; }
}
public override ResolveResult Body {
get {
if (bodyRR != null)
return bodyRR;
if (body is Expression) {
Analyze();
if (returnValues.Count == 1) {
bodyRR = returnValues[0];
if (actualReturnType != null) {
IType unpackedActualReturnType = isAsync ? visitor.UnpackTask(actualReturnType) : actualReturnType;
if (unpackedActualReturnType.Kind != TypeKind.Void) {
var conv = storedContext.conversions.ImplicitConversion(bodyRR, unpackedActualReturnType);
if (!conv.IsIdentityConversion)
bodyRR = new ConversionResolveResult(unpackedActualReturnType, bodyRR, conv, storedContext.CheckForOverflow);
}
}
return bodyRR;
}
}
return bodyRR = visitor.voidResult;
}
}
public ExplicitlyTypedLambda(IList parameters, bool isAnonymousMethod, bool isAsync, CSharpResolver storedContext, ResolveVisitor visitor, AstNode body)
{
this.parameters = parameters;
this.isAnonymousMethod = isAnonymousMethod;
this.isAsync = isAsync;
this.storedContext = storedContext;
this.visitor = visitor;
this.body = body;
if (visitor.undecidedLambdas == null)
visitor.undecidedLambdas = new List();
visitor.undecidedLambdas.Add(this);
Log.WriteLine("Added undecided explicitly-typed lambda: " + this.LambdaExpression);
}
public override IList Parameters {
get {
return parameters ?? EmptyList.Instance;
}
}
bool Analyze()
{
// If it's not already analyzed
if (inferredReturnType == null) {
Log.WriteLine("Analyzing " + this.LambdaExpression + "...");
Log.Indent();
visitor.ResetContext(
storedContext,
delegate {
var oldNavigator = visitor.navigator;
visitor.navigator = new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Resolve, oldNavigator);
visitor.AnalyzeLambda(body, isAsync, out isValidAsVoidMethod, out isEndpointUnreachable, out inferredReturnType, out returnExpressions, out returnValues);
visitor.navigator = oldNavigator;
});
Log.Unindent();
Log.WriteLine("Finished analyzing " + this.LambdaExpression);
if (inferredReturnType == null)
throw new InvalidOperationException("AnalyzeLambda() didn't set inferredReturnType");
}
return true;
}
public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions)
{
Log.WriteLine("Testing validity of {0} for return-type {1}...", this, returnType);
Log.Indent();
bool valid = Analyze() && IsValidLambda(isValidAsVoidMethod, isEndpointUnreachable, isAsync, returnValues, returnType, conversions);
Log.Unindent();
Log.WriteLine("{0} is {1} for return-type {2}", this, valid ? "valid" : "invalid", returnType);
return new AnonymousFunctionConversion(returnType, this, valid);
}
public override IType GetInferredReturnType(IType[] parameterTypes)
{
Analyze();
return inferredReturnType;
}
public override IType ReturnType {
get {
return actualReturnType ?? SpecialType.UnknownType;
}
}
public override bool IsImplicitlyTyped {
get { return false; }
}
public override bool IsAsync {
get { return isAsync; }
}
public override bool IsAnonymousMethod {
get { return isAnonymousMethod; }
}
public override bool HasParameterList {
get { return parameters != null; }
}
public override string ToString()
{
return "[ExplicitlyTypedLambda " + this.LambdaExpression + "]";
}
public void ApplyReturnType(ResolveVisitor parentVisitor, IType returnType)
{
if (returnType == null)
throw new ArgumentNullException("returnType");
if (parentVisitor != visitor) {
// Explicitly typed lambdas do not use a nested visitor
throw new InvalidOperationException();
}
if (actualReturnType != null) {
if (actualReturnType.Equals(returnType))
return; // return type already set
throw new InvalidOperationException("inconsistent return types for explicitly-typed lambda");
}
actualReturnType = returnType;
visitor.undecidedLambdas.Remove(this);
Analyze();
IType unpackedReturnType = isAsync ? visitor.UnpackTask(returnType) : returnType;
Log.WriteLine("Applying return type {0} to explicitly-typed lambda {1}", unpackedReturnType, this.LambdaExpression);
if (unpackedReturnType.Kind != TypeKind.Void || body is BlockStatement) {
for (int i = 0; i < returnExpressions.Count; i++) {
visitor.ProcessConversion(returnExpressions[i], returnValues[i], unpackedReturnType);
}
}
}
internal override void EnforceMerge(ResolveVisitor parentVisitor)
{
ApplyReturnType(parentVisitor, SpecialType.UnknownType);
}
}
#endregion
#region Implicitly typed
// Implicitly-typed lambdas are really complex, as the lambda depends on the target type (the delegate to which
// the lambda is converted), but figuring out the target type might involve overload resolution (for method
// calls in which the lambda is used as argument), which requires knowledge about the lamdba.
//
// The implementation in NRefactory works like this:
// 1. The lambda resolves to a ImplicitlyTypedLambda (derived from LambdaResolveResult).
// The lambda body is not resolved yet (one of the few places where ResolveVisitor
// deviates from the usual depth-first AST traversal).
// 2. The parent statement is resolved as usual. This might require analyzing the lambda in detail (for example
// as part of overload resolution). Such analysis happens using LambdaResolveResult.IsValid, where the caller
// (i.e. the overload resolution algorithm) supplies the parameter types to the lambda body. For every IsValid()
// call, a nested LambdaTypeHypothesis is constructed for analyzing the lambda using the supplied type assignment.
// Multiple IsValid() calls may use several LambdaTypeHypothesis instances, one for each set of parameter types.
// 3. When the resolver reports the conversions that occurred as part of the parent statement (as with any
// conversions), the results from the LambdaTypeHypothesis corresponding to the actually chosen
// conversion are merged into the main resolver.
// 4. LambdaResolveResult.Body is set to the main resolve result from the chosen nested resolver. I think this
// is the only place where NRefactory is mutating a ResolveResult (normally all resolve results are immutable).
// As this step is guaranteed to occur before the resolver returns the LamdbaResolveResult to user code, the
// mutation shouldn't cause any problems.
sealed class ImplicitlyTypedLambda : LambdaBase
{
readonly LambdaExpression lambda;
readonly QuerySelectClause selectClause;
readonly CSharpResolver storedContext;
readonly CSharpUnresolvedFile unresolvedFile;
readonly List hypotheses = new List();
internal IList parameters = new List();
internal IType actualReturnType;
internal LambdaTypeHypothesis winningHypothesis;
internal ResolveResult bodyResult;
internal readonly ResolveVisitor parentVisitor;
internal override bool IsUndecided {
get { return winningHypothesis == null; }
}
internal override AstNode LambdaExpression {
get {
if (selectClause != null)
return selectClause.Expression;
else
return lambda;
}
}
internal override AstNode BodyExpression {
get {
if (selectClause != null)
return selectClause.Expression;
else
return lambda.Body;
}
}
public override ResolveResult Body {
get { return bodyResult; }
}
private ImplicitlyTypedLambda(ResolveVisitor parentVisitor)
{
this.parentVisitor = parentVisitor;
this.storedContext = parentVisitor.resolver;
this.unresolvedFile = parentVisitor.unresolvedFile;
this.bodyResult = parentVisitor.voidResult;
}
public ImplicitlyTypedLambda(LambdaExpression lambda, ResolveVisitor parentVisitor)
: this(parentVisitor)
{
this.lambda = lambda;
foreach (var pd in lambda.Parameters) {
parameters.Add(new DefaultParameter(SpecialType.UnknownType, pd.Name, region: parentVisitor.MakeRegion(pd)));
}
RegisterUndecidedLambda();
}
public ImplicitlyTypedLambda(QuerySelectClause selectClause, IEnumerable parameters, ResolveVisitor parentVisitor)
: this(parentVisitor)
{
this.selectClause = selectClause;
foreach (IParameter p in parameters)
this.parameters.Add(p);
RegisterUndecidedLambda();
}
void RegisterUndecidedLambda()
{
if (parentVisitor.undecidedLambdas == null)
parentVisitor.undecidedLambdas = new List();
parentVisitor.undecidedLambdas.Add(this);
Log.WriteLine("Added undecided implicitly-typed lambda: " + this.LambdaExpression);
}
public override IList Parameters {
get { return parameters; }
}
public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions)
{
Log.WriteLine("Testing validity of {0} for parameters ({1}) and return-type {2}...",
this, string.Join(", ", parameterTypes), returnType);
Log.Indent();
var hypothesis = GetHypothesis(parameterTypes);
Conversion c = hypothesis.IsValid(returnType, conversions);
Log.Unindent();
Log.WriteLine("{0} is {1} for return-type {2}", hypothesis, c.IsValid ? "valid" : "invalid", returnType);
return c;
}
public override IType GetInferredReturnType(IType[] parameterTypes)
{
return GetHypothesis(parameterTypes).inferredReturnType;
}
LambdaTypeHypothesis GetHypothesis(IType[] parameterTypes)
{
if (parameterTypes.Length != parameters.Count)
throw new ArgumentException("Incorrect parameter type count");
foreach (var h in hypotheses) {
bool ok = true;
for (int i = 0; i < parameterTypes.Length; i++) {
if (!parameterTypes[i].Equals(h.parameterTypes[i])) {
ok = false;
break;
}
}
if (ok)
return h;
}
ResolveVisitor visitor = new ResolveVisitor(storedContext, unresolvedFile);
var newHypothesis = new LambdaTypeHypothesis(this, parameterTypes, visitor, lambda != null ? lambda.Parameters : null, storedContext);
hypotheses.Add(newHypothesis);
return newHypothesis;
}
///
/// Get any hypothesis for this lambda.
/// This method is used as fallback if the lambda isn't merged the normal way (AnonymousFunctionConversion)
///
internal LambdaTypeHypothesis GetAnyHypothesis()
{
if (winningHypothesis != null)
return winningHypothesis;
if (hypotheses.Count == 0) {
// make a new hypothesis with unknown parameter types
IType[] parameterTypes = new IType[parameters.Count];
for (int i = 0; i < parameterTypes.Length; i++) {
parameterTypes[i] = SpecialType.UnknownType;
}
return GetHypothesis(parameterTypes);
} else {
// We have the choice, so pick the hypothesis with the least missing parameter types
LambdaTypeHypothesis bestHypothesis = hypotheses[0];
int bestHypothesisUnknownParameters = bestHypothesis.CountUnknownParameters();
for (int i = 1; i < hypotheses.Count; i++) {
int c = hypotheses[i].CountUnknownParameters();
if (c < bestHypothesisUnknownParameters ||
(c == bestHypothesisUnknownParameters && hypotheses[i].success && !bestHypothesis.success))
{
bestHypothesis = hypotheses[i];
bestHypothesisUnknownParameters = c;
}
}
return bestHypothesis;
}
}
internal override void EnforceMerge(ResolveVisitor parentVisitor)
{
GetAnyHypothesis().MergeInto(parentVisitor, SpecialType.UnknownType);
}
public override IType ReturnType {
get { return actualReturnType ?? SpecialType.UnknownType; }
}
public override bool IsImplicitlyTyped {
get { return true; }
}
public override bool IsAnonymousMethod {
get { return false; }
}
public override bool HasParameterList {
get { return true; }
}
public override bool IsAsync {
get { return lambda != null && lambda.IsAsync; }
}
public override string ToString()
{
return "[ImplicitlyTypedLambda " + this.LambdaExpression + "]";
}
}
///
/// Every possible set of parameter types gets its own 'hypothetical world'.
/// It uses a nested ResolveVisitor that has its own resolve cache, so that resolve results cannot leave the hypothetical world.
///
/// Only after overload resolution is applied and the actual parameter types are known, the winning hypothesis will be merged
/// with the parent ResolveVisitor.
/// This is done when the AnonymousFunctionConversion is applied on the parent visitor.
///
sealed class LambdaTypeHypothesis : IResolveVisitorNavigator
{
readonly ImplicitlyTypedLambda lambda;
readonly IParameter[] lambdaParameters;
internal readonly IType[] parameterTypes;
readonly ResolveVisitor visitor;
readonly CSharpResolver storedContext;
internal readonly IType inferredReturnType;
IList returnExpressions;
IList returnValues;
bool isValidAsVoidMethod;
bool isEndpointUnreachable;
internal bool success;
public LambdaTypeHypothesis(ImplicitlyTypedLambda lambda, IType[] parameterTypes, ResolveVisitor visitor,
ICollection parameterDeclarations, CSharpResolver storedContext)
{
Debug.Assert(parameterTypes.Length == lambda.Parameters.Count);
this.lambda = lambda;
this.parameterTypes = parameterTypes;
this.visitor = visitor;
this.storedContext = storedContext;
visitor.SetNavigator(this);
Log.WriteLine("Analyzing " + ToString() + "...");
Log.Indent();
CSharpResolver oldResolver = visitor.resolver;
visitor.resolver = visitor.resolver.WithIsWithinLambdaExpression(true);
lambdaParameters = new IParameter[parameterTypes.Length];
if (parameterDeclarations != null) {
int i = 0;
foreach (var pd in parameterDeclarations) {
lambdaParameters[i] = new DefaultParameter(parameterTypes[i], pd.Name, region: visitor.MakeRegion(pd));
visitor.resolver = visitor.resolver.AddVariable(lambdaParameters[i]);
i++;
visitor.Scan(pd);
}
} else {
for (int i = 0; i < parameterTypes.Length; i++) {
var p = lambda.Parameters[i];
lambdaParameters[i] = new DefaultParameter(parameterTypes[i], p.Name, region: p.Region);
visitor.resolver = visitor.resolver.AddVariable(lambdaParameters[i]);
}
}
success = true;
visitor.AnalyzeLambda(lambda.BodyExpression, lambda.IsAsync, out isValidAsVoidMethod, out isEndpointUnreachable, out inferredReturnType, out returnExpressions, out returnValues);
visitor.resolver = oldResolver;
Log.Unindent();
Log.WriteLine("Finished analyzing " + ToString());
}
ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node)
{
return ResolveVisitorNavigationMode.Resolve;
}
void IResolveVisitorNavigator.Resolved(AstNode node, ResolveResult result)
{
if (result.IsError)
success = false;
}
void IResolveVisitorNavigator.ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType)
{
success &= conversion.IsValid;
}
internal int CountUnknownParameters()
{
int c = 0;
foreach (IType t in parameterTypes) {
if (t.Kind == TypeKind.Unknown)
c++;
}
return c;
}
public Conversion IsValid(IType returnType, CSharpConversions conversions)
{
bool valid = success && IsValidLambda(isValidAsVoidMethod, isEndpointUnreachable, lambda.IsAsync, returnValues, returnType, conversions);
return new AnonymousFunctionConversion(returnType, this, valid);
}
public void MergeInto(ResolveVisitor parentVisitor, IType returnType)
{
if (returnType == null)
throw new ArgumentNullException("returnType");
if (parentVisitor != lambda.parentVisitor)
throw new InvalidOperationException("parent visitor mismatch");
if (lambda.winningHypothesis == this)
return;
else if (lambda.winningHypothesis != null)
throw new InvalidOperationException("Trying to merge conflicting hypotheses");
lambda.actualReturnType = returnType;
if (lambda.IsAsync)
returnType = parentVisitor.UnpackTask(returnType);
lambda.winningHypothesis = this;
lambda.parameters = lambdaParameters; // replace untyped parameters with typed parameters
if (lambda.BodyExpression is Expression && returnValues.Count == 1) {
lambda.bodyResult = returnValues[0];
if (returnType.Kind != TypeKind.Void) {
var conv = storedContext.conversions.ImplicitConversion(lambda.bodyResult, returnType);
if (!conv.IsIdentityConversion)
lambda.bodyResult = new ConversionResolveResult(returnType, lambda.bodyResult, conv, storedContext.CheckForOverflow);
}
}
Log.WriteLine("Applying return type {0} to implicitly-typed lambda {1}", returnType, lambda.LambdaExpression);
if (returnType.Kind != TypeKind.Void || lambda.BodyExpression is Statement) {
for (int i = 0; i < returnExpressions.Count; i++) {
visitor.ProcessConversion(returnExpressions[i], returnValues[i], returnType);
}
}
visitor.MergeUndecidedLambdas();
Log.WriteLine("Merging " + ToString());
foreach (var pair in visitor.resolverBeforeDict) {
Debug.Assert(!parentVisitor.resolverBeforeDict.ContainsKey(pair.Key));
parentVisitor.resolverBeforeDict[pair.Key] = pair.Value;
}
foreach (var pair in visitor.resolverAfterDict) {
Debug.Assert(!parentVisitor.resolverAfterDict.ContainsKey(pair.Key));
parentVisitor.resolverAfterDict[pair.Key] = pair.Value;
}
foreach (var pair in visitor.resolveResultCache) {
parentVisitor.StoreResult(pair.Key, pair.Value);
}
parentVisitor.ImportConversions(visitor);
parentVisitor.undecidedLambdas.Remove(lambda);
}
public override string ToString()
{
StringBuilder b = new StringBuilder();
b.Append("[LambdaTypeHypothesis (");
for (int i = 0; i < parameterTypes.Length; i++) {
if (i > 0) b.Append(", ");
b.Append(parameterTypes[i]);
b.Append(' ');
b.Append(lambda.Parameters[i].Name);
}
b.Append(") => ");
b.Append(lambda.BodyExpression.ToString());
b.Append(']');
return b.ToString();
}
}
#endregion
#region MergeUndecidedLambdas
abstract class LambdaBase : LambdaResolveResult
{
internal abstract bool IsUndecided { get; }
internal abstract AstNode LambdaExpression { get; }
internal abstract AstNode BodyExpression { get; }
internal abstract void EnforceMerge(ResolveVisitor parentVisitor);
public override ResolveResult ShallowClone()
{
if (IsUndecided)
throw new NotSupportedException();
return base.ShallowClone();
}
}
void MergeUndecidedLambdas()
{
if (undecidedLambdas == null || undecidedLambdas.Count == 0)
return;
Log.WriteLine("MergeUndecidedLambdas()...");
Log.Indent();
while (undecidedLambdas.Count > 0) {
LambdaBase lambda = undecidedLambdas[0];
// may happen caused by parse error l =>
if (lambda.LambdaExpression == null) {
undecidedLambdas.Remove (lambda);
continue;
}
ResolveParentForConversion(lambda.LambdaExpression);
if (lambda.IsUndecided) {
// Lambda wasn't merged by resolving its parent -> enforce merging
Log.WriteLine("Lambda wasn't merged by conversion - enforce merging");
lambda.EnforceMerge(this);
}
}
Log.Unindent();
Log.WriteLine("MergeUndecidedLambdas() finished.");
}
void ResolveParentForConversion(AstNode expression)
{
AstNode parent = expression.Parent;
// Continue going upwards until we find a node that can be resolved and provides
// an expected type.
while (ParenthesizedExpression.ActsAsParenthesizedExpression(parent) || CSharpAstResolver.IsUnresolvableNode(parent)) {
parent = parent.Parent;
}
CSharpResolver storedResolver;
if (parent != null && resolverBeforeDict.TryGetValue(parent, out storedResolver)) {
Log.WriteLine("Trying to resolve '" + parent + "' in order to find the conversion applied to '" + expression + "'...");
Log.Indent();
ResetContext(storedResolver, delegate { Resolve(parent); });
Log.Unindent();
} else {
Log.WriteLine("Could not find a suitable parent for '" + expression + "'");
}
}
#endregion
#region AnalyzeLambda
IType GetTaskType(IType resultType)
{
if (resultType.Kind == TypeKind.Unknown)
return SpecialType.UnknownType;
if (resultType.Kind == TypeKind.Void)
return resolver.Compilation.FindType(KnownTypeCode.Task);
ITypeDefinition def = resolver.Compilation.FindType(KnownTypeCode.TaskOfT).GetDefinition();
if (def != null)
return new ParameterizedType(def, new[] { resultType });
else
return SpecialType.UnknownType;
}
void AnalyzeLambda(AstNode body, bool isAsync, out bool isValidAsVoidMethod, out bool isEndpointUnreachable, out IType inferredReturnType, out IList returnExpressions, out IList returnValues)
{
isEndpointUnreachable = false;
Expression expr = body as Expression;
if (expr != null) {
isValidAsVoidMethod = ExpressionPermittedAsStatement(expr);
returnExpressions = new [] { expr };
returnValues = new[] { Resolve(expr) };
inferredReturnType = returnValues[0].Type;
} else {
Scan(body);
AnalyzeLambdaVisitor alv = new AnalyzeLambdaVisitor();
body.AcceptVisitor(alv);
isValidAsVoidMethod = (alv.ReturnExpressions.Count == 0);
if (alv.HasVoidReturnStatements) {
returnExpressions = EmptyList.Instance;
returnValues = EmptyList.Instance;
inferredReturnType = resolver.Compilation.FindType(KnownTypeCode.Void);
} else {
returnExpressions = alv.ReturnExpressions;
returnValues = new ResolveResult[returnExpressions.Count];
for (int i = 0; i < returnValues.Count; i++) {
returnValues[i] = resolveResultCache[returnExpressions[i]];
}
// async lambdas without return statements are resolved as Task return types.
if (returnExpressions.Count == 0 && isAsync) {
inferredReturnType = resolver.Compilation.FindType(KnownTypeCode.Task);
Log.WriteLine("Lambda return type was inferred to: " + inferredReturnType);
return;
}
TypeInference ti = new TypeInference(resolver.Compilation, resolver.conversions);
bool tiSuccess;
inferredReturnType = ti.GetBestCommonType(returnValues, out tiSuccess);
// Failure to infer a return type does not make the lambda invalid,
// so we can ignore the 'tiSuccess' value
if (isValidAsVoidMethod && returnExpressions.Count == 0 && body is Statement) {
var reachabilityAnalysis = ReachabilityAnalysis.Create(
(Statement)body, (node, _) => resolveResultCache[node],
resolver.CurrentTypeResolveContext, cancellationToken);
isEndpointUnreachable = !reachabilityAnalysis.IsEndpointReachable((Statement)body);
}
}
}
if (isAsync)
inferredReturnType = GetTaskType(inferredReturnType);
Log.WriteLine("Lambda return type was inferred to: " + inferredReturnType);
}
static bool ExpressionPermittedAsStatement(Expression expr)
{
UnaryOperatorExpression uoe = expr as UnaryOperatorExpression;
if (uoe != null) {
switch (uoe.Operator) {
case UnaryOperatorType.Increment:
case UnaryOperatorType.Decrement:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.PostDecrement:
case UnaryOperatorType.Await:
return true;
default:
return false;
}
}
return expr is InvocationExpression
|| expr is ObjectCreateExpression
|| expr is AssignmentExpression;
}
static bool IsValidLambda(bool isValidAsVoidMethod, bool isEndpointUnreachable, bool isAsync, IList returnValues, IType returnType, CSharpConversions conversions)
{
if (returnType.Kind == TypeKind.Void) {
// Lambdas that are valid statement lambdas or expression lambdas with a statement-expression
// can be converted to delegates with void return type.
// This holds for both async and regular lambdas.
return isValidAsVoidMethod;
} else if (isAsync && TaskType.IsTask(returnType) && returnType.TypeParameterCount == 0) {
// Additionally, async lambdas with the above property can be converted to non-generic Task.
return isValidAsVoidMethod;
} else {
if (returnValues.Count == 0)
return isEndpointUnreachable;
if (isAsync) {
// async lambdas must return Task
if (!(TaskType.IsTask(returnType) && returnType.TypeParameterCount == 1))
return false;
// unpack Task for testing the implicit conversions
returnType = ((ParameterizedType)returnType).GetTypeArgument(0);
}
foreach (ResolveResult returnRR in returnValues) {
if (!conversions.ImplicitConversion(returnRR, returnType).IsValid)
return false;
}
return true;
}
}
IType UnpackTask(IType type)
{
return TaskType.UnpackTask(resolver.Compilation, type);
}
sealed class AnalyzeLambdaVisitor : DepthFirstAstVisitor
{
public bool HasVoidReturnStatements;
public List ReturnExpressions = new List();
public override void VisitReturnStatement(ReturnStatement returnStatement)
{
Expression expr = returnStatement.Expression;
if (expr.IsNull) {
HasVoidReturnStatements = true;
} else {
ReturnExpressions.Add(expr);
}
}
public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
{
// don't go into nested lambdas
}
public override void VisitLambdaExpression(LambdaExpression lambdaExpression)
{
// don't go into nested lambdas
}
}
#endregion
#endregion
#region ForEach Statement
ResolveResult IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement)
{
var compilation = resolver.Compilation;
ResolveResult expression = Resolve(foreachStatement.InExpression);
bool isImplicitlyTypedVariable = foreachStatement.VariableType.IsVar();
var memberLookup = resolver.CreateMemberLookup();
IType collectionType, enumeratorType, elementType;
ResolveResult getEnumeratorInvocation;
ResolveResult currentRR = null;
// C# 4.0 spec: §8.8.4 The foreach statement
if (expression.Type.Kind == TypeKind.Array || expression.Type.Kind == TypeKind.Dynamic) {
collectionType = compilation.FindType(KnownTypeCode.IEnumerable);
enumeratorType = compilation.FindType(KnownTypeCode.IEnumerator);
if (expression.Type.Kind == TypeKind.Array) {
elementType = ((ArrayType)expression.Type).ElementType;
} else {
elementType = isImplicitlyTypedVariable ? SpecialType.Dynamic : compilation.FindType(KnownTypeCode.Object);
}
getEnumeratorInvocation = resolver.ResolveCast(collectionType, expression);
getEnumeratorInvocation = resolver.ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList.Instance, NameLookupMode.InvocationTarget);
getEnumeratorInvocation = resolver.ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]);
} else {
var getEnumeratorMethodGroup = memberLookup.Lookup(expression, "GetEnumerator", EmptyList.Instance, true) as MethodGroupResolveResult;
if (getEnumeratorMethodGroup != null) {
var or = getEnumeratorMethodGroup.PerformOverloadResolution(
compilation, new ResolveResult[0],
allowExtensionMethods: false, allowExpandingParams: false, allowOptionalParameters: false);
if (or.FoundApplicableCandidate && !or.IsAmbiguous && !or.BestCandidate.IsStatic && or.BestCandidate.IsPublic) {
collectionType = expression.Type;
getEnumeratorInvocation = or.CreateResolveResult(expression);
enumeratorType = getEnumeratorInvocation.Type;
currentRR = memberLookup.Lookup(new ResolveResult(enumeratorType), "Current", EmptyList.Instance, false);
elementType = currentRR.Type;
} else {
CheckForEnumerableInterface(expression, out collectionType, out enumeratorType, out elementType, out getEnumeratorInvocation);
}
} else {
CheckForEnumerableInterface(expression, out collectionType, out enumeratorType, out elementType, out getEnumeratorInvocation);
}
}
IMethod moveNextMethod = null;
var moveNextMethodGroup = memberLookup.Lookup(new ResolveResult(enumeratorType), "MoveNext", EmptyList.Instance, false) as MethodGroupResolveResult;
if (moveNextMethodGroup != null) {
var or = moveNextMethodGroup.PerformOverloadResolution(
compilation, new ResolveResult[0],
allowExtensionMethods: false, allowExpandingParams: false, allowOptionalParameters: false);
moveNextMethod = or.GetBestCandidateWithSubstitutedTypeArguments() as IMethod;
}
if (currentRR == null)
currentRR = memberLookup.Lookup(new ResolveResult(enumeratorType), "Current", EmptyList.Instance, false);
IProperty currentProperty = null;
if (currentRR is MemberResolveResult)
currentProperty = ((MemberResolveResult)currentRR).Member as IProperty;
// end of foreach resolve logic
// back to resolve visitor:
resolver = resolver.PushBlock();
IVariable v;
if (isImplicitlyTypedVariable) {
StoreCurrentState(foreachStatement.VariableType);
StoreResult(foreachStatement.VariableType, new TypeResolveResult(elementType));
v = MakeVariable(elementType, foreachStatement.VariableNameToken);
} else {
IType variableType = ResolveType(foreachStatement.VariableType);
v = MakeVariable(variableType, foreachStatement.VariableNameToken);
}
StoreCurrentState(foreachStatement.VariableNameToken);
resolver = resolver.AddVariable(v);
StoreResult(foreachStatement.VariableNameToken, new LocalResolveResult(v));
Scan(foreachStatement.EmbeddedStatement);
resolver = resolver.PopBlock();
return new ForEachResolveResult(getEnumeratorInvocation, collectionType, enumeratorType, elementType,
v, currentProperty, moveNextMethod, voidResult.Type);
}
void CheckForEnumerableInterface(ResolveResult expression, out IType collectionType, out IType enumeratorType, out IType elementType, out ResolveResult getEnumeratorInvocation)
{
var compilation = resolver.Compilation;
bool? isGeneric;
elementType = GetElementTypeFromIEnumerable(expression.Type, compilation, false, out isGeneric);
if (isGeneric == true) {
ITypeDefinition enumerableOfT = compilation.FindType(KnownTypeCode.IEnumerableOfT).GetDefinition();
if (enumerableOfT != null)
collectionType = new ParameterizedType(enumerableOfT, new [] { elementType });
else
collectionType = SpecialType.UnknownType;
ITypeDefinition enumeratorOfT = compilation.FindType(KnownTypeCode.IEnumeratorOfT).GetDefinition();
if (enumeratorOfT != null)
enumeratorType = new ParameterizedType(enumeratorOfT, new [] { elementType });
else
enumeratorType = SpecialType.UnknownType;
} else if (isGeneric == false) {
collectionType = compilation.FindType(KnownTypeCode.IEnumerable);
enumeratorType = compilation.FindType(KnownTypeCode.IEnumerator);
} else {
collectionType = SpecialType.UnknownType;
enumeratorType = SpecialType.UnknownType;
}
getEnumeratorInvocation = resolver.ResolveCast(collectionType, expression);
getEnumeratorInvocation = resolver.ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList.Instance, NameLookupMode.InvocationTarget);
getEnumeratorInvocation = resolver.ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]);
}
#endregion
#region Local Variable Scopes (Block Statements)
ResolveResult IAstVisitor.VisitBlockStatement(BlockStatement blockStatement)
{
resolver = resolver.PushBlock();
ScanChildren(blockStatement);
resolver = resolver.PopBlock();
return voidResult;
}
ResolveResult IAstVisitor.VisitUsingStatement(UsingStatement usingStatement)
{
resolver = resolver.PushBlock();
if (resolverEnabled) {
for (AstNode child = usingStatement.FirstChild; child != null; child = child.NextSibling) {
if (child.Role == UsingStatement.ResourceAcquisitionRole && child is Expression) {
ResolveAndProcessConversion((Expression)child, resolver.Compilation.FindType(KnownTypeCode.IDisposable));
} else {
Scan(child);
}
}
} else {
ScanChildren(usingStatement);
}
resolver = resolver.PopBlock();
return resolverEnabled ? voidResult : null;
}
ResolveResult IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement)
{
resolver = resolver.PushBlock();
IType type = ResolveType(fixedStatement.Type);
foreach (VariableInitializer vi in fixedStatement.Variables) {
resolver = resolver.AddVariable(MakeVariable(type, vi.NameToken));
Scan(vi);
}
Scan(fixedStatement.EmbeddedStatement);
resolver = resolver.PopBlock();
return voidResult;
}
ResolveResult IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement)
{
resolver = resolver.PushBlock();
ScanChildren(switchStatement);
resolver = resolver.PopBlock();
return voidResult;
}
ResolveResult IAstVisitor.VisitCatchClause(CatchClause catchClause)
{
resolver = resolver.PushBlock();
if (string.IsNullOrEmpty(catchClause.VariableName)) {
Scan(catchClause.Type);
} else {
//DomRegion region = MakeRegion(catchClause.VariableNameToken);
StoreCurrentState(catchClause.VariableNameToken);
IVariable v = MakeVariable(ResolveType(catchClause.Type), catchClause.VariableNameToken);
resolver = resolver.AddVariable(v);
StoreResult(catchClause.VariableNameToken, new LocalResolveResult(v));
}
Scan(catchClause.Body);
resolver = resolver.PopBlock();
return voidResult;
}
#endregion
#region VariableDeclarationStatement
ResolveResult IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement)
{
bool isConst = (variableDeclarationStatement.Modifiers & Modifiers.Const) != 0;
if (!isConst && variableDeclarationStatement.Type.IsVar() && variableDeclarationStatement.Variables.Count == 1) {
VariableInitializer vi = variableDeclarationStatement.Variables.Single();
StoreCurrentState(variableDeclarationStatement.Type);
IType type = Resolve(vi.Initializer).Type;
StoreResult(variableDeclarationStatement.Type, new TypeResolveResult(type));
IVariable v = MakeVariable(type, vi.NameToken);
resolver = resolver.AddVariable(v);
Scan(vi);
} else {
IType type = ResolveType(variableDeclarationStatement.Type);
foreach (VariableInitializer vi in variableDeclarationStatement.Variables) {
IVariable v;
if (isConst) {
ResolveResult rr = Resolve(vi.Initializer);
rr = resolver.ResolveCast(type, rr);
v = MakeConstant(type, vi.NameToken, rr.ConstantValue);
} else {
v = MakeVariable(type, vi.NameToken);
}
resolver = resolver.AddVariable(v);
Scan(vi);
}
}
return voidResult;
}
#endregion
#region Condition Statements
ResolveResult IAstVisitor.VisitForStatement(ForStatement forStatement)
{
resolver = resolver.PushBlock();
var result = HandleConditionStatement(forStatement);
resolver = resolver.PopBlock();
return result;
}
ResolveResult IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement)
{
return HandleConditionStatement(ifElseStatement);
}
ResolveResult IAstVisitor.VisitWhileStatement(WhileStatement whileStatement)
{
return HandleConditionStatement(whileStatement);
}
ResolveResult IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement)
{
return HandleConditionStatement(doWhileStatement);
}
ResolveResult HandleConditionStatement(Statement conditionStatement)
{
if (resolverEnabled) {
for (AstNode child = conditionStatement.FirstChild; child != null; child = child.NextSibling) {
if (child.Role == Roles.Condition) {
Expression condition = (Expression)child;
ResolveResult conditionRR = Resolve(condition);
ResolveResult convertedRR = resolver.ResolveCondition(conditionRR);
if (convertedRR != conditionRR)
ProcessConversionResult(condition, convertedRR as ConversionResolveResult);
} else {
Scan(child);
}
}
return voidResult;
} else {
ScanChildren(conditionStatement);
return null;
}
}
#endregion
#region Return Statements
ResolveResult IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement)
{
if (resolverEnabled && !resolver.IsWithinLambdaExpression && resolver.CurrentMember != null) {
IType type = resolver.CurrentMember.ReturnType;
if (TaskType.IsTask(type)) {
var methodDecl = returnStatement.Ancestors.OfType().FirstOrDefault();
if (methodDecl != null && (methodDecl.Modifiers & Modifiers.Async) == Modifiers.Async)
type = UnpackTask(type);
}
ResolveAndProcessConversion(returnStatement.Expression, type);
} else {
Scan(returnStatement.Expression);
}
return resolverEnabled ? voidResult : null;
}
ResolveResult IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement)
{
if (resolverEnabled && resolver.CurrentMember != null) {
IType returnType = resolver.CurrentMember.ReturnType;
bool? isGeneric;
IType elementType = GetElementTypeFromIEnumerable(returnType, resolver.Compilation, true, out isGeneric);
ResolveAndProcessConversion(yieldStatement.Expression, elementType);
} else {
Scan(yieldStatement.Expression);
}
return resolverEnabled ? voidResult : null;
}
ResolveResult IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement)
{
return voidResult;
}
#endregion
#region Other statements
ResolveResult IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement)
{
ScanChildren(expressionStatement);
return voidResult;
}
ResolveResult IAstVisitor.VisitLockStatement(LockStatement lockStatement)
{
ScanChildren(lockStatement);
return voidResult;
}
ResolveResult IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement)
{
return voidResult;
}
ResolveResult IAstVisitor.VisitBreakStatement(BreakStatement breakStatement)
{
return voidResult;
}
ResolveResult IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement)
{
return voidResult;
}
ResolveResult IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement)
{
if (resolverEnabled) {
ResolveAndProcessConversion(throwStatement.Expression, resolver.Compilation.FindType(KnownTypeCode.Exception));
return voidResult;
} else {
Scan(throwStatement.Expression);
return null;
}
}
ResolveResult IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement)
{
ScanChildren(tryCatchStatement);
return voidResult;
}
ResolveResult IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement)
{
ScanChildren(gotoCaseStatement);
return voidResult;
}
ResolveResult IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement)
{
return voidResult;
}
ResolveResult IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement)
{
return voidResult;
}
ResolveResult IAstVisitor.VisitLabelStatement(LabelStatement labelStatement)
{
return voidResult;
}
ResolveResult IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement)
{
resolver = resolver.PushBlock();
ScanChildren(unsafeStatement);
resolver = resolver.PopBlock();
return voidResult;
}
#endregion
#region Local Variable Type Inference
IVariable MakeVariable(IType type, Identifier variableName)
{
return new SimpleVariable(MakeRegion(variableName), type, variableName.Name);
}
IVariable MakeConstant(IType type, Identifier variableName, object constantValue)
{
return new SimpleConstant(MakeRegion(variableName), type, variableName.Name, constantValue);
}
class SimpleVariable : IVariable
{
readonly DomRegion region;
readonly IType type;
readonly string name;
public SimpleVariable(DomRegion region, IType type, string name)
{
Debug.Assert(type != null);
Debug.Assert(name != null);
this.region = region;
this.type = type;
this.name = name;
}
public SymbolKind SymbolKind {
get { return SymbolKind.Variable; }
}
public string Name {
get { return name; }
}
public DomRegion Region {
get { return region; }
}
public IType Type {
get { return type; }
}
public virtual bool IsConst {
get { return false; }
}
public virtual object ConstantValue {
get { return null; }
}
public override string ToString()
{
return type.ToString() + " " + name + ";";
}
public ISymbolReference ToReference()
{
return new VariableReference(type.ToTypeReference(), name, region, IsConst, ConstantValue);
}
}
sealed class SimpleConstant : SimpleVariable
{
readonly object constantValue;
public SimpleConstant(DomRegion region, IType type, string name, object constantValue)
: base(region, type, name)
{
this.constantValue = constantValue;
}
public override bool IsConst {
get { return true; }
}
public override object ConstantValue {
get { return constantValue; }
}
public override string ToString()
{
return Type.ToString() + " " + Name + " = " + new PrimitiveExpression(constantValue).ToString() + ";";
}
}
static IType GetElementTypeFromIEnumerable(IType collectionType, ICompilation compilation, bool allowIEnumerator, out bool? isGeneric)
{
bool foundNonGenericIEnumerable = false;
foreach (IType baseType in collectionType.GetAllBaseTypes()) {
ITypeDefinition baseTypeDef = baseType.GetDefinition();
if (baseTypeDef != null) {
KnownTypeCode typeCode = baseTypeDef.KnownTypeCode;
if (typeCode == KnownTypeCode.IEnumerableOfT || (allowIEnumerator && typeCode == KnownTypeCode.IEnumeratorOfT)) {
ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) {
isGeneric = true;
return pt.GetTypeArgument(0);
}
}
if (typeCode == KnownTypeCode.IEnumerable || (allowIEnumerator && typeCode == KnownTypeCode.IEnumerator))
foundNonGenericIEnumerable = true;
}
}
// System.Collections.IEnumerable found in type hierarchy -> Object is element type.
if (foundNonGenericIEnumerable) {
isGeneric = false;
return compilation.FindType(KnownTypeCode.Object);
}
isGeneric = null;
return SpecialType.UnknownType;
}
#endregion
#region Attributes
ResolveResult IAstVisitor.VisitAttribute(Attribute attribute)
{
var type = ResolveType(attribute.Type);
// Separate arguments into ctor arguments and non-ctor arguments:
var constructorArguments = attribute.Arguments.Where(a => !(a is NamedExpression));
var nonConstructorArguments = attribute.Arguments.OfType();
// Scan the non-constructor arguments
resolver = resolver.PushObjectInitializer(new InitializedObjectResolveResult(type));
List initializerStatements = new List();
foreach (var arg in nonConstructorArguments)
HandleNamedExpression(arg, initializerStatements);
resolver = resolver.PopObjectInitializer();
// Resolve the ctor arguments and find the matching ctor overload
string[] argumentNames;
ResolveResult[] arguments = GetArguments(constructorArguments, out argumentNames);
ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames, false, initializerStatements);
ProcessInvocationResult(null, constructorArguments, rr);
return rr;
}
ResolveResult IAstVisitor.VisitAttributeSection(AttributeSection attributeSection)
{
ScanChildren(attributeSection);
return voidResult;
}
#endregion
#region Using Declaration
ResolveResult IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration)
{
ScanChildren(usingDeclaration);
return voidResult;
}
ResolveResult IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration)
{
ScanChildren(usingDeclaration);
return voidResult;
}
ResolveResult IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration)
{
return voidResult;
}
#endregion
#region Type References
ResolveResult IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType)
{
if (!resolverEnabled)
return null;
KnownTypeCode typeCode = primitiveType.KnownTypeCode;
if (typeCode == KnownTypeCode.None && primitiveType.Parent is Constraint && primitiveType.Role == Roles.BaseType) {
switch (primitiveType.Keyword) {
case "class":
case "struct":
case "new":
return voidResult;
}
}
IType type = resolver.Compilation.FindType(typeCode);
return new TypeResolveResult(type);
}
ResolveResult IAstVisitor.VisitSimpleType(SimpleType simpleType)
{
if (!resolverEnabled) {
ScanChildren(simpleType);
return null;
}
// Figure out the correct lookup mode:
NameLookupMode lookupMode = simpleType.GetNameLookupMode();
var typeArguments = ResolveTypeArguments(simpleType.TypeArguments);
Identifier identifier = simpleType.IdentifierToken;
if (string.IsNullOrEmpty(identifier.Name))
return new TypeResolveResult(SpecialType.UnboundTypeArgument);
ResolveResult rr = resolver.LookupSimpleNameOrTypeName(identifier.Name, typeArguments, lookupMode);
if (simpleType.Parent is Attribute && !identifier.IsVerbatim) {
var withSuffix = resolver.LookupSimpleNameOrTypeName(identifier.Name + "Attribute", typeArguments, lookupMode);
if (AttributeTypeReference.PreferAttributeTypeWithSuffix(rr.Type, withSuffix.Type, resolver.Compilation))
return withSuffix;
}
return rr;
}
ResolveResult IAstVisitor.VisitMemberType(MemberType memberType)
{
ResolveResult target;
NameLookupMode lookupMode = memberType.GetNameLookupMode();
if (memberType.IsDoubleColon && memberType.Target is SimpleType) {
SimpleType t = (SimpleType)memberType.Target;
StoreCurrentState(t);
target = resolver.ResolveAlias(t.Identifier);
StoreResult(t, target);
} else {
if (!resolverEnabled) {
ScanChildren(memberType);
return null;
}
target = Resolve(memberType.Target);
}
var typeArguments = ResolveTypeArguments(memberType.TypeArguments);
Identifier identifier = memberType.MemberNameToken;
ResolveResult rr = resolver.ResolveMemberAccess(target, identifier.Name, typeArguments, lookupMode);
if (memberType.Parent is Attribute && !identifier.IsVerbatim) {
var withSuffix = resolver.ResolveMemberAccess(target, identifier.Name + "Attribute", typeArguments, lookupMode);
if (AttributeTypeReference.PreferAttributeTypeWithSuffix(rr.Type, withSuffix.Type, resolver.Compilation))
return withSuffix;
}
return rr;
}
ResolveResult IAstVisitor.VisitComposedType(ComposedType composedType)
{
if (!resolverEnabled) {
ScanChildren(composedType);
return null;
}
IType t = ResolveType(composedType.BaseType);
if (composedType.HasNullableSpecifier) {
t = NullableType.Create(resolver.Compilation, t);
}
for (int i = 0; i < composedType.PointerRank; i++) {
t = new PointerType(t);
}
foreach (var a in composedType.ArraySpecifiers.Reverse()) {
t = new ArrayType(resolver.Compilation, t, a.Dimensions);
}
return new TypeResolveResult(t);
}
#endregion
#region Query Expressions
ResolveResult IAstVisitor.VisitQueryExpression(QueryExpression queryExpression)
{
resolver = resolver.PushBlock();
var oldQueryResult = currentQueryResult;
var oldCancellationToken = cancellationToken;
try {
// Because currentQueryResult isn't part of the stored state,
// query expressions must be resolved in a single operation.
// This means we can't allow cancellation within the query expression.
cancellationToken = CancellationToken.None;
currentQueryResult = null;
foreach (var clause in queryExpression.Clauses) {
currentQueryResult = Resolve(clause);
}
return WrapResult(currentQueryResult);
} finally {
currentQueryResult = oldQueryResult;
cancellationToken = oldCancellationToken;
resolver = resolver.PopBlock();
}
}
IType GetTypeForQueryVariable(IType type)
{
bool? isGeneric;
// This assumes queries are only used on IEnumerable.
var result = GetElementTypeFromIEnumerable(type, resolver.Compilation, false, out isGeneric);
// If that fails try to resolve the Select method and resolve the projection.
if (result.Kind == TypeKind.Unknown) {
var selectAccess = resolver.ResolveMemberAccess(new ResolveResult (type), "Select", EmptyList.Instance);
ResolveResult[] arguments = {
new QueryExpressionLambda(1, voidResult)
};
var rr = resolver.ResolveInvocation(selectAccess, arguments) as CSharpInvocationResolveResult;
if (rr != null && rr.Arguments.Count == 2) {
var invokeMethod = rr.Arguments[1].Type.GetDelegateInvokeMethod();
if (invokeMethod != null && invokeMethod.Parameters.Count > 0)
return invokeMethod.Parameters[0].Type;
}
}
return result;
}
ResolveResult MakeTransparentIdentifierResolveResult()
{
return new ResolveResult(new AnonymousType(resolver.Compilation, EmptyList.Instance));
}
sealed class QueryExpressionLambdaConversion : Conversion
{
internal readonly IType[] ParameterTypes;
public QueryExpressionLambdaConversion(IType[] parameterTypes)
{
this.ParameterTypes = parameterTypes;
}
public override bool IsImplicit {
get { return true; }
}
public override bool IsAnonymousFunctionConversion {
get { return true; }
}
}
sealed class QueryExpressionLambda : LambdaResolveResult
{
readonly IParameter[] parameters;
readonly ResolveResult bodyExpression;
internal IType[] inferredParameterTypes;
public QueryExpressionLambda(int parameterCount, ResolveResult bodyExpression)
{
this.parameters = new IParameter[parameterCount];
for (int i = 0; i < parameterCount; i++) {
parameters[i] = new DefaultParameter(SpecialType.UnknownType, "x" + i);
}
this.bodyExpression = bodyExpression;
}
public override IList Parameters {
get { return parameters; }
}
public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions)
{
if (parameterTypes.Length == parameters.Length) {
this.inferredParameterTypes = parameterTypes;
return new QueryExpressionLambdaConversion(parameterTypes);
} else {
return Conversion.None;
}
}
public override bool IsAsync {
get { return false; }
}
public override bool IsImplicitlyTyped {
get { return true; }
}
public override bool IsAnonymousMethod {
get { return false; }
}
public override bool HasParameterList {
get { return true; }
}
public override ResolveResult Body {
get { return bodyExpression; }
}
public override IType GetInferredReturnType(IType[] parameterTypes)
{
return bodyExpression.Type;
}
public override IType ReturnType {
get {
return bodyExpression.Type;
}
}
public override string ToString()
{
return string.Format("[QueryExpressionLambda ({0}) => {1}]", string.Join(",", parameters.Select(p => p.Name)), bodyExpression);
}
}
QueryClause GetPreviousQueryClause(QueryClause clause)
{
for (AstNode node = clause.PrevSibling; node != null; node = node.PrevSibling) {
if (node.Role == QueryExpression.ClauseRole)
return (QueryClause)node;
}
return null;
}
QueryClause GetNextQueryClause(QueryClause clause)
{
for (AstNode node = clause.NextSibling; node != null; node = node.NextSibling) {
if (node.Role == QueryExpression.ClauseRole)
return (QueryClause)node;
}
return null;
}
ResolveResult IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause)
{
ResolveResult result = errorResult;
ResolveResult expr = Resolve(queryFromClause.Expression);
IVariable v;
if (queryFromClause.Type.IsNull) {
v = MakeVariable(GetTypeForQueryVariable(expr.Type), queryFromClause.IdentifierToken);
result = expr;
} else {
v = MakeVariable(ResolveType(queryFromClause.Type), queryFromClause.IdentifierToken);
// resolve the .Cast<>() call
ResolveResult methodGroup = resolver.ResolveMemberAccess(expr, "Cast", new[] { v.Type }, NameLookupMode.InvocationTarget);
result = resolver.ResolveInvocation(methodGroup, new ResolveResult[0]);
}
StoreCurrentState(queryFromClause.IdentifierToken);
resolver = resolver.AddVariable(v);
StoreResult(queryFromClause.IdentifierToken, new LocalResolveResult(v));
if (currentQueryResult != null) {
// this is a second 'from': resolve the .SelectMany() call
QuerySelectClause selectClause = GetNextQueryClause(queryFromClause) as QuerySelectClause;
ResolveResult selectResult;
if (selectClause != null) {
// from ... from ... select - the SelectMany call also performs the Select operation
selectResult = Resolve(selectClause.Expression);
} else {
// from .. from ... ... - introduce a transparent identifier
selectResult = MakeTransparentIdentifierResolveResult();
}
ResolveResult methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "SelectMany", EmptyList.Instance, NameLookupMode.InvocationTarget);
ResolveResult[] arguments = {
new QueryExpressionLambda(1, result),
new QueryExpressionLambda(2, selectResult)
};
result = resolver.ResolveInvocation(methodGroup, arguments);
}
if (result == expr)
return WrapResult(result);
else
return result;
}
///
/// Wraps the result in an identity conversion.
/// This is necessary so that '$from x in variable$ select x*2' does not resolve
/// to the LocalResolveResult for the variable, which would confuse find references.
///
ResolveResult WrapResult(ResolveResult result)
{
return new CastResolveResult(result.Type, result, Conversion.IdentityConversion, resolver.CheckForOverflow);
}
ResolveResult IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause)
{
ResolveResult rr = Resolve(queryContinuationClause.PrecedingQuery);
IType variableType = GetTypeForQueryVariable(rr.Type);
StoreCurrentState(queryContinuationClause.IdentifierToken);
IVariable v = MakeVariable(variableType, queryContinuationClause.IdentifierToken);
resolver = resolver.AddVariable(v);
StoreResult(queryContinuationClause.IdentifierToken, new LocalResolveResult(v));
return WrapResult(rr);
}
ResolveResult IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause)
{
ResolveResult expr = Resolve(queryLetClause.Expression);
StoreCurrentState(queryLetClause.IdentifierToken);
IVariable v = MakeVariable(expr.Type, queryLetClause.IdentifierToken);
resolver = resolver.AddVariable(v);
StoreResult(queryLetClause.IdentifierToken, new LocalResolveResult(v));
if (currentQueryResult != null) {
// resolve the .Select() call
ResolveResult methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "Select", EmptyList.Instance, NameLookupMode.InvocationTarget);
ResolveResult[] arguments = { new QueryExpressionLambda(1, MakeTransparentIdentifierResolveResult()) };
return resolver.ResolveInvocation(methodGroup, arguments);
} else {
return errorResult;
}
}
ResolveResult IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause)
{
// join v in expr on onExpr equals equalsExpr [into g]
ResolveResult inResult = null;
ResolveResult expr = Resolve(queryJoinClause.InExpression);
IType variableType;
if (queryJoinClause.Type.IsNull) {
variableType = GetTypeForQueryVariable(expr.Type);
inResult = expr;
} else {
variableType = ResolveType(queryJoinClause.Type);
// resolve the .Cast<>() call
ResolveResult methodGroup = resolver.ResolveMemberAccess(expr, "Cast", new[] { variableType }, NameLookupMode.InvocationTarget);
inResult = resolver.ResolveInvocation(methodGroup, new ResolveResult[0]);
}
// resolve the 'On' expression in a context that contains only the previously existing range variables:
// (before adding any variable)
ResolveResult onResult = Resolve(queryJoinClause.OnExpression);
// scan the 'Equals' expression in a context that contains only the variable 'v'
CSharpResolver resolverOutsideQuery = resolver;
resolverOutsideQuery = resolverOutsideQuery.PopBlock(); // pop all variables from the current query expression
IVariable v = MakeVariable(variableType, queryJoinClause.JoinIdentifierToken);
resolverOutsideQuery = resolverOutsideQuery.AddVariable(v);
ResolveResult equalsResult = errorResult;
ResetContext(resolverOutsideQuery, delegate {
equalsResult = Resolve(queryJoinClause.EqualsExpression);
});
StoreCurrentState(queryJoinClause.JoinIdentifierToken);
StoreResult(queryJoinClause.JoinIdentifierToken, new LocalResolveResult(v));
if (queryJoinClause.IsGroupJoin) {
return ResolveGroupJoin(queryJoinClause, inResult, onResult, equalsResult);
} else {
resolver = resolver.AddVariable(v);
if (currentQueryResult != null) {
QuerySelectClause selectClause = GetNextQueryClause(queryJoinClause) as QuerySelectClause;
ResolveResult selectResult;
if (selectClause != null) {
// from ... join ... select - the Join call also performs the Select operation
selectResult = Resolve(selectClause.Expression);
} else {
// from .. join ... ... - introduce a transparent identifier
selectResult = MakeTransparentIdentifierResolveResult();
}
var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "Join", EmptyList.Instance);
ResolveResult[] arguments = {
inResult,
new QueryExpressionLambda(1, onResult),
new QueryExpressionLambda(1, equalsResult),
new QueryExpressionLambda(2, selectResult)
};
return resolver.ResolveInvocation(methodGroup, arguments);
} else {
return errorResult;
}
}
}
ResolveResult ResolveGroupJoin(QueryJoinClause queryJoinClause,
ResolveResult inResult, ResolveResult onResult, ResolveResult equalsResult)
{
Debug.Assert(queryJoinClause.IsGroupJoin);
DomRegion intoIdentifierRegion = MakeRegion(queryJoinClause.IntoIdentifierToken);
// We need to declare the group variable, but it's a bit tricky to determine its type:
// We'll have to resolve the GroupJoin invocation and take a look at the inferred types
// for the lambda given as last parameter.
var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "GroupJoin", EmptyList.Instance);
QuerySelectClause selectClause = GetNextQueryClause(queryJoinClause) as QuerySelectClause;
LambdaResolveResult groupJoinLambda;
if (selectClause != null) {
// from ... join ... into g select - the GroupJoin call also performs the Select operation
IParameter[] selectLambdaParameters = {
new DefaultParameter(SpecialType.UnknownType, "<>transparentIdentifier"),
new DefaultParameter(SpecialType.UnknownType, queryJoinClause.IntoIdentifier, region: intoIdentifierRegion)
};
groupJoinLambda = new ImplicitlyTypedLambda(selectClause, selectLambdaParameters, this);
} else {
// from .. join ... ... - introduce a transparent identifier
groupJoinLambda = new QueryExpressionLambda(2, MakeTransparentIdentifierResolveResult());
}
ResolveResult[] arguments = {
inResult,
new QueryExpressionLambda(1, onResult),
new QueryExpressionLambda(1, equalsResult),
groupJoinLambda
};
ResolveResult rr = resolver.ResolveInvocation(methodGroup, arguments);
InvocationResolveResult invocationRR = rr as InvocationResolveResult;
IVariable groupVariable;
if (groupJoinLambda is ImplicitlyTypedLambda) {
var implicitlyTypedLambda = (ImplicitlyTypedLambda)groupJoinLambda;
if (invocationRR != null && invocationRR.Arguments.Count > 0) {
ConversionResolveResult crr = invocationRR.Arguments[invocationRR.Arguments.Count - 1] as ConversionResolveResult;
if (crr != null)
ProcessConversion(null, crr.Input, crr.Conversion, crr.Type);
}
implicitlyTypedLambda.EnforceMerge(this);
if (implicitlyTypedLambda.Parameters.Count == 2) {
StoreCurrentState(queryJoinClause.IntoIdentifierToken);
groupVariable = implicitlyTypedLambda.Parameters[1];
} else {
groupVariable = null;
}
} else {
Debug.Assert(groupJoinLambda is QueryExpressionLambda);
// Add the variable if the query expression continues after the group join
// (there's no need to do this if there's only a select clause remaining, as
// we already handled that in the ImplicitlyTypedLambda).
// Get the inferred type of the group variable:
IType[] inferredParameterTypes = null;
if (invocationRR != null && invocationRR.Arguments.Count > 0) {
ConversionResolveResult crr = invocationRR.Arguments[invocationRR.Arguments.Count - 1] as ConversionResolveResult;
if (crr != null && crr.Conversion is QueryExpressionLambdaConversion) {
inferredParameterTypes = ((QueryExpressionLambdaConversion)crr.Conversion).ParameterTypes;
}
}
if (inferredParameterTypes == null)
inferredParameterTypes = ((QueryExpressionLambda)groupJoinLambda).inferredParameterTypes;
IType groupParameterType;
if (inferredParameterTypes != null && inferredParameterTypes.Length == 2)
groupParameterType = inferredParameterTypes[1];
else
groupParameterType = SpecialType.UnknownType;
StoreCurrentState(queryJoinClause.IntoIdentifierToken);
groupVariable = MakeVariable(groupParameterType, queryJoinClause.IntoIdentifierToken);
resolver = resolver.AddVariable(groupVariable);
}
if (groupVariable != null) {
StoreResult(queryJoinClause.IntoIdentifierToken, new LocalResolveResult(groupVariable));
}
return rr;
}
ResolveResult IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause)
{
ResolveResult condition = Resolve(queryWhereClause.Condition);
IType boolType = resolver.Compilation.FindType(KnownTypeCode.Boolean);
Conversion conversionToBool = resolver.conversions.ImplicitConversion(condition, boolType);
ProcessConversion(queryWhereClause.Condition, condition, conversionToBool, boolType);
if (currentQueryResult != null) {
if (conversionToBool != Conversion.IdentityConversion && conversionToBool != Conversion.None) {
condition = new ConversionResolveResult(boolType, condition, conversionToBool, resolver.CheckForOverflow);
}
var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "Where", EmptyList.Instance);
ResolveResult[] arguments = { new QueryExpressionLambda(1, condition) };
return resolver.ResolveInvocation(methodGroup, arguments);
} else {
return errorResult;
}
}
ResolveResult IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause)
{
if (currentQueryResult == null) {
ScanChildren(querySelectClause);
return errorResult;
}
QueryClause previousQueryClause = GetPreviousQueryClause(querySelectClause);
// If the 'select' follows on a 'SelectMany', 'Join' or 'GroupJoin' clause, then the 'select' portion
// was already done as part of the previous clause.
if (((previousQueryClause is QueryFromClause && GetPreviousQueryClause(previousQueryClause) != null))
|| previousQueryClause is QueryJoinClause)
{
// GroupJoin already scans the following select clause in a different context,
// so we must not scan it again.
if (!(previousQueryClause is QueryJoinClause && ((QueryJoinClause)previousQueryClause).IsGroupJoin))
Scan(querySelectClause.Expression);
return WrapResult(currentQueryResult);
}
QueryExpression query = querySelectClause.Parent as QueryExpression;
string rangeVariable = GetSingleRangeVariable(query);
if (rangeVariable != null) {
IdentifierExpression ident = ParenthesizedExpression.UnpackParenthesizedExpression(querySelectClause.Expression) as IdentifierExpression;
if (ident != null && ident.Identifier == rangeVariable && !ident.TypeArguments.Any()) {
// selecting the single identifier that is the range variable
if (query.Clauses.Count > 2) {
// only if the query is not degenerate:
// the Select call will be optimized away, so directly return the previous result
Scan(querySelectClause.Expression);
return WrapResult(currentQueryResult);
}
}
}
ResolveResult expr = Resolve(querySelectClause.Expression);
var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "Select", EmptyList.Instance);
ResolveResult[] arguments = { new QueryExpressionLambda(1, expr) };
return resolver.ResolveInvocation(methodGroup, arguments);
}
///
/// Gets the name of the range variable in the specified query.
/// If the query has multiple range variables, this method returns null.
///
string GetSingleRangeVariable(QueryExpression query)
{
if (query == null)
return null;
foreach (QueryClause clause in query.Clauses.Skip(1)) {
if (clause is QueryFromClause || clause is QueryJoinClause || clause is QueryLetClause) {
// query has more than 1 range variable
return null;
}
}
QueryFromClause fromClause = query.Clauses.FirstOrDefault() as QueryFromClause;
if (fromClause != null)
return fromClause.Identifier;
QueryContinuationClause continuationClause = query.Clauses.FirstOrDefault() as QueryContinuationClause;
if (continuationClause != null)
return continuationClause.Identifier;
return null;
}
ResolveResult IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause)
{
if (currentQueryResult == null) {
ScanChildren(queryGroupClause);
return errorResult;
}
// ... group projection by key
ResolveResult projection = Resolve(queryGroupClause.Projection);
ResolveResult key = Resolve(queryGroupClause.Key);
var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "GroupBy", EmptyList.Instance);
ResolveResult[] arguments = {
new QueryExpressionLambda(1, key),
new QueryExpressionLambda(1, projection)
};
return resolver.ResolveInvocation(methodGroup, arguments);
}
ResolveResult IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause)
{
foreach (QueryOrdering ordering in queryOrderClause.Orderings) {
currentQueryResult = Resolve(ordering);
}
return WrapResult(currentQueryResult);
}
ResolveResult IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering)
{
if (currentQueryResult == null) {
ScanChildren(queryOrdering);
return errorResult;
}
// ... orderby sortKey [descending]
ResolveResult sortKey = Resolve(queryOrdering.Expression);
QueryOrderClause parentClause = queryOrdering.Parent as QueryOrderClause;
bool isFirst = (parentClause == null || parentClause.Orderings.FirstOrDefault() == queryOrdering);
string methodName = isFirst ? "OrderBy" : "ThenBy";
if (queryOrdering.Direction == QueryOrderingDirection.Descending)
methodName += "Descending";
var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, methodName, EmptyList.Instance);
ResolveResult[] arguments = {
new QueryExpressionLambda(1, sortKey),
};
return resolver.ResolveInvocation(methodGroup, arguments);
}
#endregion
#region Constructor Initializer
ResolveResult IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer)
{
ResolveResult target;
if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.Base) {
target = resolver.ResolveBaseReference();
} else {
target = resolver.ResolveThisReference();
}
string[] argumentNames;
ResolveResult[] arguments = GetArguments(constructorInitializer.Arguments, out argumentNames);
ResolveResult rr = resolver.ResolveObjectCreation(target.Type, arguments, argumentNames, allowProtectedAccess: true);
ProcessInvocationResult(null, constructorInitializer.Arguments, rr);
return rr;
}
#endregion
#region Other Nodes
// Token nodes
ResolveResult IAstVisitor.VisitIdentifier(Identifier identifier)
{
return null;
}
ResolveResult IAstVisitor.VisitComment (Comment comment)
{
return null;
}
ResolveResult IAstVisitor.VisitNewLine (NewLineNode comment)
{
return null;
}
ResolveResult IAstVisitor.VisitWhitespace(WhitespaceNode whitespaceNode)
{
return null;
}
ResolveResult IAstVisitor.VisitText(TextNode textNode)
{
return null;
}
ResolveResult IAstVisitor.VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective)
{
return null;
}
ResolveResult IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode)
{
return null;
}
ResolveResult IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier)
{
return null;
}
ResolveResult IAstVisitor.VisitNullNode(AstNode nullNode)
{
return null;
}
ResolveResult IAstVisitor.VisitErrorNode(AstNode errorNode)
{
return null;
}
ResolveResult IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, ICSharpCode.NRefactory.PatternMatching.Pattern pattern)
{
return null;
}
// Nodes where we just need to visit the children:
ResolveResult IAstVisitor.VisitAccessor(Accessor accessor)
{
ScanChildren(accessor);
return voidResult;
}
ResolveResult IAstVisitor.VisitSwitchSection(SwitchSection switchSection)
{
ScanChildren(switchSection);
return voidResult;
}
ResolveResult IAstVisitor.VisitCaseLabel(CaseLabel caseLabel)
{
ScanChildren(caseLabel);
return voidResult;
}
ResolveResult IAstVisitor.VisitConstraint(Constraint constraint)
{
ScanChildren(constraint);
return voidResult;
}
#endregion
#region Documentation Reference
ResolveResult IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference)
{
// Resolve child nodes:
ITypeDefinition declaringTypeDef;
if (documentationReference.DeclaringType.IsNull)
declaringTypeDef = resolver.CurrentTypeDefinition;
else
declaringTypeDef = ResolveType(documentationReference.DeclaringType).GetDefinition();
IType[] typeArguments = documentationReference.TypeArguments.Select(ResolveType).ToArray();
IType conversionOperatorReturnType = ResolveType(documentationReference.ConversionOperatorReturnType);
IParameter[] parameters = documentationReference.Parameters.Select(ResolveXmlDocParameter).ToArray();
if (documentationReference.SymbolKind == SymbolKind.TypeDefinition) {
if (declaringTypeDef != null)
return new TypeResolveResult(declaringTypeDef);
else
return errorResult;
}
if (documentationReference.SymbolKind == SymbolKind.None) {
// might be a type, member or ctor
string memberName = documentationReference.MemberName;
ResolveResult rr;
if (documentationReference.DeclaringType.IsNull) {
rr = resolver.LookupSimpleNameOrTypeName(memberName, typeArguments, NameLookupMode.Expression);
} else {
var target = Resolve(documentationReference.DeclaringType);
rr = resolver.ResolveMemberAccess(target, memberName, typeArguments);
}
// reduce to definition:
if (rr.IsError) {
return rr;
} else if (rr is TypeResolveResult) {
var typeDef = rr.Type.GetDefinition();
if (typeDef == null)
return errorResult;
if (documentationReference.HasParameterList) {
var ctors = typeDef.GetConstructors(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions);
return FindByParameters(ctors, parameters);
} else {
return new TypeResolveResult(typeDef);
}
} else if (rr is MemberResolveResult) {
var mrr = (MemberResolveResult)rr;
return new MemberResolveResult(null, mrr.Member.MemberDefinition);
} else if (rr is MethodGroupResolveResult) {
var mgrr = (MethodGroupResolveResult)rr;
var methods = mgrr.MethodsGroupedByDeclaringType.Reverse()
.SelectMany(ml => ml.Select(m => (IParameterizedMember)m.MemberDefinition));
return FindByParameters(methods, parameters);
}
return rr;
}
// Indexer or operator
if (declaringTypeDef == null)
return errorResult;
if (documentationReference.SymbolKind == SymbolKind.Indexer) {
var indexers = declaringTypeDef.Properties.Where(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation);
return FindByParameters(indexers, parameters);
} else if (documentationReference.SymbolKind == SymbolKind.Operator) {
var opType = documentationReference.OperatorType;
string memberName = OperatorDeclaration.GetName(opType);
var methods = declaringTypeDef.Methods.Where(m => m.IsOperator && m.Name == memberName);
if (opType == OperatorType.Implicit || opType == OperatorType.Explicit) {
// conversion operator
foreach (var method in methods) {
if (ParameterListComparer.Instance.Equals(method.Parameters, parameters)) {
if (method.ReturnType.Equals(conversionOperatorReturnType))
return new MemberResolveResult(null, method);
}
}
return new MemberResolveResult(null, methods.FirstOrDefault());
} else {
// not a conversion operator
return FindByParameters(methods, parameters);
}
} else {
throw new NotSupportedException(); // unknown entity type
}
}
IParameter ResolveXmlDocParameter(ParameterDeclaration p)
{
var lrr = Resolve(p) as LocalResolveResult;
if (lrr != null && lrr.IsParameter)
return (IParameter)lrr.Variable;
else
return new DefaultParameter(SpecialType.UnknownType, string.Empty);
}
ResolveResult FindByParameters(IEnumerable methods, IList parameters)
{
foreach (var method in methods) {
if (ParameterListComparer.Instance.Equals(method.Parameters, parameters))
return new MemberResolveResult(null, method);
}
return new MemberResolveResult(null, methods.FirstOrDefault());
}
#endregion
}
}