Browse Source

Introduced NamedArgumentResolveResult.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
d6b4420940
  1. 2
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
  2. 11
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs
  3. 2
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  4. 55
      ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
  5. 135
      ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
  6. 70
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs
  7. 1
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  8. 81
      ICSharpCode.NRefactory/Semantics/NamedArgumentResolveResult.cs

2
ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs

@ -275,7 +275,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -275,7 +275,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
return true;
}
return (node.NodeType == NodeType.Whitespace || node is ArraySpecifier || node is NamedArgumentExpression);
return (node.NodeType == NodeType.Whitespace || node is ArraySpecifier);
}
}
}

11
ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs

@ -96,10 +96,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -96,10 +96,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mappedTo = IsExpandedForm ? Math.Min(i, results.Length - 1) : i;
if (mappedTo >= 0 && mappedTo < results.Length) {
if (IsExpandedForm && mappedTo == results.Length - 1)
if (IsExpandedForm && mappedTo == results.Length - 1) {
paramsArguments.Add(Arguments[i]);
else
results[mappedTo] = Arguments[i];
} else {
var narr = Arguments[i] as NamedArgumentResolveResult;
if (narr != null)
results[mappedTo] = narr.Argument;
else
results[mappedTo] = Arguments[i];
}
}
}
if (IsExpandedForm)

2
ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs

@ -1954,7 +1954,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1954,7 +1954,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
or.AddCandidate(invokeMethod);
return new CSharpInvocationResolveResult(
target, invokeMethod, //invokeMethod.ReturnType.Resolve(context),
or.GetArgumentsWithConversions(), or.BestCandidateErrors,
or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors,
isExpandedForm: or.BestCandidateIsExpandedForm,
isDelegateInvocation: true,
argumentToParameterMap: or.GetArgumentToParameterMap());

55
ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs

@ -781,15 +781,35 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -781,15 +781,35 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return null;
}
/// <summary>
/// Returns the arguments for the method call in the order they were provided (not in the order of the parameters).
/// Arguments are wrapped in a <see cref="ConversionResolveResult"/> if an implicit conversion is being applied
/// to them when calling the method.
/// </summary>
public IList<ResolveResult> GetArgumentsWithConversions()
{
if (bestCandidate == null)
return arguments;
else
return GetArgumentsWithConversions(null);
return GetArgumentsWithConversions(null, null);
}
/// <summary>
/// Returns the arguments for the method call in the order they were provided (not in the order of the parameters).
/// Arguments are wrapped in a <see cref="ConversionResolveResult"/> if an implicit conversion is being applied
/// to them when calling the method.
/// For arguments where an explicit argument name was provided, the argument will
/// be wrapped in a <see cref="NamedArgumentResolveResult"/>.
/// </summary>
public IList<ResolveResult> GetArgumentsWithConversionsAndNames()
{
if (bestCandidate == null)
return arguments;
else
return GetArgumentsWithConversions(null, GetBestCandidateWithSubstitutedTypeArguments());
}
IList<ResolveResult> GetArgumentsWithConversions(ResolveResult targetResolveResult)
IList<ResolveResult> GetArgumentsWithConversions(ResolveResult targetResolveResult, IParameterizedMember bestCandidateForNamedArguments)
{
var conversions = this.ArgumentConversions;
ResolveResult[] args = new ResolveResult[arguments.Length];
@ -797,22 +817,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -797,22 +817,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
var argument = arguments[i];
if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null)
argument = targetResolveResult;
if (conversions[i] == Conversion.IdentityConversion) {
args[i] = argument;
} else {
int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
IType parameterType;
if (parameterIndex >= 0) {
parameterType = bestCandidate.ParameterTypes[parameterIndex];
} else {
parameterType = SpecialType.UnknownType;
int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
if (parameterIndex >= 0 && conversions[i] != Conversion.IdentityConversion) {
// Wrap argument in ConversionResolveResult
IType parameterType = bestCandidate.ParameterTypes[parameterIndex];
if (parameterType.Kind != TypeKind.Unknown) {
if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) {
argument = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument);
} else {
argument = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow);
}
}
if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) {
args[i] = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument);
}
if (bestCandidateForNamedArguments != null && argumentNames[i] != null) {
// Wrap argument in NamedArgumentResolveResult
if (parameterIndex >= 0) {
argument = new NamedArgumentResolveResult(bestCandidateForNamedArguments.Parameters[parameterIndex], argument, bestCandidateForNamedArguments);
} else {
args[i] = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow);
argument = new NamedArgumentResolveResult(argumentNames[i], argument);
}
}
args[i] = argument;
}
return args;
}
@ -857,7 +882,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -857,7 +882,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return new CSharpInvocationResolveResult(
this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType) : targetResolveResult,
member,
GetArgumentsWithConversions(targetResolveResult),
GetArgumentsWithConversions(targetResolveResult, member),
this.BestCandidateErrors,
this.IsExtensionMethodInvocation,
this.BestCandidateIsExpandedForm,

135
ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs

@ -27,7 +27,6 @@ using ICSharpCode.NRefactory.CSharp.TypeSystem; @@ -27,7 +27,6 @@ using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
@ -393,10 +392,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -393,10 +392,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
void MarkUnknownNamedArguments(IEnumerable<Expression> arguments)
{
foreach (var nae in arguments.OfType<NamedArgumentExpression>()) {
StoreCurrentState(nae);
StoreResult(nae, new NamedArgumentResolveResult(nae.Name, resolveResultCache[nae.Expression]));
}
}
void ProcessConversionsInInvocation(Expression target, IEnumerable<Expression> arguments, CSharpInvocationResolveResult invocation)
{
if (invocation == null)
if (invocation == null) {
// we still need to handle the named arguments if invocation==null
MarkUnknownNamedArguments(arguments);
return;
}
int i = 0;
if (invocation.IsExtensionMethodInvocation) {
Debug.Assert(arguments.Count() + 1 == invocation.Arguments.Count);
@ -406,11 +416,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -406,11 +416,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Debug.Assert(arguments.Count() == invocation.Arguments.Count);
}
foreach (Expression arg in arguments) {
ResolveResult argRR = invocation.Arguments[i++];
NamedArgumentExpression nae = arg as NamedArgumentExpression;
if (nae != null)
ProcessConversionResult(nae.Expression, invocation.Arguments[i++] as ConversionResolveResult);
else
ProcessConversionResult(arg, invocation.Arguments[i++] as ConversionResolveResult);
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);
}
}
}
#endregion
@ -1398,7 +1414,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1398,7 +1414,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult IAstVisitor<ResolveResult>.VisitIndexerExpression(IndexerExpression indexerExpression)
{
if (resolverEnabled) {
if (resolverEnabled || NeedsResolvingDueToNamedArguments(indexerExpression)) {
Expression target = indexerExpression.Target;
ResolveResult targetResult = Resolve(target);
string[] argumentNames;
@ -1406,6 +1422,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1406,6 +1422,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult rr = resolver.ResolveIndexer(targetResult, arguments, argumentNames);
ArrayAccessResolveResult aarr = rr as ArrayAccessResolveResult;
if (aarr != null) {
MarkUnknownNamedArguments(indexerExpression.Arguments);
ProcessConversionResults(indexerExpression.Arguments, aarr.Indexes);
} else {
ProcessConversionsInInvocation(target, indexerExpression.Arguments, rr as CSharpInvocationResolveResult);
@ -1437,8 +1454,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1437,8 +1454,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// by calling GetArguments().
// This method gets called only when scanning, or when the named argument is used
// in an invalid context.
Scan(namedArgumentExpression.Expression);
return null;
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
@ -1477,48 +1498,44 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1477,48 +1498,44 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult IAstVisitor<ResolveResult>.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
{
if (resolverEnabled || !objectCreateExpression.Initializer.IsNull) {
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) {
// 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;
}
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 {
// process conversions in all other cases
ProcessConversionsInInvocation(null, objectCreateExpression.Arguments, rr as CSharpInvocationResolveResult);
return rr;
}
} else {
ScanChildren(objectCreateExpression);
return null;
// process conversions in all other cases
ProcessConversionsInInvocation(null, objectCreateExpression.Arguments, rr as CSharpInvocationResolveResult);
return rr;
}
}
@ -1701,6 +1718,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1701,6 +1718,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
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;
@ -1722,6 +1748,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1722,6 +1748,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
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;
@ -1840,7 +1875,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1840,7 +1875,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
} else {
// Regular code path
if (resolverEnabled) {
if (resolverEnabled || NeedsResolvingDueToNamedArguments(invocationExpression)) {
ResolveResult target = Resolve(invocationExpression.Target);
return ResolveInvocationOnGivenTarget(target, invocationExpression);
} else {
@ -3779,10 +3814,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -3779,10 +3814,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Constructor Initializer
ResolveResult IAstVisitor<ResolveResult>.VisitConstructorInitializer(ConstructorInitializer constructorInitializer)
{
if (!resolverEnabled) {
ScanChildren(constructorInitializer);
return null;
}
ResolveResult target;
if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.Base) {
target = resolver.ResolveBaseReference();

70
ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs

@ -539,5 +539,75 @@ class D : B { @@ -539,5 +539,75 @@ class D : B {
Assert.IsFalse(rr.IsError);
Assert.IsFalse(rr.IsVirtualCall);
}
[Test]
public void NamedArgument()
{
string program = @"
class Test {
public void F(int x) {}
public void Test() {
F($x: 0$);
}
}";
var narr = Resolve<NamedArgumentResolveResult>(program);
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
Assert.AreEqual("x", narr.ParameterName);
Assert.AreEqual("Test.F", narr.Member.FullName);
Assert.AreSame(narr.Member.Parameters.Single(), narr.Parameter);
}
[Test]
public void NamedArgumentInInvocation()
{
string program = @"
class Test {
public void F(int x) {}
public void Test() {
$F(x: 0)$;
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsInstanceOf<NamedArgumentResolveResult>(rr.Arguments.Single());
var narr = (NamedArgumentResolveResult)rr.Arguments.Single();
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
Assert.AreEqual("x", narr.ParameterName);
Assert.AreEqual("Test.F", narr.Member.FullName);
Assert.AreSame(narr.Member.Parameters.Single(), narr.Parameter);
// but GetArgumentsForCall() should directly return the constant:
Assert.IsInstanceOf<ConstantResolveResult>(rr.GetArgumentsForCall().Single());
}
[Test]
public void UnknownNamedArgument()
{
string program = @"
class Test {
public void F(int x) {}
public void Test() {
F($y: 0$);
}
}";
var narr = Resolve<NamedArgumentResolveResult>(program);
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
Assert.AreEqual("y", narr.ParameterName);
Assert.IsNull(narr.Parameter);
}
[Test]
public void NamedArgumentInMissingMethod()
{
string program = @"
class Test {
public void Test() {
Missing($x: 0$);
}
}";
var narr = Resolve<NamedArgumentResolveResult>(program);
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
Assert.AreEqual("x", narr.ParameterName);
Assert.IsNull(narr.Parameter);
}
}
}

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -120,6 +120,7 @@ @@ -120,6 +120,7 @@
<Compile Include="Semantics\InvocationResolveResult.cs" />
<Compile Include="Semantics\LocalResolveResult.cs" />
<Compile Include="Semantics\MemberResolveResult.cs" />
<Compile Include="Semantics\NamedArgumentResolveResult.cs" />
<Compile Include="Semantics\NamespaceResolveResult.cs" />
<Compile Include="Semantics\OperatorResolveResult.cs" />
<Compile Include="Semantics\ResolveResult.cs" />

81
ICSharpCode.NRefactory/Semantics/NamedArgumentResolveResult.cs

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.Semantics
{
/// <summary>
/// Represents a named argument.
/// </summary>
public class NamedArgumentResolveResult : ResolveResult
{
/// <summary>
/// Gets the member to which the parameter belongs.
/// This field can be null.
/// </summary>
public readonly IParameterizedMember Member;
/// <summary>
/// Gets the parameter.
/// This field can be null.
/// </summary>
public readonly IParameter Parameter;
/// <summary>
/// Gets the parameter name.
/// </summary>
public readonly string ParameterName;
/// <summary>
/// Gets the argument passed to the parameter.
/// </summary>
public readonly ResolveResult Argument;
public NamedArgumentResolveResult(IParameter parameter, ResolveResult argument, IParameterizedMember member = null)
: base(argument.Type)
{
if (parameter == null)
throw new ArgumentNullException("parameter");
if (argument == null)
throw new ArgumentNullException("argument");
this.Member = member;
this.Parameter = parameter;
this.ParameterName = parameter.Name;
this.Argument = argument;
}
public NamedArgumentResolveResult(string parameterName, ResolveResult argument)
: base(argument.Type)
{
if (parameterName == null)
throw new ArgumentNullException("parameterName");
if (argument == null)
throw new ArgumentNullException("argument");
this.ParameterName = parameterName;
this.Argument = argument;
}
public override IEnumerable<ResolveResult> GetChildResults()
{
return new [] { Argument };
}
}
}
Loading…
Cancel
Save