Browse Source

Update to NRefactory 5.3.0-37-g52d116e

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
51675a7180
  1. 45
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs
  2. 29
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs
  3. 41
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs
  4. 7
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs
  5. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs
  6. 1
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  7. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  8. 6
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/ConvertToInitializerAction.cs
  9. 17
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/StatementsToInitializerConverter.cs
  10. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateClassDeclarationAction.cs
  11. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateFieldAction.cs
  12. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/IntroduceFormatItemAction.cs
  13. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/JoinStringAction.cs
  14. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/SplitStringAction.cs
  15. 10
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/CallToVirtualFunctionFromConstructorIssue.cs
  16. 22
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/InconsistentNamingIssue/NamingRule.cs
  17. 16
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/NegativeRelationalExpressionIssue.cs
  18. 51
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs
  19. 228
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  20. 5
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs
  21. 27
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  22. 51
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
  23. 19
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs
  24. 37
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertToInitializer/ConvertToInitializerTests.cs
  25. 27
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionAccessibleTests.cs
  26. 25
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs
  27. 79
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs
  28. 19
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/CallToVirtualFunctionFromConstructorTests.cs
  29. 33
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/NegativeRelationalExpressionIssueTests.cs
  30. 48
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterNotUsedIssueTests.cs
  31. 420
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  32. 32
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs
  33. 344
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs
  34. 63
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs
  35. 12
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs
  36. 100
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs
  37. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  38. 1
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs
  39. 12
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
  40. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  41. 30
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/Conversion.cs
  42. 5
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs
  43. 8
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs
  44. 11
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedParameter.cs
  45. 2
      src/Libraries/NRefactory/NRefactory.sln
  46. 3
      src/Libraries/NRefactory/README

45
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs

@ -0,0 +1,45 @@
// Copyright (c) 2013 Daniel Grunwald
//
// 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;
namespace ICSharpCode.NRefactory.CSharp
{
/// <summary>
/// Extension methods for the syntax tree.
/// </summary>
public static class SyntaxExtensions
{
public static bool IsComparisonOperator(this OperatorType operatorType)
{
switch (operatorType) {
case OperatorType.Equality:
case OperatorType.Inequality:
case OperatorType.GreaterThan:
case OperatorType.LessThan:
case OperatorType.GreaterThanOrEqual:
case OperatorType.LessThanOrEqual:
return true;
default:
return false;
}
}
}
}

29
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs

@ -1862,7 +1862,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
Action<ICompletionData, IType> typeCallback = null; Action<ICompletionData, IType> typeCallback = null;
var inferredTypesCategory = new Category("Inferred Types", null); var inferredTypesCategory = new Category("Inferred Types", null);
var derivedTypesCategory = new Category("Derived Types", null); var derivedTypesCategory = new Category("Derived Types", null);
if (hintType != null) { if (hintType != null) {
if (hintType.Kind != TypeKind.Unknown) { if (hintType.Kind != TypeKind.Unknown) {
var lookup = new MemberLookup( var lookup = new MemberLookup(
@ -1884,23 +1883,27 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
// check for valid constructors // check for valid constructors
if (t.GetConstructors().Count() > 0) { if (t.GetConstructors().Count() > 0) {
bool isProtectedAllowed = currentType != null ? bool isProtectedAllowed = currentType != null ?
currentType.Resolve(ctx).GetDefinition().IsDerivedFrom(t.GetDefinition()) : currentType.Resolve(ctx).GetDefinition().IsDerivedFrom(t.GetDefinition()) : false;
false; if (!t.GetConstructors().Any(m => lookup.IsAccessible(m, isProtectedAllowed))) {
if (!t.GetConstructors().Any(m => lookup.IsAccessible(
m,
isProtectedAllowed
)
)) {
return null; return null;
} }
} }
// check derived types
var typeDef = t.GetDefinition();
var hintDef = hintType.GetDefinition();
if (typeDef != null && hintDef != null && typeDef.IsDerivedFrom(hintDef)) {
var newType = wrapper.AddType(t, true);
if (newType != null) {
newType.CompletionCategory = inferredTypesCategory;
}
}
// check type inference
var typeInference = new TypeInference(Compilation); var typeInference = new TypeInference(Compilation);
typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults; typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults;
var inferedType = typeInference.FindTypeInBounds(
new [] { t }, var inferedType = typeInference.FindTypeInBounds (new [] { t }, new [] { hintType });
new [] { hintType }
);
if (inferedType != SpecialType.UnknownType) { if (inferedType != SpecialType.UnknownType) {
var newType = wrapper.AddType(inferedType, true); var newType = wrapper.AddType(inferedType, true);
if (newType != null) { if (newType != null) {
@ -2997,7 +3000,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
restart: restart:
string lineText = document.GetText(line); string lineText = document.GetText(line);
if (!lineText.Trim().StartsWith("///")) if (!lineText.Trim().StartsWith("///", StringComparison.Ordinal))
return null; return null;
int startIndex = Math.Min(location.Column - 1, lineText.Length - 1) - 1; int startIndex = Math.Min(location.Column - 1, lineText.Length - 1) - 1;
while (startIndex > 0 && lineText [startIndex] != '<') { while (startIndex > 0 && lineText [startIndex] != '<') {

41
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs

@ -109,6 +109,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) { if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) {
onlyStatic = true; onlyStatic = true;
} }
var methods = new List<IMethod>();
foreach (var method in resolveResult.Methods) { foreach (var method in resolveResult.Methods) {
if (method.IsConstructor) { if (method.IsConstructor) {
continue; continue;
@ -118,15 +119,47 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
if (onlyStatic && !method.IsStatic) { if (onlyStatic && !method.IsStatic) {
continue; continue;
} }
yield return method; if (method.IsShadowing) {
for (int j = 0; j < methods.Count; j++) {
if (ParameterListComparer.Instance.Equals(methods[j].Parameters, method.Parameters)) {
methods.RemoveAt (j);
j--;
}
}
}
methods.Add (method);
} }
foreach (var m in methods)
yield return m;
foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) {
foreach (var method in extMethods) { foreach (var method in extMethods) {
yield return method; yield return method;
} }
} }
} }
IEnumerable<IProperty> GetAccessibleIndexers(IType type)
{
var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly);
var properties = new List<IProperty>();
foreach (var property in type.GetProperties ()) {
if (!property.IsIndexer)
continue;
if (!lookup.IsAccessible (property, true))
continue;
if (property.IsShadowing) {
for (int j = 0; j < properties.Count; j++) {
if (ParameterListComparer.Instance.Equals(properties[j].Parameters, property.Parameters)) {
properties.RemoveAt (j);
j--;
}
}
}
properties.Add (property);
}
return properties;
}
public IParameterDataProvider GetParameterDataProvider(int offset, char completionChar) public IParameterDataProvider GetParameterDataProvider(int offset, char completionChar)
{ {
@ -259,7 +292,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
} }
} }
if (resolveResult != null) { if (resolveResult != null) {
return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), resolveResult.Type, invoke.Node); return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), resolveResult.Type, GetAccessibleIndexers (resolveResult.Type), invoke.Node);
} }
break; break;
case '<': case '<':
@ -285,7 +318,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
if (indexerExpression == null || indexerExpression.Item1 == null || indexerExpression.Item1.IsError) { if (indexerExpression == null || indexerExpression.Item1 == null || indexerExpression.Item1.IsError) {
return null; return null;
} }
return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), indexerExpression.Item1.Type, invoke.Node); return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), indexerExpression.Item1.Type, GetAccessibleIndexers (indexerExpression.Item1.Type), invoke.Node);
} }
return null; return null;
} }

7
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs

@ -111,7 +111,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
ICompletionData usedType; ICompletionData usedType;
var data = Factory.CreateTypeCompletionData(type, showFullName, isInAttributeContext); var data = Factory.CreateTypeCompletionData(type, showFullName, isInAttributeContext);
var text = data.DisplayText; var text = data.DisplayText;
if (typeDisplayText.TryGetValue(text, out usedType)) { if (typeDisplayText.TryGetValue(text, out usedType)) {
usedType.AddOverload(data); usedType.AddOverload(data);
return usedType; return usedType;
@ -158,9 +157,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
public ICompletionData AddMember (IMember member) public ICompletionData AddMember (IMember member)
{ {
if (data.ContainsKey (member.Name))
return null;
var newData = Factory.CreateEntityCompletionData (member); var newData = Factory.CreateEntityCompletionData (member);
if (member.ParentAssembly != completion.ctx.CurrentAssembly && !member.IsBrowsable ()) if (member.ParentAssembly != completion.ctx.CurrentAssembly && !member.IsBrowsable ())
@ -175,8 +171,9 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
} }
List<ICompletionData> existingData; List<ICompletionData> existingData;
data.TryGetValue (memberKey, out existingData); data.TryGetValue (memberKey, out existingData);
if (existingData != null) { if (existingData != null) {
if (member.EntityType == EntityType.Field || member.EntityType == EntityType.Property || member.EntityType == EntityType.Event)
return null;
var a = member as IEntity; var a = member as IEntity;
foreach (var d in existingData) { foreach (var d in existingData) {
if (!(d is IEntityCompletionData)) if (!(d is IEntityCompletionData))

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
IParameterDataProvider CreateDelegateDataProvider (int startOffset, IType type); IParameterDataProvider CreateDelegateDataProvider (int startOffset, IType type);
IParameterDataProvider CreateIndexerParameterDataProvider (int startOffset, IType type, AstNode resolvedNode); IParameterDataProvider CreateIndexerParameterDataProvider (int startOffset, IType type, IEnumerable<IProperty> accessibleIndexers, AstNode resolvedNode);
IParameterDataProvider CreateTypeParameterDataProvider (int startOffset, IEnumerable<IType> types); IParameterDataProvider CreateTypeParameterDataProvider (int startOffset, IEnumerable<IType> types);
} }

1
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -93,6 +93,7 @@
<Compile Include="Ast\AstType.cs" /> <Compile Include="Ast\AstType.cs" />
<Compile Include="Ast\DocumentationReference.cs" /> <Compile Include="Ast\DocumentationReference.cs" />
<Compile Include="Ast\IdentifierExpressionBackreference.cs" /> <Compile Include="Ast\IdentifierExpressionBackreference.cs" />
<Compile Include="Ast\SyntaxExtensions.cs" />
<Compile Include="Ast\SyntaxTree.cs" /> <Compile Include="Ast\SyntaxTree.cs" />
<Compile Include="Ast\ComposedType.cs" /> <Compile Include="Ast\ComposedType.cs" />
<Compile Include="Ast\CSharpModifierToken.cs" /> <Compile Include="Ast\CSharpModifierToken.cs" />

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -235,7 +235,7 @@ namespace ICSharpCode.NRefactory.CSharp
ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary); ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary);
ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary); ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary);
} else { } else {
// ?? is right-associate // ?? is right-associative
ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1); ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1);
ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence);
} }

6
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/ConvertToInitializerAction.cs

@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
IList<AstNode> statements = GetNodes(context.GetNode<Statement>()); IList<AstNode> statements = GetNodes(context.GetNode<Statement>());
var converter = new StatementsToInitializerConverter(context); var converter = new StatementsToInitializerConverter(context);
var newInitializer = converter.ConvertToInitializer(initializer, ref statements); var newInitializer = converter.ConvertToInitializer(initializer, ref statements);
if (statements.Count == 0) if (newInitializer == null || statements.Count == 0)
return null; return null;
return MakeAction(context, initializer, newInitializer, statements); return MakeAction(context, initializer, newInitializer, statements);
} }
@ -68,7 +68,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
CodeAction HandleExpressionStatement(RefactoringContext context, ExpressionStatement expressionStatement) CodeAction HandleExpressionStatement(RefactoringContext context, ExpressionStatement expressionStatement)
{ {
var expression = expressionStatement.Expression as AssignmentExpression; var expression = expressionStatement.Expression as AssignmentExpression;
if (expression == null) if (expression == null || expression.Operator != AssignmentOperatorType.Assign)
return null; return null;
if (!(expression.Right is ObjectCreateExpression)) if (!(expression.Right is ObjectCreateExpression))
return null; return null;
@ -78,7 +78,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
IList<AstNode> statements = GetNodes(context.GetNode<Statement>()); IList<AstNode> statements = GetNodes(context.GetNode<Statement>());
var converter = new StatementsToInitializerConverter(context); var converter = new StatementsToInitializerConverter(context);
var newExpression = converter.ConvertToInitializer(expression, ref statements); var newExpression = converter.ConvertToInitializer(expression, ref statements);
if (statements.Count == 0) if (newExpression == null || statements.Count == 0)
return null; return null;
return MakeAction(context, expression, newExpression, statements); return MakeAction(context, expression, newExpression, statements);
} }

17
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/StatementsToInitializerConverter.cs

@ -47,14 +47,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
this.context = context; this.context = context;
} }
void Initialize(AstNode targetNode) bool Initialize(AstNode targetNode)
{ {
var target = context.Resolve(targetNode); var target = context.Resolve(targetNode);
var targetInitializerPath = AccessPath.FromResolveResult(target); var targetInitializerPath = AccessPath.FromResolveResult(target);
if (targetInitializerPath == null) if (targetInitializerPath == null)
throw new ArgumentException(string.Format("Could not create the main initializer path from resolve result ({0})", target)); return false;
mainAccessPath = targetInitializerPath; mainAccessPath = targetInitializerPath;
return true;
} }
public VariableInitializer ConvertToInitializer(VariableInitializer variableInitializer, ref IList<AstNode> statements) public VariableInitializer ConvertToInitializer(VariableInitializer variableInitializer, ref IList<AstNode> statements)
@ -64,7 +65,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (statements == null) if (statements == null)
throw new ArgumentNullException("statements"); throw new ArgumentNullException("statements");
Initialize(variableInitializer); if (!Initialize(variableInitializer))
return null;
accessPaths [mainAccessPath] = variableInitializer.Initializer.Clone(); accessPaths [mainAccessPath] = variableInitializer.Initializer.Clone();
Convert(statements); Convert(statements);
@ -81,7 +83,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (!(assignmentExpression.Right is ObjectCreateExpression)) if (!(assignmentExpression.Right is ObjectCreateExpression))
throw new ArgumentException("assignmentExpression.Right must be an ObjectCreateExpression", "assignmentExpression"); throw new ArgumentException("assignmentExpression.Right must be an ObjectCreateExpression", "assignmentExpression");
Initialize(assignmentExpression.Left); if (!Initialize(assignmentExpression.Left))
return null;
accessPaths [mainAccessPath] = assignmentExpression.Right.Clone(); accessPaths [mainAccessPath] = assignmentExpression.Right.Clone();
Convert(statements); Convert(statements);
@ -124,12 +127,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return false; return false;
} }
variableInitializer = variableDeclarationStatement.Variables.FirstOrNullObject(); variableInitializer = variableDeclarationStatement.Variables.FirstOrNullObject();
if (variableInitializer.IsNull) if (variableInitializer.IsNull || variableInitializer.Initializer.IsNull)
return false; return false;
var sourceResolveResult = context.Resolve(variableInitializer.Initializer) as LocalResolveResult; var sourceResolveResult = context.Resolve(variableInitializer.Initializer) as LocalResolveResult;
if (HasDependency(variableInitializer.Initializer) && !CanReplaceDependent(sourceResolveResult)) if (HasDependency(variableInitializer.Initializer) && !CanReplaceDependent(sourceResolveResult))
return false; return false;
var targetResolveResult = context.Resolve(variableInitializer) as LocalResolveResult; var targetResolveResult = context.Resolve(variableInitializer) as LocalResolveResult;
if (targetResolveResult == null)
return false;
AddNewVariable(targetResolveResult.Variable, variableInitializer.Initializer, node); AddNewVariable(targetResolveResult.Variable, variableInitializer.Initializer, node);
return true; return true;
} }
@ -176,7 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
bool TryHandleAssignmentExpression(ExpressionStatement expressionStatement) bool TryHandleAssignmentExpression(ExpressionStatement expressionStatement)
{ {
var assignmentExpression = expressionStatement.Expression as AssignmentExpression; var assignmentExpression = expressionStatement.Expression as AssignmentExpression;
if (assignmentExpression == null) if (assignmentExpression == null || assignmentExpression.Operator != AssignmentOperatorType.Assign)
return false; return false;
var resolveResult = context.Resolve(assignmentExpression.Right); var resolveResult = context.Resolve(assignmentExpression.Right);
if (HasDependency(assignmentExpression.Right) && !CanReplaceDependent(resolveResult)) if (HasDependency(assignmentExpression.Right) && !CanReplaceDependent(resolveResult))

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateClassDeclarationAction.cs

@ -1,4 +1,4 @@
// //
// CreateClassDeclarationAction.cs // CreateClassDeclarationAction.cs
// //
// Author: // Author:
@ -86,7 +86,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
string className = simpleType.Identifier; string className = simpleType.Identifier;
if (simpleType.Parent is Attribute) { if (simpleType.Parent is Attribute) {
if (!className.EndsWith("Attribute")) if (!className.EndsWith("Attribute", System.StringComparison.Ordinal))
className += "Attribute"; className += "Attribute";
} }

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateFieldAction.cs

@ -272,7 +272,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return Enumerable.Empty<IType>(); return Enumerable.Empty<IType>();
} }
static readonly IType[] emptyTypes = new IType[0]; static readonly IType[] emptyTypes = new IType[0];
internal static AstType GuessAstType(RefactoringContext context, Expression expr) public static AstType GuessAstType(RefactoringContext context, Expression expr)
{ {
var type = GetValidTypes(context.Resolver, expr).ToArray(); var type = GetValidTypes(context.Resolver, expr).ToArray();
var typeInference = new TypeInference(context.Compilation); var typeInference = new TypeInference(context.Compilation);
@ -283,7 +283,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return context.CreateShortType(inferedType); return context.CreateShortType(inferedType);
} }
internal static IType GuessType(RefactoringContext context, Expression expr) public static IType GuessType(RefactoringContext context, Expression expr)
{ {
var type = GetValidTypes(context.Resolver, expr).ToArray(); var type = GetValidTypes(context.Resolver, expr).ToArray();
var typeInference = new TypeInference(context.Compilation); var typeInference = new TypeInference(context.Compilation);

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/IntroduceFormatItemAction.cs

@ -50,8 +50,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (pexpr == null || !(pexpr.Value is string)) { if (pexpr == null || !(pexpr.Value is string)) {
yield break; yield break;
} }
if (pexpr.LiteralValue.StartsWith("@")) { if (pexpr.LiteralValue.StartsWith("@", StringComparison.Ordinal)) {
if (!(pexpr.StartLocation < new TextLocation (context.Location.Line, context.Location.Column - 1) && new TextLocation (context.Location.Line, context.Location.Column + 1) < pexpr.EndLocation)) { if (!(pexpr.StartLocation < new TextLocation(context.Location.Line, context.Location.Column - 1) && new TextLocation(context.Location.Line, context.Location.Column + 1) < pexpr.EndLocation)) {
yield break; yield break;
} }
} else { } else {

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/JoinStringAction.cs

@ -47,8 +47,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
!(left.Value is string) || !(right.Value is string) || !node.OperatorToken.Contains(context.Location)) !(left.Value is string) || !(right.Value is string) || !node.OperatorToken.Contains(context.Location))
return null; return null;
var isLeftVerbatim = left.LiteralValue.StartsWith ("@"); var isLeftVerbatim = left.LiteralValue.StartsWith("@", System.StringComparison.Ordinal);
var isRightVerbatime = right.LiteralValue.StartsWith ("@"); var isRightVerbatime = right.LiteralValue.StartsWith("@", System.StringComparison.Ordinal);
if (isLeftVerbatim != isRightVerbatime) if (isLeftVerbatim != isRightVerbatime)
return null; return null;

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/SplitStringAction.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (pexpr == null || !(pexpr.Value is string)) { if (pexpr == null || !(pexpr.Value is string)) {
yield break; yield break;
} }
if (pexpr.LiteralValue.StartsWith("@")) { if (pexpr.LiteralValue.StartsWith("@", StringComparison.Ordinal)) {
if (!(pexpr.StartLocation < new TextLocation(context.Location.Line, context.Location.Column - 2) && if (!(pexpr.StartLocation < new TextLocation(context.Location.Line, context.Location.Column - 2) &&
new TextLocation(context.Location.Line, context.Location.Column + 2) < pexpr.EndLocation)) { new TextLocation(context.Location.Line, context.Location.Column + 2) < pexpr.EndLocation)) {
yield break; yield break;
@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
yield return new CodeAction(context.TranslateString("Split string literal"), script => { yield return new CodeAction(context.TranslateString("Split string literal"), script => {
int offset = context.GetOffset (context.Location); int offset = context.GetOffset (context.Location);
script.InsertText (offset, pexpr.LiteralValue.StartsWith ("@") ? "\" + @\"" : "\" + \""); script.InsertText (offset, pexpr.LiteralValue.StartsWith("@", StringComparison.Ordinal) ? "\" + @\"" : "\" + \"");
}); });
} }
} }

10
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/CallToVirtualFunctionFromConstructorIssue.cs

@ -144,6 +144,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return true; return true;
return false; return false;
} }
public override void VisitLambdaExpression(LambdaExpression lambdaExpression)
{
// ignore lambdas
}
public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
{
// ignore anonymous methods
}
} }
} }
} }

22
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/InconsistentNamingIssue/NamingRule.cs

@ -1,4 +1,4 @@
// //
// NamingRule.cs // NamingRule.cs
// //
// Author: // Author:
@ -82,7 +82,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
string id = name; string id = name;
bool foundPrefix = false; bool foundPrefix = false;
if (RequiredPrefixes != null && RequiredPrefixes.Length > 0) { if (RequiredPrefixes != null && RequiredPrefixes.Length > 0) {
var prefix = RequiredPrefixes.FirstOrDefault(p => id.StartsWith(p)); var prefix = RequiredPrefixes.FirstOrDefault(p => id.StartsWith(p, StringComparison.Ordinal));
if (prefix == null) { if (prefix == null) {
return false; return false;
} }
@ -91,7 +91,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
} }
if (!foundPrefix && AllowedPrefixes != null && AllowedPrefixes.Length > 0) { if (!foundPrefix && AllowedPrefixes != null && AllowedPrefixes.Length > 0) {
var prefix = AllowedPrefixes.FirstOrDefault(p => id.StartsWith(p)); var prefix = AllowedPrefixes.FirstOrDefault(p => id.StartsWith(p, StringComparison.Ordinal));
if (prefix != null) { if (prefix != null) {
id = id.Substring(prefix.Length); id = id.Substring(prefix.Length);
foundPrefix = true; foundPrefix = true;
@ -99,19 +99,19 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
} }
if (!foundPrefix && ForbiddenPrefixes != null && ForbiddenPrefixes.Length > 0) { if (!foundPrefix && ForbiddenPrefixes != null && ForbiddenPrefixes.Length > 0) {
if (ForbiddenPrefixes.Any(p => id.StartsWith(p))) { if (ForbiddenPrefixes.Any(p => id.StartsWith(p, StringComparison.Ordinal))) {
return false; return false;
} }
} }
if (RequiredSuffixes != null && RequiredSuffixes.Length > 0) { if (RequiredSuffixes != null && RequiredSuffixes.Length > 0) {
var suffix = RequiredSuffixes.FirstOrDefault(s => id.EndsWith(s)); var suffix = RequiredSuffixes.FirstOrDefault(s => id.EndsWith(s, StringComparison.Ordinal));
if (suffix == null) { if (suffix == null) {
return false; return false;
} }
id = id.Substring(0, id.Length - suffix.Length); id = id.Substring(0, id.Length - suffix.Length);
} else if (ForbiddenSuffixes != null && ForbiddenSuffixes.Length > 0) { } else if (ForbiddenSuffixes != null && ForbiddenSuffixes.Length > 0) {
if (ForbiddenSuffixes.Any(p => id.EndsWith(p))) { if (ForbiddenSuffixes.Any(p => id.EndsWith(p, StringComparison.Ordinal))) {
return false; return false;
} }
} }
@ -166,14 +166,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
string suffix = null; string suffix = null;
if (AllowedPrefixes != null && AllowedPrefixes.Length > 0) { if (AllowedPrefixes != null && AllowedPrefixes.Length > 0) {
allowedPrefix = AllowedPrefixes.FirstOrDefault(p => id.StartsWith(p)); allowedPrefix = AllowedPrefixes.FirstOrDefault(p => id.StartsWith(p, StringComparison.Ordinal));
if (allowedPrefix != null) if (allowedPrefix != null)
id = id.Substring(allowedPrefix.Length); id = id.Substring(allowedPrefix.Length);
} }
if (RequiredPrefixes != null && RequiredPrefixes.Length > 0) { if (RequiredPrefixes != null && RequiredPrefixes.Length > 0) {
requiredPrefix = RequiredPrefixes.FirstOrDefault(p => id.StartsWith(p)); requiredPrefix = RequiredPrefixes.FirstOrDefault(p => id.StartsWith(p, StringComparison.Ordinal));
if (requiredPrefix == null) { if (requiredPrefix == null) {
errorMessage = string.Format(ctx.TranslateString("Name should have prefix '{0}'."), RequiredPrefixes [0]); errorMessage = string.Format(ctx.TranslateString("Name should have prefix '{0}'."), RequiredPrefixes [0]);
missingRequiredPrefix = true; missingRequiredPrefix = true;
@ -181,7 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
id = id.Substring(requiredPrefix.Length); id = id.Substring(requiredPrefix.Length);
} }
} else if (ForbiddenPrefixes != null && ForbiddenPrefixes.Length > 0) { } else if (ForbiddenPrefixes != null && ForbiddenPrefixes.Length > 0) {
requiredPrefix = ForbiddenPrefixes.FirstOrDefault(p => id.StartsWith(p)); requiredPrefix = ForbiddenPrefixes.FirstOrDefault(p => id.StartsWith(p, StringComparison.Ordinal));
if (requiredPrefix != null) { if (requiredPrefix != null) {
errorMessage = string.Format(ctx.TranslateString("Name has forbidden prefix '{0}'."), requiredPrefix); errorMessage = string.Format(ctx.TranslateString("Name has forbidden prefix '{0}'."), requiredPrefix);
id = id.Substring(requiredPrefix.Length); id = id.Substring(requiredPrefix.Length);
@ -189,7 +189,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
} }
if (RequiredSuffixes != null && RequiredSuffixes.Length > 0) { if (RequiredSuffixes != null && RequiredSuffixes.Length > 0) {
suffix = RequiredSuffixes.FirstOrDefault(s => id.EndsWith(s)); suffix = RequiredSuffixes.FirstOrDefault(s => id.EndsWith(s, StringComparison.Ordinal));
if (suffix == null) { if (suffix == null) {
errorMessage = string.Format(ctx.TranslateString("Name should have suffix '{0}'."), RequiredSuffixes [0]); errorMessage = string.Format(ctx.TranslateString("Name should have suffix '{0}'."), RequiredSuffixes [0]);
missingRequiredSuffix = true; missingRequiredSuffix = true;
@ -197,7 +197,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
id = id.Substring(0, id.Length - suffix.Length); id = id.Substring(0, id.Length - suffix.Length);
} }
} else if (ForbiddenSuffixes != null && ForbiddenSuffixes.Length > 0) { } else if (ForbiddenSuffixes != null && ForbiddenSuffixes.Length > 0) {
suffix = ForbiddenSuffixes.FirstOrDefault(p => id.EndsWith(p)); suffix = ForbiddenSuffixes.FirstOrDefault(p => id.EndsWith(p, StringComparison.Ordinal));
if (suffix != null) { if (suffix != null) {
errorMessage = string.Format(ctx.TranslateString("Name has forbidden suffix '{0}'."), suffix); errorMessage = string.Format(ctx.TranslateString("Name has forbidden suffix '{0}'."), suffix);
id = id.Substring(0, id.Length - suffix.Length); id = id.Substring(0, id.Length - suffix.Length);

16
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/NegativeRelationalExpressionIssue.cs

@ -74,14 +74,26 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (negatedOp == BinaryOperatorType.Any) if (negatedOp == BinaryOperatorType.Any)
return; return;
if (IsFloatingPoint (binaryOperatorExpr.Left) || IsFloatingPoint (binaryOperatorExpr.Right)) if (IsFloatingPoint (binaryOperatorExpr.Left) || IsFloatingPoint (binaryOperatorExpr.Right)) {
return; if (negatedOp != BinaryOperatorType.Equality && negatedOp != BinaryOperatorType.InEquality)
return;
}
AddIssue (unaryOperatorExpression, ctx.TranslateString ("Simplify negative relational expression"), AddIssue (unaryOperatorExpression, ctx.TranslateString ("Simplify negative relational expression"),
script => script.Replace (unaryOperatorExpression, script => script.Replace (unaryOperatorExpression,
new BinaryOperatorExpression (binaryOperatorExpr.Left.Clone (), negatedOp, new BinaryOperatorExpression (binaryOperatorExpr.Left.Clone (), negatedOp,
binaryOperatorExpr.Right.Clone ()))); binaryOperatorExpr.Right.Clone ())));
} }
public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
if (operatorDeclaration.OperatorType.IsComparisonOperator()) {
// Ignore operator declaration; within them it's common to define one operator
// by negating another.
return;
}
base.VisitOperatorDeclaration(operatorDeclaration);
}
} }
} }
} }

51
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs

@ -34,10 +34,10 @@ using System;
namespace ICSharpCode.NRefactory.CSharp.Refactoring namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
[IssueDescription ("Unused parameter", [IssueDescription ("Unused parameter",
Description = "Parameter is never used.", Description = "Parameter is never used.",
Category = IssueCategories.Redundancies, Category = IssueCategories.Redundancies,
Severity = Severity.Warning, Severity = Severity.Warning,
IssueMarker = IssueMarker.GrayOut)] IssueMarker = IssueMarker.GrayOut)]
public class ParameterNotUsedIssue : ICodeIssueProvider public class ParameterNotUsedIssue : ICodeIssueProvider
{ {
#region ICodeIssueProvider implementation #region ICodeIssueProvider implementation
@ -63,29 +63,48 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public override void VisitIdentifierExpression(IdentifierExpression identifierExpression) public override void VisitIdentifierExpression(IdentifierExpression identifierExpression)
{ {
var mgr = ctx.Resolve (identifierExpression) as MethodGroupResolveResult; if (!IsTargetOfInvocation(identifierExpression)) {
if (mgr != null) var mgr = ctx.Resolve (identifierExpression) as MethodGroupResolveResult;
UsedMethods.AddRange (mgr.Methods); if (mgr != null)
UsedMethods.AddRange (mgr.Methods);
}
base.VisitIdentifierExpression(identifierExpression); base.VisitIdentifierExpression(identifierExpression);
} }
public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
{ {
var mgr = ctx.Resolve (memberReferenceExpression) as MethodGroupResolveResult; if (!IsTargetOfInvocation(memberReferenceExpression)) {
if (mgr != null) var mgr = ctx.Resolve (memberReferenceExpression) as MethodGroupResolveResult;
UsedMethods.AddRange (mgr.Methods); if (mgr != null)
UsedMethods.AddRange (mgr.Methods);
}
base.VisitMemberReferenceExpression(memberReferenceExpression); base.VisitMemberReferenceExpression(memberReferenceExpression);
} }
static bool IsTargetOfInvocation(AstNode node)
{
return node.Role == Roles.TargetExpression && node.Parent is InvocationExpression;
}
} }
class GatherVisitor : GatherVisitorBase class GatherVisitor : GatherVisitorBase
{ {
GetDelgateUsagesVisitor usedDelegates; GetDelgateUsagesVisitor usedDelegates;
bool currentTypeIsPartial;
public GatherVisitor (BaseRefactoringContext ctx, GetDelgateUsagesVisitor usedDelegates) public GatherVisitor (BaseRefactoringContext ctx, GetDelgateUsagesVisitor usedDelegates)
: base (ctx) : base (ctx)
{ {
this.usedDelegates = usedDelegates; this.usedDelegates = usedDelegates;
} }
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
bool outerTypeIsPartial = currentTypeIsPartial;
currentTypeIsPartial = typeDeclaration.HasModifier(Modifiers.Partial);
base.VisitTypeDeclaration(typeDeclaration);
currentTypeIsPartial = outerTypeIsPartial;
}
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{ {
@ -101,17 +120,23 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return; return;
if (member.ImplementedInterfaceMembers.Any ()) if (member.ImplementedInterfaceMembers.Any ())
return; return;
if (usedDelegates.UsedMethods.Any (m => m.Region.Begin == methodDeclaration.StartLocation)) if (usedDelegates.UsedMethods.Any (m => m.MemberDefinition == member))
return; return;
if (currentTypeIsPartial && methodDeclaration.Parameters.Count == 2) {
if (methodDeclaration.Parameters.First().Name == "sender") {
// Looks like an event handler; the registration might be in the designer part
return;
}
}
foreach (var parameter in methodDeclaration.Parameters) foreach (var parameter in methodDeclaration.Parameters)
parameter.AcceptVisitor (this); parameter.AcceptVisitor (this);
} }
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration)
{ {
base.VisitParameterDeclaration (parameterDeclaration); base.VisitParameterDeclaration (parameterDeclaration);
if (!(parameterDeclaration.Parent is MethodDeclaration)) if (!(parameterDeclaration.Parent is MethodDeclaration || parameterDeclaration.Parent is ConstructorDeclaration))
return; return;
var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult; var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult;

228
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs

@ -99,27 +99,49 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion #endregion
#region ImplicitConversion #region ImplicitConversion
public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType) private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined)
{ {
if (resolveResult == null)
throw new ArgumentNullException("resolveResult");
Conversion c; Conversion c;
if (resolveResult.IsCompileTimeConstant) { if (resolveResult.IsCompileTimeConstant) {
c = ImplicitEnumerationConversion(resolveResult, toType); c = ImplicitEnumerationConversion(resolveResult, toType);
if (c.IsValid) return c; if (c.IsValid) return c;
if (ImplicitConstantExpressionConversion(resolveResult, toType)) if (ImplicitConstantExpressionConversion(resolveResult, toType))
return Conversion.ImplicitConstantExpressionConversion; return Conversion.ImplicitConstantExpressionConversion;
c = StandardImplicitConversion(resolveResult.Type, toType);
if (c != Conversion.None) return c;
if (allowUserDefined) {
c = UserDefinedImplicitConversion(resolveResult, resolveResult.Type, toType);
if (c != Conversion.None) return c;
}
} else {
c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined);
if (c != Conversion.None) return c;
} }
c = ImplicitConversion(resolveResult.Type, toType);
if (c.IsValid) return c;
if (resolveResult.Type.Kind == TypeKind.Dynamic) if (resolveResult.Type.Kind == TypeKind.Dynamic)
return Conversion.ImplicitDynamicConversion; return Conversion.ImplicitDynamicConversion;
c = AnonymousFunctionConversion(resolveResult, toType); c = AnonymousFunctionConversion(resolveResult, toType);
if (c.IsValid) return c; if (c != Conversion.None) return c;
c = MethodGroupConversion(resolveResult, toType); c = MethodGroupConversion(resolveResult, toType);
return c; return c;
} }
private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined)
{
// C# 4.0 spec: §6.1
var c = StandardImplicitConversion(fromType, toType);
if (c == Conversion.None && allowUserDefined) {
c = UserDefinedImplicitConversion(null, fromType, toType);
}
return c;
}
public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType)
{
if (resolveResult == null)
throw new ArgumentNullException("resolveResult");
return ImplicitConversion(resolveResult, toType, allowUserDefined: true);
}
public Conversion ImplicitConversion(IType fromType, IType toType) public Conversion ImplicitConversion(IType fromType, IType toType)
{ {
if (fromType == null) if (fromType == null)
@ -131,12 +153,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Conversion c; Conversion c;
if (implicitConversionCache.TryGetValue(pair, out c)) if (implicitConversionCache.TryGetValue(pair, out c))
return c; return c;
// C# 4.0 spec: §6.1 c = ImplicitConversion(fromType, toType, allowUserDefined: true);
c = StandardImplicitConversion(fromType, toType);
if (!c.IsValid) {
c = UserDefinedImplicitConversion(fromType, toType);
}
implicitConversionCache[pair] = c; implicitConversionCache[pair] = c;
return c; return c;
} }
@ -153,7 +172,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (ImplicitNumericConversion(fromType, toType)) if (ImplicitNumericConversion(fromType, toType))
return Conversion.ImplicitNumericConversion; return Conversion.ImplicitNumericConversion;
Conversion c = ImplicitNullableConversion(fromType, toType); Conversion c = ImplicitNullableConversion(fromType, toType);
if (c.IsValid) if (c != Conversion.None)
return c; return c;
if (NullLiteralConversion(fromType, toType)) if (NullLiteralConversion(fromType, toType))
return Conversion.NullLiteralConversion; return Conversion.NullLiteralConversion;
@ -211,11 +230,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (resolveResult.Type.Kind == TypeKind.Dynamic) if (resolveResult.Type.Kind == TypeKind.Dynamic)
return Conversion.ExplicitDynamicConversion; return Conversion.ExplicitDynamicConversion;
Conversion c = ImplicitConversion(resolveResult, toType); Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false);
if (c.IsValid) if (c != Conversion.None)
return c; return c;
else c = ExplicitConversionImpl(resolveResult.Type, toType);
return ExplicitConversionImpl(resolveResult.Type, toType); if (c != Conversion.None)
return c;
return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType);
} }
public Conversion ExplicitConversion(IType fromType, IType toType) public Conversion ExplicitConversion(IType fromType, IType toType)
@ -225,11 +246,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (toType == null) if (toType == null)
throw new ArgumentNullException("toType"); throw new ArgumentNullException("toType");
Conversion c = ImplicitConversion(fromType, toType); Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false);
if (c.IsValid) if (c != Conversion.None)
return c; return c;
else c = ExplicitConversionImpl(fromType, toType);
return ExplicitConversionImpl(fromType, toType); if (c != Conversion.None)
return c;
return UserDefinedExplicitConversion(null, fromType, toType);
} }
Conversion ExplicitConversionImpl(IType fromType, IType toType) Conversion ExplicitConversionImpl(IType fromType, IType toType)
@ -241,18 +264,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (ExplicitEnumerationConversion(fromType, toType)) if (ExplicitEnumerationConversion(fromType, toType))
return Conversion.EnumerationConversion(false, false); return Conversion.EnumerationConversion(false, false);
Conversion c = ExplicitNullableConversion(fromType, toType); Conversion c = ExplicitNullableConversion(fromType, toType);
if (c.IsValid) if (c != Conversion.None)
return c; return c;
if (ExplicitReferenceConversion(fromType, toType)) if (ExplicitReferenceConversion(fromType, toType))
return Conversion.ExplicitReferenceConversion; return Conversion.ExplicitReferenceConversion;
if (UnboxingConversion(fromType, toType)) if (UnboxingConversion(fromType, toType))
return Conversion.UnboxingConversion; return Conversion.UnboxingConversion;
c = ExplicitTypeParameterConversion(fromType, toType); c = ExplicitTypeParameterConversion(fromType, toType);
if (c.IsValid) if (c != Conversion.None)
return c; return c;
if (ExplicitPointerConversion(fromType, toType)) if (ExplicitPointerConversion(fromType, toType))
return Conversion.ExplicitPointerConversion; return Conversion.ExplicitPointerConversion;
return UserDefinedExplicitConversion(fromType, toType); return Conversion.None;
} }
#endregion #endregion
@ -643,8 +666,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Implicit Constant-Expression Conversion #region Implicit Constant-Expression Conversion
bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
{ {
if (rr == null || !rr.IsCompileTimeConstant)
return false;
// C# 4.0 spec: §6.1.9 // C# 4.0 spec: §6.1.9
Debug.Assert(rr.IsCompileTimeConstant);
TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType)); TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType));
if (fromTypeCode == TypeCode.Int64) { if (fromTypeCode == TypeCode.Int64) {
@ -744,27 +768,138 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface
&& (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid); && (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid);
} }
Conversion UserDefinedImplicitConversion(IType fromType, IType toType) IType FindMostEncompassedType(IEnumerable<IType> candidates)
{
IType best = null;
foreach (var current in candidates) {
if (best == null || IsEncompassedBy(current, best))
best = current;
else if (!IsEncompassedBy(best, current))
return null; // Ambiguous
}
return best;
}
IType FindMostEncompassingType(IEnumerable<IType> candidates)
{
IType best = null;
foreach (var current in candidates) {
if (best == null || IsEncompassedBy(best, current))
best = current;
else if (!IsEncompassedBy(current, best))
return null; // Ambiguous
}
return best;
}
Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList<OperatorInfo> operators, bool isImplicit)
{
var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList();
if (selected.Count == 0)
return Conversion.None;
if (selected.Count == 1)
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit);
int nNonLifted = selected.Count(s => !s.IsLifted);
if (nNonLifted == 1) {
var op = selected.First(s => !s.IsLifted);
return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit);
}
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true);
}
Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
{ {
// C# 4.0 spec §6.4.4 User-defined implicit conversions // C# 4.0 spec §6.4.4 User-defined implicit conversions
var operators = GetApplicableConversionOperators(fromType, toType, false); var operators = GetApplicableConversionOperators(fromResult, fromType, toType, false);
// TODO: Find most specific conversion
if (operators.Count > 0) if (operators.Count > 0) {
return Conversion.UserDefinedImplicitConversion(operators[0].Method, operators[0].IsLifted); var mostSpecificSource = operators.Any(op => op.SourceType.Equals(fromType)) ? fromType : FindMostEncompassedType(operators.Select(op => op.SourceType));
else if (mostSpecificSource == null)
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true);
var mostSpecificTarget = operators.Any(op => op.TargetType.Equals(toType)) ? toType : FindMostEncompassingType(operators.Select(op => op.TargetType));
if (mostSpecificTarget == null) {
if (NullableType.IsNullable(toType))
return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
else
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true);
}
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, true);
if (selected != Conversion.None) {
if (selected.IsLifted && NullableType.IsNullable(toType)) {
// Prefer A -> B -> B? over A -> A? -> B?
var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
if (other != Conversion.None)
return other;
}
return selected;
}
else if (NullableType.IsNullable(toType))
return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
else
return Conversion.None;
}
else {
return Conversion.None; return Conversion.None;
}
} }
Conversion UserDefinedExplicitConversion(IType fromType, IType toType) Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
{ {
// C# 4.0 spec §6.4.5 User-defined implicit conversions // C# 4.0 spec §6.4.5 User-defined implicit conversions
var operators = GetApplicableConversionOperators(fromType, toType, true); var operators = GetApplicableConversionOperators(fromResult, fromType, toType, true);
// TODO: Find most specific conversion if (operators.Count > 0) {
if (operators.Count > 0) IType mostSpecificSource;
return Conversion.UserDefinedExplicitConversion(operators[0].Method, operators[0].IsLifted); if (operators.Any(op => op.SourceType.Equals(fromType))) {
else mostSpecificSource = fromType;
} else {
var operatorsWithSourceEncompassingFromType = operators.Where(op => IsEncompassedBy(fromType, op.SourceType) || ImplicitConstantExpressionConversion(fromResult, NullableType.GetUnderlyingType(op.SourceType))).ToList();
if (operatorsWithSourceEncompassingFromType.Any())
mostSpecificSource = FindMostEncompassedType(operatorsWithSourceEncompassingFromType.Select(op => op.SourceType));
else
mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType));
}
if (mostSpecificSource == null)
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true);
IType mostSpecificTarget;
if (operators.Any(op => op.TargetType.Equals(toType)))
mostSpecificTarget = toType;
else if (operators.Any(op => IsEncompassedBy(op.TargetType, toType)))
mostSpecificTarget = FindMostEncompassingType(operators.Where(op => IsEncompassedBy(op.TargetType, toType)).Select(op => op.TargetType));
else
mostSpecificTarget = FindMostEncompassedType(operators.Select(op => op.TargetType));
if (mostSpecificTarget == null) {
if (NullableType.IsNullable(toType))
return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
else
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true);
}
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, false);
if (selected != Conversion.None) {
if (selected.IsLifted && NullableType.IsNullable(toType)) {
// Prefer A -> B -> B? over A -> A? -> B?
var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
if (other != Conversion.None)
return other;
}
return selected;
}
else if (NullableType.IsNullable(toType))
return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
else if (NullableType.IsNullable(fromType))
return UserDefinedExplicitConversion(null, NullableType.GetUnderlyingType(fromType), toType); // A? -> A -> B
else
return Conversion.None;
}
else {
return Conversion.None; return Conversion.None;
}
} }
class OperatorInfo class OperatorInfo
@ -783,7 +918,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
List<OperatorInfo> GetApplicableConversionOperators(IType fromType, IType toType, bool isExplicit) List<OperatorInfo> GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit)
{ {
// Find the candidate operators: // Find the candidate operators:
Predicate<IUnresolvedMethod> opFilter; Predicate<IUnresolvedMethod> opFilter;
@ -802,26 +937,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// Try if the operator is applicable: // Try if the operator is applicable:
bool isApplicable; bool isApplicable;
if (isExplicit) { if (isExplicit) {
isApplicable = IsEncompassingOrEncompassedBy(fromType, sourceType) isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
&& IsEncompassingOrEncompassedBy(targetType, toType); && IsEncompassingOrEncompassedBy(targetType, toType);
} else { } else {
isApplicable = IsEncompassedBy(fromType, sourceType) && IsEncompassedBy(targetType, toType); isApplicable = (IsEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
&& IsEncompassedBy(targetType, toType);
} }
// Try if the operator is applicable in lifted form:
if (isApplicable) { if (isApplicable) {
result.Add(new OperatorInfo(op, sourceType, targetType, false)); result.Add(new OperatorInfo(op, sourceType, targetType, false));
} }
// Try if the operator is applicable in lifted form: if (NullableType.IsNonNullableValueType(sourceType)) {
if (NullableType.IsNonNullableValueType(sourceType) // An operator can be applicable in both lifted and non-lifted form in case of explicit conversions
&& NullableType.IsNonNullableValueType(targetType))
{
IType liftedSourceType = NullableType.Create(compilation, sourceType); IType liftedSourceType = NullableType.Create(compilation, sourceType);
IType liftedTargetType = NullableType.Create(compilation, targetType); IType liftedTargetType = NullableType.IsNonNullableValueType(targetType) ? NullableType.Create(compilation, targetType) : targetType;
if (isExplicit) { if (isExplicit) {
isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType) isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType)
&& IsEncompassingOrEncompassedBy(liftedTargetType, toType); && IsEncompassingOrEncompassedBy(liftedTargetType, toType);
} else { } else {
isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType); isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType);
} }
if (isApplicable) { if (isApplicable) {
result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true)); result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true));
} }

5
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs

@ -57,9 +57,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
bool isExpandedForm = false, bool isExpandedForm = false,
bool isDelegateInvocation = false, bool isDelegateInvocation = false,
IList<int> argumentToParameterMap = null, IList<int> argumentToParameterMap = null,
IList<ResolveResult> initializerStatements = null IList<ResolveResult> initializerStatements = null,
IType returnTypeOverride = null
) )
: base(targetResult, member, arguments, initializerStatements) : base(targetResult, member, arguments, initializerStatements, returnTypeOverride)
{ {
this.OverloadResolutionErrors = overloadResolutionErrors; this.OverloadResolutionErrors = overloadResolutionErrors;
this.IsExtensionMethodInvocation = isExtensionMethodInvocation; this.IsExtensionMethodInvocation = isExtensionMethodInvocation;

27
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs

@ -1188,18 +1188,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
static bool IsComparisonOperator(IMethod m) static bool IsComparisonOperator(IMethod m)
{ {
var type = OperatorDeclaration.GetOperatorType(m.Name); var type = OperatorDeclaration.GetOperatorType(m.Name);
if (type.HasValue) { return type.HasValue && type.Value.IsComparisonOperator();
switch (type.Value) {
case OperatorType.Equality:
case OperatorType.Inequality:
case OperatorType.GreaterThan:
case OperatorType.LessThan:
case OperatorType.GreaterThanOrEqual:
case OperatorType.LessThanOrEqual:
return true;
}
}
return false;
} }
sealed class LiftedUserDefinedOperator : SpecializedMethod, OverloadResolution.ILiftedOperator sealed class LiftedUserDefinedOperator : SpecializedMethod, OverloadResolution.ILiftedOperator
@ -1944,9 +1933,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames));
} }
bool isDynamic = arguments.Any(a => a.Type.Kind == TypeKind.Dynamic);
MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; MethodGroupResolveResult mgrr = target as MethodGroupResolveResult;
if (mgrr != null) { if (mgrr != null) {
if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { if (isDynamic) {
// If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method.
var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray()); var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray());
var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList(); var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList();
@ -1971,9 +1961,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters); OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters);
if (or.BestCandidate != null) { if (or.BestCandidate != null) {
if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult))
return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType)); return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
else else
return or.CreateResolveResult(mgrr.TargetResult); return or.CreateResolveResult(mgrr.TargetResult, returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
} else { } else {
// No candidate found at all (not even an inapplicable one). // No candidate found at all (not even an inapplicable one).
// This can happen with empty method groups (as sometimes used with extension methods) // This can happen with empty method groups (as sometimes used with extension methods)
@ -1998,7 +1988,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors, or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors,
isExpandedForm: or.BestCandidateIsExpandedForm, isExpandedForm: or.BestCandidateIsExpandedForm,
isDelegateInvocation: true, isDelegateInvocation: true,
argumentToParameterMap: or.GetArgumentToParameterMap()); argumentToParameterMap: or.GetArgumentToParameterMap(),
returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
} }
return ErrorResult; return ErrorResult;
} }
@ -2320,7 +2311,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (!c.IsValid) { if (!c.IsValid) {
var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault(); var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault();
if (opTrue != null) { if (opTrue != null) {
c = Conversion.UserDefinedImplicitConversion(opTrue, false); c = Conversion.UserDefinedConversion(opTrue, isImplicit: true);
} }
} }
return Convert(input, boolean, c); return Convert(input, boolean, c);
@ -2340,7 +2331,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (!c.IsValid) { if (!c.IsValid) {
var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault(); var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault();
if (opFalse != null) { if (opFalse != null) {
c = Conversion.UserDefinedImplicitConversion(opFalse, false); c = Conversion.UserDefinedConversion(opFalse, isImplicit: true);
return Convert(input, boolean, c); return Convert(input, boolean, c);
} }
} }

51
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs

@ -101,16 +101,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
this.Member = member; this.Member = member;
this.IsExpandedForm = isExpanded; this.IsExpandedForm = isExpanded;
IMethod method = member as IMethod; IParameterizedMember memberDefinition = (IParameterizedMember)member.MemberDefinition;
if (method != null && method.TypeParameters.Count > 0) { // For specificialized methods, go back to the original parameters:
// For generic methods, go back to the original parameters // (without any type parameter substitution, not even class type parameters)
// (without any type parameter substitution, not even class type parameters) // We'll re-substitute them as part of RunTypeInference().
// We'll re-substitute them as part of RunTypeInference(). this.Parameters = memberDefinition.Parameters;
method = (IMethod)method.MemberDefinition; IMethod methodDefinition = memberDefinition as IMethod;
this.Parameters = method.Parameters; if (methodDefinition != null && methodDefinition.TypeParameters.Count > 0) {
this.TypeParameters = method.TypeParameters; this.TypeParameters = methodDefinition.TypeParameters;
} else {
this.Parameters = member.Parameters;
} }
this.ParameterTypes = new IType[this.Parameters.Count]; this.ParameterTypes = new IType[this.Parameters.Count];
} }
@ -251,7 +249,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <returns>True if the calculation was successful, false if the candidate should be removed without reporting an error</returns> /// <returns>True if the calculation was successful, false if the candidate should be removed without reporting an error</returns>
bool CalculateCandidate(Candidate candidate) bool CalculateCandidate(Candidate candidate)
{ {
if (!ResolveParameterTypes(candidate)) if (!ResolveParameterTypes(candidate, false))
return false; return false;
MapCorrespondingParameters(candidate); MapCorrespondingParameters(candidate);
RunTypeInference(candidate); RunTypeInference(candidate);
@ -260,10 +258,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return true; return true;
} }
bool ResolveParameterTypes(Candidate candidate) bool ResolveParameterTypes(Candidate candidate, bool useSpecializedParameters)
{ {
for (int i = 0; i < candidate.Parameters.Count; i++) { for (int i = 0; i < candidate.Parameters.Count; i++) {
IType type = candidate.Parameters[i].Type; IType type;
if (useSpecializedParameters) {
// Use the parameter type of the specialized non-generic method or indexer
Debug.Assert(!candidate.IsGenericMethod);
type = candidate.Member.Parameters[i].Type;
} else {
// Use the type of the original formal parameter
type = candidate.Parameters[i].Type;
}
if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) { if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) {
ArrayType arrayType = type as ArrayType; ArrayType arrayType = type as ArrayType;
if (arrayType != null && arrayType.Dimensions == 1) if (arrayType != null && arrayType.Dimensions == 1)
@ -372,12 +378,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region RunTypeInference #region RunTypeInference
void RunTypeInference(Candidate candidate) void RunTypeInference(Candidate candidate)
{ {
IMethod method = candidate.Member as IMethod; if (candidate.TypeParameters == null) {
if (method == null || method.TypeParameters.Count == 0) {
if (explicitlyGivenTypeArguments != null) { if (explicitlyGivenTypeArguments != null) {
// method does not expect type arguments, but was given some // method does not expect type arguments, but was given some
candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments); candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments);
} }
// Grab new parameter types:
ResolveParameterTypes(candidate, true);
return; return;
} }
ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType; ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType;
@ -389,12 +396,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
// The method is generic: // The method is generic:
if (explicitlyGivenTypeArguments != null) { if (explicitlyGivenTypeArguments != null) {
if (explicitlyGivenTypeArguments.Length == method.TypeParameters.Count) { if (explicitlyGivenTypeArguments.Length == candidate.TypeParameters.Count) {
candidate.InferredTypes = explicitlyGivenTypeArguments; candidate.InferredTypes = explicitlyGivenTypeArguments;
} else { } else {
candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments); candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments);
// wrong number of type arguments given, so truncate the list or pad with UnknownType // wrong number of type arguments given, so truncate the list or pad with UnknownType
candidate.InferredTypes = new IType[method.TypeParameters.Count]; candidate.InferredTypes = new IType[candidate.TypeParameters.Count];
for (int i = 0; i < candidate.InferredTypes.Length; i++) { for (int i = 0; i < candidate.InferredTypes.Length; i++) {
if (i < explicitlyGivenTypeArguments.Length) if (i < explicitlyGivenTypeArguments.Length)
candidate.InferredTypes[i] = explicitlyGivenTypeArguments[i]; candidate.InferredTypes[i] = explicitlyGivenTypeArguments[i];
@ -585,7 +592,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion)) if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion))
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
} else { } else {
if (!c.IsValid && parameterType.Kind != TypeKind.Unknown) if ((!c.IsValid && !c.IsUserDefined && !c.IsMethodGroupConversion) && parameterType.Kind != TypeKind.Unknown)
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
} }
} }
@ -933,8 +940,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <param name="initializerStatements"> /// <param name="initializerStatements">
/// Statements for Objects/Collections initializer. /// Statements for Objects/Collections initializer.
/// <see cref="InvocationResolveResult.InitializerStatements"/> /// <see cref="InvocationResolveResult.InitializerStatements"/>
/// <param name="returnTypeOverride">
/// If not null, use this instead of the ReturnType of the member as the type of the created resolve result.
/// </param>
/// </param> /// </param>
public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList<ResolveResult> initializerStatements = null) public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList<ResolveResult> initializerStatements = null, IType returnTypeOverride = null)
{ {
IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments(); IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments();
if (member == null) if (member == null)
@ -949,7 +959,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.BestCandidateIsExpandedForm, this.BestCandidateIsExpandedForm,
isDelegateInvocation: false, isDelegateInvocation: false,
argumentToParameterMap: this.GetArgumentToParameterMap(), argumentToParameterMap: this.GetArgumentToParameterMap(),
initializerStatements: initializerStatements); initializerStatements: initializerStatements,
returnTypeOverride: returnTypeOverride);
} }
#endregion #endregion
} }

19
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs

@ -624,6 +624,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
tp.LowerBounds.Add(U); tp.LowerBounds.Add(U);
return; return;
} }
// Handle nullable covariance:
if (NullableType.IsNullable(U) && NullableType.IsNullable(V)) {
MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V));
return;
}
// Handle array types: // Handle array types:
ArrayType arrU = U as ArrayType; ArrayType arrU = U as ArrayType;
@ -857,10 +862,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
.Where(c => lowerBounds.All(b => conversions.ImplicitConversion(b, c).IsValid)) .Where(c => lowerBounds.All(b => conversions.ImplicitConversion(b, c).IsValid))
.Where(c => upperBounds.All(b => conversions.ImplicitConversion(c, b).IsValid)) .Where(c => upperBounds.All(b => conversions.ImplicitConversion(c, b).IsValid))
.ToList(); // evaluate the query only once .ToList(); // evaluate the query only once
Log.WriteCollection("FindTypesInBound, Candidates=", candidateTypes);
// According to the C# specification, we need to pick the most specific
// of the candidate types. (the type which has conversions to all others)
// However, csc actually seems to choose the least specific.
candidateTypes = candidateTypes.Where( candidateTypes = candidateTypes.Where(
c => candidateTypes.All(o => conversions.ImplicitConversion(c, o).IsValid) c => candidateTypes.All(o => conversions.ImplicitConversion(o, c).IsValid)
).ToList(); ).ToList();
// If the specified algorithm produces a single candidate, we return // If the specified algorithm produces a single candidate, we return
// that candidate. // that candidate.
// We also return the whole candidate list if we're not using the improved // We also return the whole candidate list if we're not using the improved
@ -916,8 +927,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
Log.WriteLine("Candidate type: " + candidate); Log.WriteLine("Candidate type: " + candidate);
if (lowerBounds.Count > 0) { if (upperBounds.Count == 0) {
// if there were lower bounds, we aim for the most specific candidate: // if there were only lower bounds, we aim for the most specific candidate:
// if this candidate isn't made redundant by an existing, more specific candidate: // if this candidate isn't made redundant by an existing, more specific candidate:
if (!candidateTypes.Any(c => c.GetDefinition().IsDerivedFrom(candidateDef))) { if (!candidateTypes.Any(c => c.GetDefinition().IsDerivedFrom(candidateDef))) {
@ -927,7 +938,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
candidateTypes.Add(candidate); candidateTypes.Add(candidate);
} }
} else { } else {
// if there only were upper bounds, we aim for the least specific candidate: // if there were upper bounds, we aim for the least specific candidate:
// if this candidate isn't made redundant by an existing, less specific candidate: // if this candidate isn't made redundant by an existing, less specific candidate:
if (!candidateTypes.Any(c => candidateDef.IsDerivedFrom(c.GetDefinition()))) { if (!candidateTypes.Any(c => candidateDef.IsDerivedFrom(c.GetDefinition()))) {

37
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertToInitializer/ConvertToInitializerTests.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
{ {
// TODO: Remove this when the formatter handles object and collection initializers // TODO: Remove this when the formatter handles object and collection initializers
// This tests the expected code vs the actual code based on their ASTs instead of the text they produce. // This tests the expected code vs the actual code based on their ASTs instead of the text they produce.
public new void Test<T>(string input, string output, int action = 0, bool expectErrors = false) public new void Test<T>(string input, string output, int action = 0, bool expectErrors = false)
where T : ICodeActionProvider, new () where T : ICodeActionProvider, new ()
{ {
string result = RunContextAction(new T(), HomogenizeEol(input), action, expectErrors); string result = RunContextAction(new T(), HomogenizeEol(input), action, expectErrors);
@ -664,6 +664,41 @@ class TestClass
} }
}"); }");
} }
[Test]
public void DoesNotCrashOnVariableDeclarationWithoutInitializer()
{
TestWrongContext<ConvertToInitializerAction>(@"
class TestClass
{
void F()
{
TestClass $s1 = new TestClass();
TestClass s2;
}
}");
}
[Test]
public void DoesNotCrashOnDelegateAssignment()
{
TestWrongContext<ConvertToInitializerAction>(@"
using System;
using System.Collections.Generic;
class TestClass {
void F() {
((CheckBox)ControlDictionary[""ReplaceCheckBox""]).CheckedChanged = new E$ventHandler(ReplaceCheckBox_CheckedChanged);
}
Dictionary<string, Control> ControlDictionary;
void ReplaceCheckBox_CheckedChanged(object sender, EventArgs e) {}
}
class Control {}
class CheckBox : Control {
public EventHandler CheckedChanged;
}
");
}
} }
} }

27
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionAccessibleTests.cs

@ -1517,7 +1517,7 @@ class Test
Assert.IsNull (provider.Find ("TestMethod"), "'TestMethod' found."); Assert.IsNull (provider.Find ("TestMethod"), "'TestMethod' found.");
}); });
} }
[Test] [Test]
public void TestVariableHiding () public void TestVariableHiding ()
{ {
@ -1537,5 +1537,30 @@ class Test
Assert.AreEqual (1, provider.Data.Count (p => p.DisplayText == "test")); Assert.AreEqual (1, provider.Data.Count (p => p.DisplayText == "test"));
}); });
} }
[Test]
public void TestOverloadCount ()
{
CodeCompletionBugTests.CombinedProviderTest (@"
using System;
class Test
{
static void Foo () {}
static void Foo (int i) {}
static void Foo (int i, string s) {}
public static void Main (int test)
{
$f$
}
}
", provider => {
Assert.AreEqual (1, provider.Data.Count (p => p.DisplayText == "Foo"));
var data = provider.Find ("Foo");
Assert.AreEqual (3, data.OverloadedData.Count ());
});
}
} }
} }

25
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs

@ -138,6 +138,27 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion
} }
} }
public class EntityCompletionData : CompletionData, IEntityCompletionData
{
#region IEntityCompletionData implementation
public IEntity Entity {
get;
private set;
}
#endregion
public EntityCompletionData(IEntity entity) : this(entity, entity.Name)
{
}
public EntityCompletionData(IEntity entity, string txt) : base(txt)
{
this.Entity = entity;
}
}
public class ImportCompletionData : CompletionData public class ImportCompletionData : CompletionData
{ {
public IType Type { public IType Type {
@ -160,7 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion
#region ICompletionDataFactory implementation #region ICompletionDataFactory implementation
public ICompletionData CreateEntityCompletionData (ICSharpCode.NRefactory.TypeSystem.IEntity entity) public ICompletionData CreateEntityCompletionData (ICSharpCode.NRefactory.TypeSystem.IEntity entity)
{ {
return new CompletionData (entity.Name); return new EntityCompletionData (entity);
} }
public ICompletionData CreateEntityCompletionData (ICSharpCode.NRefactory.TypeSystem.IEntity entity, string text) public ICompletionData CreateEntityCompletionData (ICSharpCode.NRefactory.TypeSystem.IEntity entity, string text)
@ -185,7 +206,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion
public ICompletionData CreateMemberCompletionData(IType type, IEntity member) public ICompletionData CreateMemberCompletionData(IType type, IEntity member)
{ {
string name = builder.ConvertType(type).GetText(); string name = builder.ConvertType(type).GetText();
return new CompletionData (name + "."+ member.Name); return new EntityCompletionData (member, name + "."+ member.Name);
} }

79
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs

@ -262,13 +262,13 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion
}; };
} }
public IParameterDataProvider CreateIndexerParameterDataProvider(int startOffset, IType type, AstNode resolvedNode) public IParameterDataProvider CreateIndexerParameterDataProvider(int startOffset, IType type, IEnumerable<IProperty> accessibleIndexers, AstNode resolvedNode)
{ {
Assert.IsTrue(type.Kind != TypeKind.Unknown); Assert.IsTrue(type.Kind != TypeKind.Unknown);
if (type.Kind == TypeKind.Array) if (type.Kind == TypeKind.Array)
return new ArrayProvider (); return new ArrayProvider ();
return new IndexerProvider () { return new IndexerProvider () {
Data = type.GetProperties (p => p.IsIndexer) Data = accessibleIndexers
}; };
} }
@ -1040,7 +1040,7 @@ class TestClass
Assert.IsTrue (provider == null || provider.Count == 0); Assert.IsTrue (provider == null || provider.Count == 0);
} }
[Test()] [Test]
public void TestJaggedArrayCreationCase2() public void TestJaggedArrayCreationCase2()
{ {
IParameterDataProvider provider = CreateProvider( IParameterDataProvider provider = CreateProvider(
@ -1057,5 +1057,78 @@ class TestClass
Assert.IsTrue (provider == null || provider.Count == 0); Assert.IsTrue (provider == null || provider.Count == 0);
} }
/// <summary>
/// Bug 9301 - Inaccessible indexer overload in completion
/// </summary>
[Test]
public void TestBug9301()
{
IParameterDataProvider provider = CreateProvider(
@"using System;
public class A
{
public virtual int this [int i, string s] {
get {
return 1;
}
}
}
public class B : A
{
public new bool this [int i, string s2] {
get {
return true;
}
}
}
public class Test
{
public static int Main ()
{
B p = new B ();
$p[$
return 0;
}
}
");
Assert.AreEqual (1, provider.Count);
}
[Test]
public void TestBug9301Case2()
{
IParameterDataProvider provider = CreateProvider(
@"using System;
public class A
{
public virtual int Test (int i, string s) {
return 1;
}
}
public class B : A
{
public new bool Test (int i, string s2) {
return true;
}
}
public class Test
{
public static int Main ()
{
B p = new B ();
$p.Test($
return 0;
}
}
");
Assert.AreEqual (1, provider.Count);
}
} }
} }

19
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/CallToVirtualFunctionFromConstructorTests.cs

@ -105,6 +105,25 @@ namespace ICSharpCode.NRefactory.CSharp.CodeIssues
f.Bar(); f.Bar();
} }
virtual void Bar ()
{
}
}";
TestRefactoringContext context;
var issues = GetIssues(new CallToVirtualFunctionFromConstructorIssue(), input, out context);
Assert.AreEqual(0, issues.Count);
}
[Test]
public void IgnoresEventHandlers()
{
var input = @"class Foo
{
Foo()
{
SomeEvent += delegate { Bar(); };
}
virtual void Bar () virtual void Bar ()
{ {
} }

33
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/NegativeRelationalExpressionIssueTests.cs

@ -100,6 +100,39 @@ class TestClass
{ {
var x = !(d > 0.1); var x = !(d > 0.1);
} }
}";
Test<NegativeRelationalExpressionIssue> (input, 0);
}
[Test]
public void TestFloatingPointEquality ()
{
var input = @"
class TestClass
{
void TestMethod (double d)
{
var x = !(d == 0.1);
}
}";
Test<NegativeRelationalExpressionIssue> (input, 1);
}
[Test]
public void TestUserDefinedOperator ()
{
var input = @"
struct LineChangeInfo
{
public static bool operator ==(LineChangeInfo lhs, LineChangeInfo rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(LineChangeInfo lhs, LineChangeInfo rhs)
{
return !(lhs == rhs);
}
}"; }";
Test<NegativeRelationalExpressionIssue> (input, 0); Test<NegativeRelationalExpressionIssue> (input, 0);
} }

48
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterNotUsedIssueTests.cs

@ -44,6 +44,19 @@ class TestClass {
Test<ParameterNotUsedIssue> (input, 1); Test<ParameterNotUsedIssue> (input, 1);
} }
[Test]
public void TestUnusedParameterMethodGetsCalled ()
{
var input = @"
class TestClass {
void TestMethod (int i)
{
TestMethod(0);
}
}";
Test<ParameterNotUsedIssue> (input, 1);
}
[Test] [Test]
public void TestInterfaceImplementation () public void TestInterfaceImplementation ()
{ {
@ -130,5 +143,40 @@ class TestClass {
}"; }";
Test<ParameterNotUsedIssue> (input, 0); Test<ParameterNotUsedIssue> (input, 0);
} }
[Test]
public void TestMethodLooksLikeEventHandlerButNotUsedAsSuch ()
{
var input = @"using System;
class TestClass {
void FooBar (object sender, EventArgs e) {}
}";
Test<ParameterNotUsedIssue> (input, 2);
}
[Test]
public void TestMethodUsedAsDelegateInOtherPart ()
{
// This test doesn't add the second part;
// but the issue doesn't look at other files after all;
// we just rely on heuristics if the class is partial
var input = @"using System;
partial class TestClass {
void FooBar (object sender, EventArgs e) {}
}";
Test<ParameterNotUsedIssue> (input, 0);
}
[Test]
public void UnusedParameterInConstructor()
{
var input = @"
class TestClass {
public TestClass(int i)
{
}
}";
Test<ParameterNotUsedIssue> (input, 1);
}
} }
} }

420
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs

@ -831,5 +831,425 @@ class Test {
Assert.IsTrue(c.IsMethodGroupConversion); Assert.IsTrue(c.IsMethodGroupConversion);
Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName);
} }
[Test]
public void UserDefined_IntLiteral_ViaUInt_ToCustomStruct()
{
string program = @"using System;
struct T {
public static implicit operator T(uint a) { return new T(); }
}
class Test {
static void M() {
T t = $1$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
}
[Test]
public void UserDefined_NullLiteral_ViaString_ToCustomStruct()
{
string program = @"using System;
struct T {
public static implicit operator T(string a) { return new T(); }
}
class Test {
static void M() {
T t = $null$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
}
[Test]
public void UserDefined_CanUseLiftedEvenIfReturnTypeAlreadyNullable()
{
string program = @"using System;
struct S {
public static implicit operator short?(S s) { return 0; }
}
class Test {
static void M(S? s) {
int? i = $s$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.IsTrue(c.IsLifted);
}
[Test]
public void UserDefinedImplicitConversion_PicksExactSourceTypeIfPossible() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(int i) {return new Convertible(); }
public static implicit operator Convertible(short s) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("i", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_PicksMostEncompassedSourceType() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(long l) {return new Convertible(); }
public static implicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $(ushort)33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("ui", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_NoMostEncompassedSourceTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(ulong l) {return new Convertible(); }
public static implicit operator Convertible(int ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $(ushort)33$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_PicksExactTargetTypeIfPossible() {
string program = @"using System;
class Convertible {
public static implicit operator int(Convertible i) {return 0; }
public static implicit operator short(Convertible s) {return 0; }
}
class Test {
public void M() {
int a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("i", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_PicksMostEncompassingTargetType() {
string program = @"using System;
class Convertible {
public static implicit operator int(Convertible i) {return 0; }
public static implicit operator ushort(Convertible us) {return 0; }
}
class Test {
public void M() {
ulong a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("us", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_NoMostEncompassingTargetTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static implicit operator uint(Convertible i) {return 0; }
public static implicit operator short(Convertible us) {return 0; }
}
class Test {
public void M() {
long a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_AmbiguousIsInvalid() {
string program = @"using System;
class Convertible1 {
public static implicit operator Convertible2(Convertible1 c) {return 0; }
}
class Convertible2 {
public static implicit operator Convertible2(Convertible1 c) {return 0; }
}
class Test {
public void M() {
Convertible2 a = $new Convertible1()$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_DefinedNullableTakesPrecedenceOverLifted() {
string program = @"using System;
struct Convertible {
public static implicit operator Convertible(int i) {return new Convertible(); }
public static implicit operator Convertible?(int? ni) {return new Convertible(); }
}
class Test {
public void M() {
Convertible? a = $(int?)33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.IsFalse(c.IsLifted);
Assert.AreEqual("ni", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_UIntConstant() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(long l) {return new Convertible(); }
public static implicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("ui", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_NullableUIntConstant() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(long? l) {return new Convertible(); }
public static implicit operator Convertible(uint? ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("ui", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_UseShortResult_BecauseNullableCannotBeUnpacked()
{
string program = @"using System;
class Test {
public static implicit operator int?(Test i) { return 0; }
public static implicit operator short(Test s) { return 0; }
}
class Program {
public static void Main(string[] args)
{
int x = $new Test()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("System.Int16", c.Method.ReturnType.FullName);
}
[Test]
public void UserDefinedImplicitConversion_Short_Or_NullableByte_Target()
{
string program = @"using System;
class Test {
public static implicit operator short(Test s) { return 0; }
public static implicit operator byte?(Test b) { return 0; }
}
class Program {
public static void Main(string[] args)
{
int? x = $new Test()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("System.Int16", c.Method.ReturnType.FullName);
}
[Test]
public void UserDefinedImplicitConversion_Byte_Or_NullableShort_Target()
{
string program = @"using System;
class Test {
public static implicit operator byte(Test b) { return 0; }
public static implicit operator short?(Test s) { return 0; }
}
class Program {
public static void Main(string[] args)
{
int? x = $new Test()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("s", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_Int_Or_NullableLong_Source()
{
string program = @"using System;
class Test {
public static implicit operator Test(int i) { return new Test(); }
public static implicit operator Test(long? l) { return new Test(); }
}
class Program {
static void Main() {
short s = 0;
Test t = $s$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("i", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_NullableInt_Or_Long_Source()
{
string program = @"using System;
class Test {
public static implicit operator Test(int? i) { return new Test(); }
public static implicit operator Test(long l) { return new Test(); }
}
class Program {
static void Main() {
short s = 0;
Test t = $s$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
}
[Test]
public void UserDefinedImplicitConversion_NullableInt_Or_Long_Constant_Source() {
string program = @"using System;
class Test {
public static implicit operator Test(int? i) { return new Test(); }
public static implicit operator Test(long l) { return new Test(); }
}
class Program {
static void Main() {
Test t = $1$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
}
[Test]
public void UserDefinedImplicitConversion_NullableInt_Or_NullableLong_Source()
{
string program = @"using System;
class Test {
public static implicit operator Test(int? i) { return new Test(); }
public static implicit operator Test(long? l) { return new Test(); }
}
class Program {
static void Main() {
short s = 0;
Test t = $s$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("i", c.Method.Parameters[0].Name);
}
[Test]
public void PreferUserDefinedConversionOverReferenceConversion()
{
// actually this is not because user-defined conversions are better;
// but because string is a better conversion target
string program = @"
class AA {
public static implicit operator string(AA a) { return null; }
}
class Test {
static void M(object obj) {}
static void M(string str) {}
static void Main() {
$M(new AA())$;
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("str", rr.Member.Parameters[0].Name);
}
[Test]
public void PreferAmbiguousConversionOverReferenceConversion()
{
// Ambiguous conversions are a compiler error; but they are not
// preventing the overload from being chosen.
// The user-defined conversion wins because BB is a better conversion target than object.
string program = @"
class AA {
public static implicit operator BB(AA a) { return null; }
}
class BB {
public static implicit operator BB(AA a) { return null; }
}
class Test {
static void M(BB b) {}
static void M(object o) {}
static void Main() {
M($new AA()$);
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsUserDefined);
Assert.IsFalse(c.IsValid);
}
} }
} }

32
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs

@ -110,8 +110,8 @@ class TestClass {
public void InvocationWithDynamicArgumentWithOneApplicableMethod() { public void InvocationWithDynamicArgumentWithOneApplicableMethod() {
string program = @"using System; string program = @"using System;
class TestClass { class TestClass {
public void SomeMethod(int a) {} public int SomeMethod(int a) {}
public void SomeMethod(int a, string b) {} public int SomeMethod(int a, string b) {}
void F() { void F() {
dynamic obj = null; dynamic obj = null;
@ -119,7 +119,9 @@ class TestClass {
} }
}"; }";
var rr = Resolve<CSharpInvocationResolveResult>(program); var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.That(rr, Is.Not.Null);
Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod"));
Assert.That(rr.Type.Kind == TypeKind.Dynamic);
Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1));
Assert.That(rr.Arguments.Count, Is.EqualTo(1)); Assert.That(rr.Arguments.Count, Is.EqualTo(1));
var cr = rr.Arguments[0] as ConversionResolveResult; var cr = rr.Arguments[0] as ConversionResolveResult;
@ -129,6 +131,30 @@ class TestClass {
Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj");
} }
[Test]
public void InvocationWithDynamicArgumentWithOneApplicableMethodReturningVoid() {
string program = @"using System;
class TestClass {
public void SomeMethod(int a) {}
void F() {
dynamic obj = null;
var x = $this.SomeMethod(obj)$;
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.That(rr.IsError, Is.False);
Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic));
Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod"));
Assert.That(rr.Member.Parameters.Count, Is.EqualTo(1));
Assert.That(rr.Arguments.Count, Is.EqualTo(1));
var cr = rr.Arguments[0] as ConversionResolveResult;
Assert.That(cr, Is.Not.Null);
Assert.That(cr.Conversion.IsImplicit, Is.True);
Assert.That(cr.Conversion.IsDynamicConversion, Is.True);
Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj");
}
[Test] [Test]
public void InvocationWithDynamicArgumentWhenBothAnOwnAndABaseMethodAreApplicable() { public void InvocationWithDynamicArgumentWhenBothAnOwnAndABaseMethodAreApplicable() {
string program = @"using System; string program = @"using System;
@ -286,7 +312,7 @@ class TestClass {
var x = $this.SomeMethod(obj)$; var x = $this.SomeMethod(obj)$;
} }
}"; }";
var rr = Resolve(program); var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.That(rr.IsError, Is.True); Assert.That(rr.IsError, Is.True);
} }

344
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs

@ -509,7 +509,6 @@ class C {
} }
[Test] [Test]
[Ignore("Not implemented yet.")]
public void BothDirectConversionAndBaseClassConversionAvailable() public void BothDirectConversionAndBaseClassConversionAvailable()
{ {
var rr = Resolve<ConversionResolveResult>(@" var rr = Resolve<ConversionResolveResult>(@"
@ -528,5 +527,348 @@ class C {
Assert.IsTrue(rr.Conversion.IsUserDefined); Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("b", rr.Conversion.Method.Parameters.Single().Name); Assert.AreEqual("b", rr.Conversion.Method.Parameters.Single().Name);
} }
[Test]
public void UserDefinedExplicitConversion_PicksExactSourceTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible(short s) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassedSourceTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(long l) {return new Convertible(); }
public static explicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)(ushort)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassingSourceType() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible(ushort us) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)(long)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_NoMostEncompassingSourceTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(uint i) {return new Convertible(); }
public static explicit operator Convertible(short us) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)(long)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsFalse(rr.Conversion.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_PicksExactTargetTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator int(Convertible i) {return 0; }
public static explicit operator short(Convertible s) {return 0; }
}
class Test {
public void M() {
var a = $(int)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassingTargetTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator int(Convertible i) {return 0; }
public static explicit operator ushort(Convertible us) {return 0; }
}
class Test {
public void M() {
var a = $(ulong)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("us", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassedTargetType() {
string program = @"using System;
class Convertible {
public static explicit operator long(Convertible l) { return 0; }
public static explicit operator uint(Convertible ui) { return 0; }
}
class Test {
public void M() {
var a = $(ushort)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_NoMostEncompassedTargetTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static explicit operator ulong(Convertible l) { return 0; }
public static explicit operator int(Convertible ui) { return 0; }
}
class Test {
public void M() {
var a = $(ushort)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsFalse(rr.Conversion.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_AmbiguousIsInvalid() {
string program = @"using System;
class Convertible1 {
public static explicit operator Convertible2(Convertible1 c) {return 0; }
}
class Convertible2 {
public static explicit operator Convertible2(Convertible1 c) {return 0; }
}
class Test {
public void M() {
var a = $(Convertible2)new Convertible1()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsFalse(rr.Conversion.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_Lifted() {
string program = @"using System;
struct Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
}
class Test {
public void M(int? i) {
a = $(Convertible?)i$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.IsTrue(rr.Conversion.IsLifted);
}
[Test]
public void UserDefinedExplicitConversionFollowedByImplicitNullableConversion() {
string program = @"using System;
struct Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
}
class Test {
public void M(int i) {
a = $(Convertible?)i$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.IsFalse(rr.Conversion.IsLifted);
}
[Test]
public void UserDefinedExplicitConversion_ExplicitNullable_ThenUserDefined() {
string program = @"using System;
struct Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible?(int? ni) {return new Convertible(); }
}
class Test {
public void M(int? i) {
a = $(Convertible)i$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.IsFalse(rr.Conversion.IsLifted);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_DefinedNullableTakesPrecedenceOverLifted() {
string program = @"using System;
struct Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible?(int? ni) {return new Convertible(); }
}
class Test {
public void M() {
a = $(Convertible?)(int?)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.IsFalse(rr.Conversion.IsLifted);
Assert.AreEqual("ni", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_UIntConstant() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(long l) {return new Convertible(); }
public static explicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_NullableUIntConstant() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(long? l) {return new Convertible(); }
public static explicit operator Convertible(uint? ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $(Convertible)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UseDefinedExplicitConversion_Lifted() {
string program = @"
struct Convertible {
public static explicit operator Convertible(int i) { return new Convertible(); }
}
class Test {
public void M(int? i) {
a = $(Convertible?)i$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.IsTrue(rr.Conversion.IsLifted);
Assert.IsTrue(rr.Input is LocalResolveResult);
}
[Test]
public void UserDefinedExplicitConversion_Short_Or_NullableByte_Target()
{
string program = @"using System;
class Test {
public static explicit operator short(Test s) { return 0; }
public static explicit operator byte?(Test b) { return 0; }
}
class Program {
public static void Main(string[] args)
{
int? x = $(int?)new Test()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("System.Int16", rr.Conversion.Method.ReturnType.FullName);
}
[Test]
public void UserDefinedExplicitConversion_Byte_Or_NullableShort_Target()
{
string program = @"using System;
class Test {
public static explicit operator byte(Test b) { return 0; }
public static explicit operator short?(Test s) { return 0; }
}
class Program {
public static void Main(string[] args)
{
int? x = $(int?)new Test()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("s", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void ExplicitConversionOperatorsCanOverrideApplicableImplicitOnes()
{
string program = @"
struct Convertible {
public static explicit operator int(Convertible ci) {return 0; }
public static implicit operator short(Convertible cs) {return 0; }
}
class Test {
static void Main() {
int i = $(int)new Convertible()$; // csc uses the explicit conversion operator
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ci", rr.Conversion.Method.Parameters[0].Name);
}
} }
} }

63
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs

@ -735,5 +735,68 @@ public class C {
var rr = Resolve<CSharpInvocationResolveResult>(program); var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError); Assert.IsFalse(rr.IsError);
} }
[Test]
public void OverloadResolutionIsAmbiguousEvenIfNotDelegateCompatible() {
string program = @"
class Test {
static void M(Func<int> o) {}
static void M(Action o) {}
static int K() { return 0; }
static void Main() {
$M(K)$;
}
}";
// K is only delegate-compatible with one of the overloads; yet we get an invalid match.
// This is because the conversion exists even though it is invalid.
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, rr.OverloadResolutionErrors);
}
[Test]
public void IndexerWithMoreSpecificParameterTypesIsPreferred()
{
string program = @"
class A {
public static void Test(B<object> b) {
x = $b[4]$;
}
}
public class B<T> {
public string this[T key] {
get { return ""1""; }
}
public int this[object key] {
get { return 2; }
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("System.Int32", rr.Member.ReturnType.FullName);
}
[Test]
public void MethodWithMoreSpecificParameterTypesIsPreferred()
{
string program = @"
class A {
public static void Test(B<object> b) {
$b.M(4)$;
}
}
public class B<T> {
public string M(T key) {
return ""1"";
}
public int M(object key) {
return 2;
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("System.Int32", rr.Member.ReturnType.FullName);
}
} }
} }

12
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs

@ -89,6 +89,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Assert.AreSame(c1, r.BestCandidate); Assert.AreSame(c1, r.BestCandidate);
} }
[Test]
public void PreferUIntOverLong_FromIntLiteral()
{
ResolveResult[] args = { new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1) };
OverloadResolution r = new OverloadResolution(compilation, args);
var c1 = MakeMethod(typeof(uint));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(c1));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(long))));
Assert.IsFalse(r.IsAmbiguous);
Assert.AreSame(c1, r.BestCandidate);
}
[Test] [Test]
public void NullableIntAndNullableUIntIsAmbiguous() public void NullableIntAndNullableUIntIsAmbiguous()
{ {

100
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs

@ -21,7 +21,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -30,9 +30,8 @@ using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Resolver namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
[TestFixture] [TestFixture]
public class TypeInferenceTests public class TypeInferenceTests : ResolverTestBase
{ {
readonly ICompilation compilation = new SimpleCompilation(CecilLoaderTests.Mscorlib);
TypeInference ti; TypeInference ti;
[SetUp] [SetUp]
@ -456,9 +455,102 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public void CommonSubTypeIEnumerableClonableIEnumerableComparableList() public void CommonSubTypeIEnumerableClonableIEnumerableComparableList()
{ {
Assert.AreEqual( Assert.AreEqual(
Resolve(typeof(List<string>), typeof(List<Version>), typeof(Collection<string>), typeof(Collection<Version>), typeof(ReadOnlyCollection<string>), typeof(ReadOnlyCollection<Version>)), Resolve(typeof(List<string>), typeof(List<Version>), typeof(Collection<string>), typeof(Collection<Version>),
typeof(ReadOnlyCollectionBuilder<string>), typeof(ReadOnlyCollectionBuilder<Version>),
typeof(ReadOnlyCollection<string>), typeof(ReadOnlyCollection<Version>)),
FindAllTypesInBounds(Resolve(), Resolve(typeof(IEnumerable<ICloneable>), typeof(IEnumerable<IComparable>), typeof(IList)))); FindAllTypesInBounds(Resolve(), Resolve(typeof(IEnumerable<ICloneable>), typeof(IEnumerable<IComparable>), typeof(IList))));
} }
#endregion #endregion
[Test]
public void NullablePick()
{
string program = @"
interface ICo<out T> {}
interface IContra<in T> {}
class Test
{
static T Pick<T> (T? a, T? b)
{
return a;
}
public static void Test(int? i, long? l)
{
$Pick(i, l)$;
}
}
";
var mrr = Resolve<CSharpInvocationResolveResult>(program);
Assert.AreEqual("System.Int64", mrr.Type.FullName);
Assert.IsFalse(mrr.IsError);
}
[Test]
public void CoContraPick()
{
string program = @"
interface ICo<out T> {}
interface IContra<in T> {}
class Test
{
static T Pick<T> (ICo<T> a, IContra<T> b)
{
return a;
}
public static void Test(ICo<string> i, IContra<object> l)
{
$Pick(i, l)$;
}
}
";
// String and Object are both valid choices; and csc ends up picking object,
// even though the C# specification says it should pick string:
// 7.5.2.11 Fixing - both string and object are in the candidate set;
// string has a conversion to object (the other candidate),
// object doesn't have that; so string should be chosen as the result.
// We follow the csc behavior.
var mrr = Resolve<CSharpInvocationResolveResult>(program);
Assert.AreEqual("System.Object", mrr.Type.FullName);
Assert.IsFalse(mrr.IsError);
}
/// <summary>
/// Bug 9300 - Unknown Resolve Error
/// </summary>
[Test]
public void TestBug9300()
{
string program = @"struct S
{
public static implicit operator string (S s)
{
return ""a"";
}
}
interface I<in T>
{
}
class C : I<string>
{
static T Foo<T> (T a, I<T> b)
{
return a;
}
public static void Main ()
{
S s = new S ();
I<string> i = new C ();
var result = $Foo (s, i)$;
}
}
";
var mrr = Resolve<CSharpInvocationResolveResult>(program);
Assert.AreEqual("System.String", mrr.Type.FullName);
Assert.IsFalse(mrr.IsError);
}
} }
} }

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -370,7 +370,7 @@
<Compile Include="CSharp\CodeCompletion\ImportCompletionTests.cs" /> <Compile Include="CSharp\CodeCompletion\ImportCompletionTests.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj"> <ProjectReference Include="..\..\cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project> <Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name> <Name>Mono.Cecil</Name>
<Private>False</Private> <Private>False</Private>

1
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs

@ -149,6 +149,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase
public void MethodWithOutParameter(out int x) { x = 0; } public void MethodWithOutParameter(out int x) { x = 0; }
public void MethodWithParamsArray(params object[] x) {} public void MethodWithParamsArray(params object[] x) {}
public void MethodWithOptionalParameter(int x = 4) {} public void MethodWithOptionalParameter(int x = 4) {}
public void MethodWithExplicitOptionalParameter([Optional] int x) {}
public void MethodWithEnumOptionalParameter(StringComparison x = StringComparison.OrdinalIgnoreCase) {} public void MethodWithEnumOptionalParameter(StringComparison x = StringComparison.OrdinalIgnoreCase) {}
} }

12
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs

@ -605,6 +605,18 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual(0, p.Attributes.Count); Assert.AreEqual(0, p.Attributes.Count);
Assert.AreEqual(4, p.ConstantValue); Assert.AreEqual(4, p.ConstantValue);
} }
[Test]
public void MethodWithExplicitOptionalParameter()
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithExplicitOptionalParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsFalse(p.IsParams);
// explicit optional parameter appears in type system if it's read from C#, but not when read from IL
//Assert.AreEqual(1, p.Attributes.Count);
}
[Test] [Test]
public void MethodWithEnumOptionalParameter() public void MethodWithEnumOptionalParameter()

2
src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -272,7 +272,7 @@
<Folder Include="Utils\CompositeFormatStringParser\" /> <Folder Include="Utils\CompositeFormatStringParser\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj"> <ProjectReference Include="..\..\cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project> <Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name> <Name>Mono.Cecil</Name>
</ProjectReference> </ProjectReference>

30
src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/Conversion.cs

@ -74,18 +74,27 @@ namespace ICSharpCode.NRefactory.Semantics
/// </summary> /// </summary>
public static readonly Conversion TryCast = new BuiltinConversion(false, 9); public static readonly Conversion TryCast = new BuiltinConversion(false, 9);
[Obsolete("Use UserDefinedConversion() instead")]
public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod, bool isLifted) public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod, bool isLifted)
{ {
if (operatorMethod == null) if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod"); throw new ArgumentNullException("operatorMethod");
return new UserDefinedConversion(true, operatorMethod, isLifted); return new UserDefinedConv(true, operatorMethod, isLifted, false);
} }
[Obsolete("Use UserDefinedConversion() instead")]
public static Conversion UserDefinedExplicitConversion(IMethod operatorMethod, bool isLifted) public static Conversion UserDefinedExplicitConversion(IMethod operatorMethod, bool isLifted)
{ {
if (operatorMethod == null) if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod"); throw new ArgumentNullException("operatorMethod");
return new UserDefinedConversion(false, operatorMethod, isLifted); return new UserDefinedConv(false, operatorMethod, isLifted, false);
}
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, bool isLifted = false, bool isAmbiguous = false)
{
if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod");
return new UserDefinedConv(isImplicit, operatorMethod, isLifted, isAmbiguous);
} }
public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup) public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup)
@ -262,17 +271,23 @@ namespace ICSharpCode.NRefactory.Semantics
} }
} }
sealed class UserDefinedConversion : Conversion sealed class UserDefinedConv : Conversion
{ {
readonly IMethod method; readonly IMethod method;
readonly bool isLifted; readonly bool isLifted;
readonly bool isImplicit; readonly bool isImplicit;
readonly bool isValid;
public UserDefinedConversion(bool isImplicit, IMethod method, bool isLifted) public UserDefinedConv(bool isImplicit, IMethod method, bool isLifted, bool isAmbiguous)
{ {
this.method = method; this.method = method;
this.isLifted = isLifted; this.isLifted = isLifted;
this.isImplicit = isImplicit; this.isImplicit = isImplicit;
this.isValid = !isAmbiguous;
}
public override bool IsValid {
get { return isValid; }
} }
public override bool IsImplicit { public override bool IsImplicit {
@ -297,19 +312,20 @@ namespace ICSharpCode.NRefactory.Semantics
public override bool Equals(Conversion other) public override bool Equals(Conversion other)
{ {
UserDefinedConversion o = other as UserDefinedConversion; UserDefinedConv o = other as UserDefinedConv;
return o != null && isLifted == o.isLifted && isImplicit == o.isImplicit && method.Equals(o.method); return o != null && isLifted == o.isLifted && isImplicit == o.isImplicit && isValid == o.isValid && method.Equals(o.method);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return unchecked(method.GetHashCode() * (isLifted ? 31 : 27) * (isImplicit ? 71 : 61)); return unchecked(method.GetHashCode() + (isLifted ? 31 : 27) + (isImplicit ? 71 : 61) + (isValid ? 107 : 109));
} }
public override string ToString() public override string ToString()
{ {
return (isImplicit ? "implicit" : "explicit") return (isImplicit ? "implicit" : "explicit")
+ (isLifted ? " lifted" : "") + (isLifted ? " lifted" : "")
+ (isValid ? "" : " ambiguous")
+ "user-defined conversion (" + method + ")"; + "user-defined conversion (" + method + ")";
} }
} }

5
src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs

@ -45,8 +45,9 @@ namespace ICSharpCode.NRefactory.Semantics
public InvocationResolveResult(ResolveResult targetResult, IParameterizedMember member, public InvocationResolveResult(ResolveResult targetResult, IParameterizedMember member,
IList<ResolveResult> arguments = null, IList<ResolveResult> arguments = null,
IList<ResolveResult> initializerStatements = null) IList<ResolveResult> initializerStatements = null,
: base(targetResult, member) IType returnTypeOverride = null)
: base(targetResult, member, returnTypeOverride)
{ {
this.Arguments = arguments ?? EmptyList<ResolveResult>.Instance; this.Arguments = arguments ?? EmptyList<ResolveResult>.Instance;
this.InitializerStatements = initializerStatements ?? EmptyList<ResolveResult>.Instance; this.InitializerStatements = initializerStatements ?? EmptyList<ResolveResult>.Instance;

8
src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs

@ -38,8 +38,8 @@ namespace ICSharpCode.NRefactory.Semantics
readonly ResolveResult targetResult; readonly ResolveResult targetResult;
readonly bool isVirtualCall; readonly bool isVirtualCall;
public MemberResolveResult(ResolveResult targetResult, IMember member) public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnTypeOverride = null)
: base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) : base(returnTypeOverride ?? (member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType))
{ {
this.targetResult = targetResult; this.targetResult = targetResult;
this.member = member; this.member = member;
@ -54,8 +54,8 @@ namespace ICSharpCode.NRefactory.Semantics
} }
} }
public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall) public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall, IType returnTypeOverride = null)
: base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) : base(returnTypeOverride ?? (member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType))
{ {
this.targetResult = targetResult; this.targetResult = targetResult;
this.member = member; this.member = member;

11
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedParameter.cs

@ -190,6 +190,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
return b.ToString(); return b.ToString();
} }
static bool IsOptionalAttribute (IType attributeType)
{
return attributeType.Name == "OptionalAttribute" && attributeType.Namespace == "System.Runtime.InteropServices";
}
public IParameter CreateResolvedParameter(ITypeResolveContext context) public IParameter CreateResolvedParameter(ITypeResolveContext context)
{ {
@ -205,8 +210,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
IsParams = this.IsParams IsParams = this.IsParams
}; };
} else { } else {
return new DefaultParameter(type.Resolve(context), name, region, var resolvedAttributes = attributes.CreateResolvedAttributes (context);
attributes.CreateResolvedAttributes(context), IsRef, IsOut, IsParams); bool isOptional = resolvedAttributes != null && resolvedAttributes.Any (a => IsOptionalAttribute (a.AttributeType));
return new DefaultParameter (type.Resolve (context), name, region,
resolvedAttributes, IsRef, IsOut, IsParams, isOptional);
} }
} }

2
src/Libraries/NRefactory/NRefactory.sln

@ -14,7 +14,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "I
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.Tests", "ICSharpCode.NRefactory.Tests\ICSharpCode.NRefactory.Tests.csproj", "{63D3B27A-D966-4902-90B3-30290E1692F1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.Tests", "ICSharpCode.NRefactory.Tests\ICSharpCode.NRefactory.Tests.csproj", "{63D3B27A-D966-4902-90B3-30290E1692F1}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "..\Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "..\cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.CSharp", "ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj", "{53DCA265-3C3C-42F9-B647-F72BA678122B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.CSharp", "ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj", "{53DCA265-3C3C-42F9-B647-F72BA678122B}"
EndProject EndProject

3
src/Libraries/NRefactory/README

@ -9,7 +9,8 @@ How to download:
How to compile: How to compile:
1. Get Mono.Cecil 1. Get Mono.Cecil
Download Cecil from https://github.com/jbevain/cecil/ and unzip it into a directory named "Mono.Cecil" Get Cecil from https://github.com/jbevain/cecil ('git clone git://github.com/jbevain/cecil.git')
or download Cecil from https://github.com/jbevain/cecil/ and unzip it into a directory named "cecil"
next to the directory containing NRefactory. next to the directory containing NRefactory.
2. Open NRefactory.sln in your favorite .NET IDE and compile. 2. Open NRefactory.sln in your favorite .NET IDE and compile.

Loading…
Cancel
Save