You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4039 lines
150 KiB
4039 lines
150 KiB
// Copyright (c) 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 |
|
{ |
|
/// <summary> |
|
/// Traverses the DOM and resolves expressions. |
|
/// </summary> |
|
/// <remarks> |
|
/// 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 <see cref="IResolveVisitorNavigator"/> 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 <c>ResolveAll</c> 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. |
|
/// </remarks> |
|
sealed class ResolveVisitor : IAstVisitor<ResolveResult> |
|
{ |
|
// The ResolveVisitor is also responsible for handling lambda expressions. |
|
|
|
static readonly ResolveResult errorResult = ErrorResolveResult.UnknownError; |
|
|
|
CSharpResolver resolver; |
|
/// <summary>Resolve result of the current LINQ query.</summary> |
|
/// <remarks>We do not have to put this into the stored state (resolver) because |
|
/// query expressions are always resolved in a single operation.</remarks> |
|
ResolveResult currentQueryResult; |
|
readonly CSharpUnresolvedFile unresolvedFile; |
|
readonly Dictionary<AstNode, ResolveResult> resolveResultCache = new Dictionary<AstNode, ResolveResult>(); |
|
readonly Dictionary<AstNode, CSharpResolver> resolverBeforeDict = new Dictionary<AstNode, CSharpResolver>(); |
|
readonly Dictionary<AstNode, CSharpResolver> resolverAfterDict = new Dictionary<AstNode, CSharpResolver>(); |
|
readonly Dictionary<Expression, ConversionWithTargetType> conversionDict = new Dictionary<Expression, ConversionWithTargetType>(); |
|
|
|
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<LambdaBase> undecidedLambdas; |
|
internal CancellationToken cancellationToken; |
|
|
|
#region Constructor |
|
static readonly IResolveVisitorNavigator skipAllNavigator = new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Skip, null); |
|
|
|
/// <summary> |
|
/// Creates a new ResolveVisitor instance. |
|
/// </summary> |
|
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 |
|
/// <summary> |
|
/// Resets the visitor to the stored position, runs the action, and then reverts the visitor to the previous position. |
|
/// </summary> |
|
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 |
|
/// <summary> |
|
/// Scans the AST rooted at the given node. |
|
/// </summary> |
|
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"); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 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 <c>resolver.Scan(syntaxTree); var result = resolver.GetResolveResult(node);</c> |
|
/// instead. |
|
/// -- |
|
/// This method now is internal, because it is difficult to use correctly. |
|
/// Users of the public API should use Scan()+GetResolveResult() instead. |
|
/// </summary> |
|
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)); |
|
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; } |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Convert 'rr' to the target type using the specified conversion. |
|
/// </summary> |
|
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); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Convert 'rr' to the target type. |
|
/// </summary> |
|
void ProcessConversion(Expression expr, ResolveResult rr, IType targetType) |
|
{ |
|
if (expr == null || expr.IsNull) |
|
return; |
|
ProcessConversion(expr, rr, resolver.conversions.ImplicitConversion(rr, targetType), targetType); |
|
} |
|
|
|
/// <summary> |
|
/// Resolves the specified expression and processes the conversion to targetType. |
|
/// </summary> |
|
void ResolveAndProcessConversion(Expression expr, IType targetType) |
|
{ |
|
if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void) { |
|
// 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) |
|
ProcessConversion(expr, rr.Input, rr.Conversion, rr.Type); |
|
} |
|
|
|
void ProcessConversionResults(IEnumerable<Expression> expr, IEnumerable<ResolveResult> 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<Expression> arguments) |
|
{ |
|
foreach (var nae in arguments.OfType<NamedArgumentExpression>()) { |
|
StoreCurrentState(nae); |
|
StoreResult(nae, new NamedArgumentResolveResult(nae.Name, resolveResultCache[nae.Expression])); |
|
} |
|
} |
|
|
|
void ProcessInvocationResult(Expression target, IEnumerable<Expression> arguments, ResolveResult invocation) |
|
{ |
|
if (invocation is CSharpInvocationResolveResult || invocation is DynamicInvocationResolveResult) { |
|
int i = 0; |
|
IList<ResolveResult> 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 |
|
/// <summary> |
|
/// Gets the resolve result for the specified node. |
|
/// If the node was not resolved by the navigator, this method will resolve it. |
|
/// </summary> |
|
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)) { |
|
parent = parent.Parent; |
|
if (parent == 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?"); |
|
} |
|
return storedResolver; |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
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<ResolveResult>.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<AstNode> 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<ResolveResult>.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) |
|
{ |
|
CSharpResolver previousResolver = resolver; |
|
try { |
|
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].Name); |
|
usingScope.Region = region; |
|
PushUsingScope(usingScope); |
|
} |
|
// Last using scope: |
|
usingScope = new UsingScope(resolver.CurrentUsingScope.UnresolvedUsingScope, identifiers.Last().Name); |
|
usingScope.Region = region; |
|
var cv = new TypeSystemConvertVisitor(new CSharpUnresolvedFile(region.FileName ?? string.Empty), usingScope); |
|
ApplyVisitorToUsings(cv, namespaceDeclaration.Children); |
|
PushUsingScope(usingScope); |
|
} |
|
ScanChildren(namespaceDeclaration); |
|
// 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<ResolveResult>.VisitTypeDeclaration(TypeDeclaration typeDeclaration) |
|
{ |
|
return VisitTypeOrDelegate(typeDeclaration, typeDeclaration.Name, typeDeclaration.TypeParameters.Count); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) |
|
{ |
|
return VisitTypeOrDelegate(delegateDeclaration, delegateDeclaration.Name, delegateDeclaration.TypeParameters.Count); |
|
} |
|
#endregion |
|
|
|
#region Track CurrentMember |
|
ResolveResult IAstVisitor<ResolveResult>.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) |
|
{ |
|
return VisitFieldOrEventDeclaration(fieldDeclaration, EntityType.Field); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) |
|
{ |
|
return VisitFieldOrEventDeclaration(fixedFieldDeclaration, EntityType.Field); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitEventDeclaration(EventDeclaration eventDeclaration) |
|
{ |
|
return VisitFieldOrEventDeclaration(eventDeclaration, EntityType.Event); |
|
} |
|
|
|
ResolveResult VisitFieldOrEventDeclaration(EntityDeclaration fieldOrEventDeclaration, EntityType entityType) |
|
{ |
|
//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) { |
|
IMember member; |
|
if (unresolvedFile != null) { |
|
member = GetMemberFromLocation(node); |
|
} else { |
|
string name = ((VariableInitializer)node).Name; |
|
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, entityType, 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<ResolveResult>.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<Expression> initializerElements = new List<Expression>(); |
|
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<ResolveResult>.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; |
|
if (unresolvedFile != null) { |
|
member = GetMemberFromLocation(memberDeclaration); |
|
} else { |
|
// Re-discover the method: |
|
EntityType entityType = memberDeclaration.EntityType; |
|
var parameterTypes = TypeSystemConvertVisitor.GetParameterTypes(memberDeclaration.GetChildrenByRole(Roles.Parameter), InterningProvider.Dummy); |
|
if (entityType == EntityType.Constructor) { |
|
string name = memberDeclaration.HasModifier(Modifiers.Static) ? ".cctor" : ".ctor"; |
|
member = AbstractUnresolvedMember.Resolve( |
|
resolver.CurrentTypeResolveContext, entityType, name, |
|
parameterTypeReferences: parameterTypes); |
|
} else if (entityType == EntityType.Destructor) { |
|
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, entityType, "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, entityType, 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<ResolveResult>.VisitMethodDeclaration(MethodDeclaration methodDeclaration) |
|
{ |
|
return VisitMethodMember(methodDeclaration); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) |
|
{ |
|
return VisitMethodMember(operatorDeclaration); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) |
|
{ |
|
return VisitMethodMember(constructorDeclaration); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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.EntityType, 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<ResolveResult>.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) |
|
{ |
|
return VisitPropertyMember(propertyDeclaration); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) |
|
{ |
|
return VisitPropertyMember(indexerDeclaration); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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, EntityType.Event, name); |
|
} else { |
|
member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, EntityType.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<ResolveResult>.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<IParameter>()) { |
|
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<ResolveResult>.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<ResolveResult>.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<ResolveResult>.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<ResolveResult>.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<ResolveResult>.VisitCheckedStatement(CheckedStatement checkedStatement) |
|
{ |
|
CSharpResolver oldResolver = resolver; |
|
try { |
|
resolver = resolver.WithCheckForOverflow(true); |
|
ScanChildren(checkedStatement); |
|
return voidResult; |
|
} finally { |
|
resolver = oldResolver; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) |
|
{ |
|
// 7.6.10.6 Anonymous object creation expressions |
|
List<IUnresolvedProperty> unresolvedProperties = new List<IUnresolvedProperty>(); |
|
List<AnonymousTypeMember> members = new List<AnonymousTypeMember>(); |
|
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 { |
|
Name = name, |
|
Accessibility = Accessibility.Public, |
|
ReturnType = returnTypeRef, |
|
Getter = new DefaultUnresolvedMethod { |
|
Name = "get_" + name, |
|
Accessibility = Accessibility.Public, |
|
ReturnType = returnTypeRef |
|
} |
|
}; |
|
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<ResolveResult> assignments = new List<ResolveResult>(); |
|
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<ResolveResult>.VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) |
|
{ |
|
int dimensions = arrayCreateExpression.Arguments.Count; |
|
IEnumerable<Expression> sizeArgumentExpressions; |
|
ResolveResult[] sizeArguments; |
|
IEnumerable<ArraySpecifier> additionalArraySpecifiers; |
|
if (dimensions == 0) { |
|
var firstSpecifier = arrayCreateExpression.AdditionalArraySpecifiers.FirstOrDefault(); |
|
if (firstSpecifier != null) { |
|
dimensions = firstSpecifier.Dimensions; |
|
additionalArraySpecifiers = arrayCreateExpression.AdditionalArraySpecifiers.Skip(1); |
|
} else { |
|
dimensions = 0; |
|
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<Expression> initializerElements; |
|
ResolveResult[] initializerElementResults; |
|
if (arrayCreateExpression.Initializer.IsNull) { |
|
sizes = null; |
|
initializerElements = null; |
|
initializerElementResults = null; |
|
} else { |
|
StoreCurrentState(arrayCreateExpression.Initializer); |
|
|
|
initializerElements = new List<Expression>(); |
|
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<Expression> 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<ResolveResult>.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) |
|
{ |
|
// Array initializers are handled by their parent expression. |
|
ScanChildren(arrayInitializerExpression); |
|
return errorResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitAsExpression(AsExpression asExpression) |
|
{ |
|
if (resolverEnabled) { |
|
ResolveResult input = Resolve(asExpression.Expression); |
|
var targetType = ResolveType(asExpression.Type); |
|
return new ConversionResolveResult(targetType, input, Conversion.TryCast, resolver.CheckForOverflow); |
|
} else { |
|
ScanChildren(asExpression); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) |
|
{ |
|
if (resolverEnabled) { |
|
return resolver.ResolveBaseReference(); |
|
} else { |
|
ScanChildren(baseReferenceExpression); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitCastExpression(CastExpression castExpression) |
|
{ |
|
if (resolverEnabled) { |
|
IType targetType = ResolveType(castExpression.Type); |
|
Expression expr = castExpression.Expression; |
|
ResolveResult rr = resolver.ResolveCast(targetType, Resolve(expr)); |
|
ProcessConversionResult(expr, rr as ConversionResolveResult); |
|
return rr; |
|
} else { |
|
ScanChildren(castExpression); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) |
|
{ |
|
if (resolverEnabled) { |
|
return resolver.ResolveDefaultValue(ResolveType(defaultValueExpression.Type)); |
|
} else { |
|
ScanChildren(defaultValueExpression); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitEmptyExpression(EmptyExpression emptyExpression) |
|
{ |
|
return errorResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.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<ResolveResult>.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<ResolveResult>.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<ResolveResult> 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<ResolveResult>.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) |
|
{ |
|
return resolver.ResolvePrimitive(null); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) |
|
{ |
|
var typeResolveResult = Resolve(objectCreateExpression.Type); |
|
if (typeResolveResult.IsError) { |
|
ScanChildren (objectCreateExpression); |
|
return typeResolveResult; |
|
} |
|
IType type = typeResolveResult.Type; |
|
|
|
List<ResolveResult> initializerStatements = null; |
|
var initializer = objectCreateExpression.Initializer; |
|
if (!initializer.IsNull) { |
|
initializerStatements = new List<ResolveResult>(); |
|
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 && crr.Input == arguments[0]) { |
|
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 WrapResult(rr); |
|
} else { |
|
return rr; |
|
} |
|
} else { |
|
// process conversions in all other cases |
|
ProcessInvocationResult(null, objectCreateExpression.Arguments, rr); |
|
return rr; |
|
} |
|
} |
|
|
|
void HandleObjectInitializer(ResolveResult initializedObject, ArrayInitializerExpression initializer, List<ResolveResult> 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<IType>.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<ResolveResult>.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) |
|
{ |
|
if (resolverEnabled) { |
|
return Resolve(parenthesizedExpression.Expression); |
|
} else { |
|
Scan(parenthesizedExpression.Expression); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) |
|
{ |
|
if (resolverEnabled) { |
|
ResolveResult target = Resolve(pointerReferenceExpression.Target); |
|
ResolveResult deferencedTarget = resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, target); |
|
List<IType> typeArguments = new List<IType>(); |
|
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<ResolveResult>.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) |
|
{ |
|
return resolver.ResolvePrimitive(primitiveExpression.Value); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) |
|
{ |
|
return resolver.ResolveSizeOf(ResolveType(sizeOfExpression.Type)); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) |
|
{ |
|
ResolveAndProcessConversion(stackAllocExpression.CountExpression, resolver.Compilation.FindType(KnownTypeCode.Int32)); |
|
return new ResolveResult(new PointerType(ResolveType(stackAllocExpression.Type))); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) |
|
{ |
|
return resolver.ResolveThisReference(); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitTypeOfExpression(TypeOfExpression typeOfExpression) |
|
{ |
|
if (resolverEnabled) { |
|
return resolver.ResolveTypeOf(ResolveType(typeOfExpression.Type)); |
|
} else { |
|
Scan(typeOfExpression.Type); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) |
|
{ |
|
if (resolverEnabled) { |
|
return Resolve(typeReferenceExpression.Type).ShallowClone(); |
|
} else { |
|
Scan(typeReferenceExpression.Type); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.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<IType> ResolveTypeArguments(IEnumerable<AstType> typeArguments) |
|
{ |
|
List<IType> result = new List<IType>(); |
|
foreach (AstType typeArgument in typeArguments) { |
|
result.Add(ResolveType(typeArgument)); |
|
} |
|
return result; |
|
} |
|
|
|
/// <summary> |
|
/// Gets and resolves the arguments; unpacking any NamedArgumentExpressions. |
|
/// </summary> |
|
/// <remarks> |
|
/// 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(). |
|
/// </remarks> |
|
ResolveResult[] GetArguments(IEnumerable<Expression> 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; |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether 'rr' is considered a static access on the target identifier. |
|
/// </summary> |
|
/// <param name="rr">Resolve Result of the MemberReferenceExpression</param> |
|
/// <param name="invocationRR">Resolve Result of the InvocationExpression</param> |
|
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<ResolveResult>.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<ResolveResult>.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<IType>.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<ResolveResult>.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<IType>.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<ResolveResult>.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) |
|
{ |
|
return HandleExplicitlyTypedLambda( |
|
anonymousMethodExpression.Parameters, anonymousMethodExpression.Body, |
|
isAnonymousMethod: true, |
|
hasParameterList: anonymousMethodExpression.HasParameterList, |
|
isAsync: anonymousMethodExpression.IsAsync); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ParameterDeclaration> parameterDeclarations, |
|
AstNode body, bool isAnonymousMethod, bool hasParameterList, bool isAsync) |
|
{ |
|
CSharpResolver oldResolver = resolver; |
|
List<IParameter> parameters = (hasParameterList || parameterDeclarations.Any()) ? new List<IParameter>() : 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, 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<IParameter> parameters; |
|
readonly bool isAnonymousMethod; |
|
readonly bool isAsync; |
|
|
|
CSharpResolver storedContext; |
|
ResolveVisitor visitor; |
|
AstNode body; |
|
|
|
IType inferredReturnType; |
|
IList<Expression> returnExpressions; |
|
IList<ResolveResult> returnValues; |
|
bool isValidAsVoidMethod; |
|
bool isEndpointUnreachable; |
|
|
|
// The actual return type is set when the lambda is applied by the conversion. |
|
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 (body is Expression) { |
|
Analyze(); |
|
if (returnValues.Count == 1) |
|
return returnValues[0]; |
|
} |
|
return visitor.voidResult; |
|
} |
|
} |
|
|
|
public ExplicitlyTypedLambda(IList<IParameter> 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<LambdaBase>(); |
|
visitor.undecidedLambdas.Add(this); |
|
Log.WriteLine("Added undecided explicitly-typed lambda: " + this.LambdaExpression); |
|
} |
|
|
|
public override IList<IParameter> Parameters { |
|
get { |
|
return parameters ?? EmptyList<IParameter>.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 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(); |
|
Log.WriteLine("Applying return type {0} to explicitly-typed lambda {1}", returnType, this.LambdaExpression); |
|
if (isAsync) |
|
returnType = parentVisitor.UnpackTask(returnType); |
|
if (returnType.Kind != TypeKind.Void) { |
|
for (int i = 0; i < returnExpressions.Count; i++) { |
|
visitor.ProcessConversion(returnExpressions[i], returnValues[i], returnType); |
|
} |
|
} |
|
} |
|
|
|
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<LambdaTypeHypothesis> hypotheses = new List<LambdaTypeHypothesis>(); |
|
internal IList<IParameter> parameters = new List<IParameter>(); |
|
|
|
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, parentVisitor.MakeRegion(pd))); |
|
} |
|
RegisterUndecidedLambda(); |
|
} |
|
|
|
public ImplicitlyTypedLambda(QuerySelectClause selectClause, IEnumerable<IParameter> 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<LambdaBase>(); |
|
parentVisitor.undecidedLambdas.Add(this); |
|
Log.WriteLine("Added undecided implicitly-typed lambda: " + this.LambdaExpression); |
|
} |
|
|
|
public override IList<IParameter> 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<IType>(", ", 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); |
|
hypotheses.Add(newHypothesis); |
|
return newHypothesis; |
|
} |
|
|
|
/// <summary> |
|
/// Get any hypothesis for this lambda. |
|
/// This method is used as fallback if the lambda isn't merged the normal way (AnonymousFunctionConversion) |
|
/// </summary> |
|
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 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 + "]"; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
sealed class LambdaTypeHypothesis : IResolveVisitorNavigator |
|
{ |
|
readonly ImplicitlyTypedLambda lambda; |
|
readonly IParameter[] lambdaParameters; |
|
internal readonly IType[] parameterTypes; |
|
readonly ResolveVisitor visitor; |
|
|
|
internal readonly IType inferredReturnType; |
|
IList<Expression> returnExpressions; |
|
IList<ResolveResult> returnValues; |
|
bool isValidAsVoidMethod; |
|
bool isEndpointUnreachable; |
|
internal bool success; |
|
|
|
public LambdaTypeHypothesis(ImplicitlyTypedLambda lambda, IType[] parameterTypes, ResolveVisitor visitor, |
|
ICollection<ParameterDeclaration> parameterDeclarations) |
|
{ |
|
Debug.Assert(parameterTypes.Length == lambda.Parameters.Count); |
|
|
|
this.lambda = lambda; |
|
this.parameterTypes = parameterTypes; |
|
this.visitor = visitor; |
|
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, 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, 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.winningHypothesis = this; |
|
lambda.parameters = lambdaParameters; // replace untyped parameters with typed parameters |
|
if (lambda.BodyExpression is Expression && returnValues.Count == 1) { |
|
lambda.bodyResult = returnValues[0]; |
|
} |
|
|
|
Log.WriteLine("Applying return type {0} to implicitly-typed lambda {1}", returnType, lambda.LambdaExpression); |
|
if (lambda.IsAsync) |
|
returnType = parentVisitor.UnpackTask(returnType); |
|
if (returnType.Kind != TypeKind.Void) { |
|
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]; |
|
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<Expression> returnExpressions, out IList<ResolveResult> 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<Expression>.Instance; |
|
returnValues = EmptyList<ResolveResult>.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]]; |
|
} |
|
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<ResolveResult> 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 && 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<T> |
|
if (!(IsTask(returnType) && returnType.TypeParameterCount == 1)) |
|
return false; |
|
// unpack Task<T> 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; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the T in Task<T>. |
|
/// Returns void for non-generic Task. |
|
/// </summary> |
|
IType UnpackTask(IType type) |
|
{ |
|
if (!IsTask(type)) |
|
return type; |
|
if (type.TypeParameterCount == 0) |
|
return resolver.Compilation.FindType(KnownTypeCode.Void); |
|
else |
|
return ((ParameterizedType)type).GetTypeArgument(0); |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether the specified type is Task or Task<T>. |
|
/// </summary> |
|
static bool IsTask(IType type) |
|
{ |
|
ITypeDefinition def = type.GetDefinition(); |
|
if (def != null) { |
|
if (def.KnownTypeCode == KnownTypeCode.Task) |
|
return true; |
|
if (def.KnownTypeCode == KnownTypeCode.TaskOfT) |
|
return type is ParameterizedType; |
|
} |
|
return false; |
|
} |
|
|
|
sealed class AnalyzeLambdaVisitor : DepthFirstAstVisitor |
|
{ |
|
public bool HasVoidReturnStatements; |
|
public List<Expression> ReturnExpressions = new List<Expression>(); |
|
|
|
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<ResolveResult>.VisitForeachStatement(ForeachStatement foreachStatement) |
|
{ |
|
var compilation = resolver.Compilation; |
|
ResolveResult expression = Resolve(foreachStatement.InExpression); |
|
bool isImplicitlyTypedVariable = IsVar(foreachStatement.VariableType); |
|
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<IType>.Instance, NameLookupMode.InvocationTarget); |
|
getEnumeratorInvocation = resolver.ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]); |
|
} else { |
|
var getEnumeratorMethodGroup = memberLookup.Lookup(expression, "GetEnumerator", EmptyList<IType>.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<IType>.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<IType>.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<IType>.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<IType>.Instance, NameLookupMode.InvocationTarget); |
|
getEnumeratorInvocation = resolver.ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]); |
|
} |
|
#endregion |
|
|
|
#region Local Variable Scopes (Block Statements) |
|
ResolveResult IAstVisitor<ResolveResult>.VisitBlockStatement(BlockStatement blockStatement) |
|
{ |
|
resolver = resolver.PushBlock(); |
|
ScanChildren(blockStatement); |
|
resolver = resolver.PopBlock(); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.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<ResolveResult>.VisitSwitchStatement(SwitchStatement switchStatement) |
|
{ |
|
resolver = resolver.PushBlock(); |
|
ScanChildren(switchStatement); |
|
resolver = resolver.PopBlock(); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) |
|
{ |
|
bool isConst = (variableDeclarationStatement.Modifiers & Modifiers.Const) != 0; |
|
if (!isConst && IsVar(variableDeclarationStatement.Type) && 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) { |
|
v = MakeConstant(type, vi.NameToken, Resolve(vi.Initializer).ConstantValue); |
|
} else { |
|
v = MakeVariable(type, vi.NameToken); |
|
} |
|
resolver = resolver.AddVariable(v); |
|
Scan(vi); |
|
} |
|
} |
|
return voidResult; |
|
} |
|
#endregion |
|
|
|
#region Condition Statements |
|
ResolveResult IAstVisitor<ResolveResult>.VisitForStatement(ForStatement forStatement) |
|
{ |
|
resolver = resolver.PushBlock(); |
|
var result = HandleConditionStatement(forStatement); |
|
resolver = resolver.PopBlock(); |
|
return result; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitIfElseStatement(IfElseStatement ifElseStatement) |
|
{ |
|
return HandleConditionStatement(ifElseStatement); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitWhileStatement(WhileStatement whileStatement) |
|
{ |
|
return HandleConditionStatement(whileStatement); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitReturnStatement(ReturnStatement returnStatement) |
|
{ |
|
if (resolverEnabled && !resolver.IsWithinLambdaExpression && resolver.CurrentMember != null) { |
|
IType type = resolver.CurrentMember.ReturnType; |
|
if (IsTask(type)) { |
|
var methodDecl = returnStatement.Ancestors.OfType<EntityDeclaration>().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<ResolveResult>.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<ResolveResult>.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) |
|
{ |
|
return voidResult; |
|
} |
|
#endregion |
|
|
|
#region Other statements |
|
ResolveResult IAstVisitor<ResolveResult>.VisitExpressionStatement(ExpressionStatement expressionStatement) |
|
{ |
|
ScanChildren(expressionStatement); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitLockStatement(LockStatement lockStatement) |
|
{ |
|
ScanChildren(lockStatement); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitEmptyStatement(EmptyStatement emptyStatement) |
|
{ |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitBreakStatement(BreakStatement breakStatement) |
|
{ |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitContinueStatement(ContinueStatement continueStatement) |
|
{ |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitThrowStatement(ThrowStatement throwStatement) |
|
{ |
|
if (resolverEnabled) { |
|
ResolveAndProcessConversion(throwStatement.Expression, resolver.Compilation.FindType(KnownTypeCode.Exception)); |
|
return voidResult; |
|
} else { |
|
Scan(throwStatement.Expression); |
|
return null; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) |
|
{ |
|
ScanChildren(tryCatchStatement); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) |
|
{ |
|
ScanChildren(gotoCaseStatement); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) |
|
{ |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitGotoStatement(GotoStatement gotoStatement) |
|
{ |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitLabelStatement(LabelStatement labelStatement) |
|
{ |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitUnsafeStatement(UnsafeStatement unsafeStatement) |
|
{ |
|
resolver = resolver.PushBlock(); |
|
ScanChildren(unsafeStatement); |
|
resolver = resolver.PopBlock(); |
|
return voidResult; |
|
} |
|
#endregion |
|
|
|
#region Local Variable Type Inference |
|
static bool IsVar(AstNode returnType) |
|
{ |
|
SimpleType st = returnType as SimpleType; |
|
return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; |
|
} |
|
|
|
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 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 + ";"; |
|
} |
|
} |
|
|
|
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<ResolveResult>.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<NamedExpression>(); |
|
|
|
// Scan the non-constructor arguments |
|
resolver = resolver.PushObjectInitializer(new InitializedObjectResolveResult(type)); |
|
List<ResolveResult> initializerStatements = new List<ResolveResult>(); |
|
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<ResolveResult>.VisitAttributeSection(AttributeSection attributeSection) |
|
{ |
|
ScanChildren(attributeSection); |
|
return voidResult; |
|
} |
|
#endregion |
|
|
|
#region Using Declaration |
|
ResolveResult IAstVisitor<ResolveResult>.VisitUsingDeclaration(UsingDeclaration usingDeclaration) |
|
{ |
|
ScanChildren(usingDeclaration); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) |
|
{ |
|
ScanChildren(usingDeclaration); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) |
|
{ |
|
return voidResult; |
|
} |
|
#endregion |
|
|
|
#region Type References |
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitSimpleType(SimpleType simpleType) |
|
{ |
|
if (!resolverEnabled) { |
|
ScanChildren(simpleType); |
|
return null; |
|
} |
|
|
|
// Figure out the correct lookup mode: |
|
NameLookupMode lookupMode = GetNameLookupMode(simpleType); |
|
|
|
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; |
|
} |
|
|
|
NameLookupMode GetNameLookupMode(AstType type) |
|
{ |
|
AstType outermostType = type; |
|
while (outermostType.Parent is AstType) |
|
outermostType = (AstType)outermostType.Parent; |
|
|
|
if (outermostType.Parent is UsingDeclaration || outermostType.Parent is UsingAliasDeclaration) { |
|
return NameLookupMode.TypeInUsingDeclaration; |
|
} else if (outermostType.Role == Roles.BaseType) { |
|
// Use BaseTypeReference for a type's base type, and for a constraint on a type. |
|
// Do not use it for a constraint on a method. |
|
if (outermostType.Parent is TypeDeclaration || (outermostType.Parent is Constraint && outermostType.Parent.Parent is TypeDeclaration)) |
|
return NameLookupMode.BaseTypeReference; |
|
} |
|
return NameLookupMode.Type; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitMemberType(MemberType memberType) |
|
{ |
|
ResolveResult target; |
|
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); |
|
} |
|
|
|
NameLookupMode lookupMode = GetNameLookupMode(memberType); |
|
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<ResolveResult>.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<ResolveResult>.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) |
|
{ |
|
// This assumes queries are only used on IEnumerable. |
|
// We might want to look at the signature of a LINQ method (e.g. Select) instead. |
|
bool? isGeneric; |
|
return GetElementTypeFromIEnumerable(type, resolver.Compilation, false, out isGeneric); |
|
} |
|
|
|
ResolveResult MakeTransparentIdentifierResolveResult() |
|
{ |
|
return new ResolveResult(new AnonymousType(resolver.Compilation, EmptyList<IUnresolvedProperty>.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<IParameter> 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 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<ResolveResult>.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<IType>.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; |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
ResolveResult WrapResult(ResolveResult result) |
|
{ |
|
return new ConversionResolveResult(result.Type, result, Conversion.IdentityConversion, resolver.CheckForOverflow); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.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<IType>.Instance, NameLookupMode.InvocationTarget); |
|
ResolveResult[] arguments = { new QueryExpressionLambda(1, MakeTransparentIdentifierResolveResult()) }; |
|
return resolver.ResolveInvocation(methodGroup, arguments); |
|
} else { |
|
return errorResult; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<IType>.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<IType>.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<ResolveResult>.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<IType>.Instance); |
|
ResolveResult[] arguments = { new QueryExpressionLambda(1, condition) }; |
|
return resolver.ResolveInvocation(methodGroup, arguments); |
|
} else { |
|
return errorResult; |
|
} |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<IType>.Instance); |
|
ResolveResult[] arguments = { new QueryExpressionLambda(1, expr) }; |
|
return resolver.ResolveInvocation(methodGroup, arguments); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the name of the range variable in the specified query. |
|
/// If the query has multiple range variables, this method returns null. |
|
/// </summary> |
|
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<ResolveResult>.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<IType>.Instance); |
|
ResolveResult[] arguments = { |
|
new QueryExpressionLambda(1, key), |
|
new QueryExpressionLambda(1, projection) |
|
}; |
|
return resolver.ResolveInvocation(methodGroup, arguments); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitQueryOrderClause(QueryOrderClause queryOrderClause) |
|
{ |
|
foreach (QueryOrdering ordering in queryOrderClause.Orderings) { |
|
currentQueryResult = Resolve(ordering); |
|
} |
|
return WrapResult(currentQueryResult); |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.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<IType>.Instance); |
|
ResolveResult[] arguments = { |
|
new QueryExpressionLambda(1, sortKey), |
|
}; |
|
return resolver.ResolveInvocation(methodGroup, arguments); |
|
} |
|
#endregion |
|
|
|
#region Constructor Initializer |
|
ResolveResult IAstVisitor<ResolveResult>.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<ResolveResult>.VisitIdentifier(Identifier identifier) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitComment (Comment comment) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitNewLine (NewLineNode comment) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitWhitespace(WhitespaceNode whitespaceNode) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitText(TextNode textNode) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitArraySpecifier(ArraySpecifier arraySpecifier) |
|
{ |
|
return null; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitPatternPlaceholder(AstNode placeholder, ICSharpCode.NRefactory.PatternMatching.Pattern pattern) |
|
{ |
|
return null; |
|
} |
|
|
|
// Nodes where we just need to visit the children: |
|
ResolveResult IAstVisitor<ResolveResult>.VisitAccessor(Accessor accessor) |
|
{ |
|
ScanChildren(accessor); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitSwitchSection(SwitchSection switchSection) |
|
{ |
|
ScanChildren(switchSection); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitCaseLabel(CaseLabel caseLabel) |
|
{ |
|
ScanChildren(caseLabel); |
|
return voidResult; |
|
} |
|
|
|
ResolveResult IAstVisitor<ResolveResult>.VisitConstraint(Constraint constraint) |
|
{ |
|
ScanChildren(constraint); |
|
return voidResult; |
|
} |
|
#endregion |
|
|
|
#region Documentation Reference |
|
ResolveResult IAstVisitor<ResolveResult>.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.EntityType == EntityType.TypeDefinition) { |
|
if (declaringTypeDef != null) |
|
return new TypeResolveResult(declaringTypeDef); |
|
else |
|
return errorResult; |
|
} |
|
|
|
if (documentationReference.EntityType == EntityType.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.EntityType == EntityType.Indexer) { |
|
var indexers = declaringTypeDef.Properties.Where(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation); |
|
return FindByParameters(indexers, parameters); |
|
} else if (documentationReference.EntityType == EntityType.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<IParameterizedMember> methods, IList<IParameter> 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 |
|
} |
|
}
|
|
|