Browse Source

Merge NRefactory commit 'a3ced27e3e0515d4192e49318f2d24ec9f11fcd1' into SharpDevelop newNR branch.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
5e58218a22
  1. 95
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs
  2. 36
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs
  3. 16
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  4. 103
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs
  5. 19
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CheckIfParameterIsNull.cs
  6. 6
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/ConvertForeachToFor.cs
  7. 36
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IInspector.cs
  8. 61
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/InspectionIssue.cs
  9. 98
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs
  10. 63
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/GatherVisitorBase.cs
  11. 77
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/NotImplementedExceptionInspector.cs
  12. 82
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantInternalInspector.cs
  13. 95
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantNamespaceUsageInspector.cs
  14. 143
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantPrivateInspector.cs
  15. 119
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantThisInspector.cs
  16. 77
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantUsingInspector.cs
  17. 121
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/StringIsNullOrEmptyInspector.cs
  18. 113
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/UseVarKeywordInspector.cs
  19. 40
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/PatternHelper.cs
  20. 49
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringAstHelper.cs
  21. 50
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs
  22. 10
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  23. 13
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  24. 42
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs
  25. 178
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs
  26. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
  27. 30
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
  28. 16
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
  29. 24
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs
  30. 22
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs
  31. 13
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/NameContextTests.cs
  32. 23
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/ConvertForeachToForTests.cs
  33. 66
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs
  34. 28
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs
  35. 13
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs
  36. 19
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs
  37. 48
      src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
  38. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs
  39. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ITextSource.cs
  40. 19
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/Conversion.cs
  41. 25
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs
  42. 12
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs
  43. 17
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
  44. 1
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/IMember.cs
  45. 32
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs
  46. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs
  47. 3
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
  48. 10
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs
  49. 23
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs
  50. 15
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs
  51. 184
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs
  52. 103
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs
  53. 21
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs
  54. 45
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs
  55. 94
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs
  56. 17
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/InheritanceHelper.cs

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

@ -189,6 +189,29 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
} }
} }
IEnumerable<ICompletionData> HandleMemberReferenceCompletion(ExpressionResult expr)
{
if (expr == null)
return null;
// do not complete <number>. (but <number>.<number>.)
if (expr.Node is PrimitiveExpression) {
var pexpr = (PrimitiveExpression)expr.Node;
if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains('.')) {
return null;
}
}
var resolveResult = ResolveExpression (expr);
if (resolveResult == null) {
return null;
}
if (expr.Node is AstType) {
return CreateTypeAndNamespaceCompletionData(location, resolveResult.Item1, expr.Node, resolveResult.Item2);
}
return CreateCompletionData(location, resolveResult.Item1, expr.Node, resolveResult.Item2);
}
IEnumerable<ICompletionData> MagicKeyCompletion(char completionChar, bool controlSpace) IEnumerable<ICompletionData> MagicKeyCompletion(char completionChar, bool controlSpace)
{ {
ExpressionResult expr; ExpressionResult expr;
@ -201,26 +224,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
if (IsInsideCommentOrString()) { if (IsInsideCommentOrString()) {
return Enumerable.Empty<ICompletionData>(); return Enumerable.Empty<ICompletionData>();
} }
expr = GetExpressionBeforeCursor(); return HandleMemberReferenceCompletion(GetExpressionBeforeCursor());
if (expr == null) {
return null;
}
// do not complete <number>. (but <number>.<number>.)
if (expr.Node is PrimitiveExpression) {
var pexpr = (PrimitiveExpression)expr.Node;
if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains('.')) {
return null;
}
}
resolveResult = ResolveExpression(expr);
if (resolveResult == null) {
return null;
}
if (expr.Node is AstType) {
return CreateTypeAndNamespaceCompletionData(location, resolveResult.Item1, expr.Node, resolveResult.Item2);
}
return CreateCompletionData(location, resolveResult.Item1, expr.Node, resolveResult.Item2);
case '#': case '#':
if (IsInsideCommentOrString()) { if (IsInsideCommentOrString()) {
return null; return null;
@ -506,20 +510,32 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
var contextList = new CompletionDataWrapper (this); var contextList = new CompletionDataWrapper (this);
var identifierStart = GetExpressionAtCursor(); var identifierStart = GetExpressionAtCursor();
if (identifierStart != null) {
if (identifierStart != null && identifierStart.Node is TypeParameterDeclaration) {
return null;
}
if (identifierStart != null && identifierStart.Node is VariableInitializer && location <= ((VariableInitializer)identifierStart.Node).NameToken.EndLocation) {
return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(identifierStart) : null;
}
if (identifierStart != null && identifierStart.Node is CatchClause) { if (identifierStart.Node is TypeParameterDeclaration) {
if (((CatchClause)identifierStart.Node).VariableNameToken.Contains(location)) {
return null; return null;
} }
identifierStart = null;
if (identifierStart.Node is MemberReferenceExpression) {
return HandleMemberReferenceCompletion(new ExpressionResult (((MemberReferenceExpression)identifierStart.Node).Target, identifierStart.Unit));
}
if (identifierStart.Node is Identifier) {
// May happen in variable names
return controlSpace ? DefaultControlSpaceItems(identifierStart) : null;
}
if (identifierStart.Node is VariableInitializer && location <= ((VariableInitializer)identifierStart.Node).NameToken.EndLocation) {
return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(identifierStart) : null;
}
if (identifierStart.Node is CatchClause) {
if (((CatchClause)identifierStart.Node).VariableNameToken.Contains(location)) {
return null;
}
identifierStart = null;
}
} }
if (!(char.IsLetter(completionChar) || completionChar == '_') && (!controlSpace || identifierStart == null || !(identifierStart.Node.Parent is ArrayInitializerExpression))) { if (!(char.IsLetter(completionChar) || completionChar == '_') && (!controlSpace || identifierStart == null || !(identifierStart.Node.Parent is ArrayInitializerExpression))) {
return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(identifierStart) : null; return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(identifierStart) : null;
@ -2307,14 +2323,14 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
ExpressionResult GetExpressionAtCursor() ExpressionResult GetExpressionAtCursor()
{ {
// TextLocation memberLocation; // TextLocation memberLocation;
// if (currentMember != null) { // if (currentMember != null) {
// memberLocation = currentMember.Region.Begin; // memberLocation = currentMember.Region.Begin;
// } else if (currentType != null) { // } else if (currentType != null) {
// memberLocation = currentType.Region.Begin; // memberLocation = currentType.Region.Begin;
// } else { // } else {
// memberLocation = location; // memberLocation = location;
// } // }
var baseUnit = ParseStub("a"); var baseUnit = ParseStub("a");
var tmpUnit = baseUnit; var tmpUnit = baseUnit;
@ -2322,7 +2338,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
if (expr == null) { if (expr == null) {
expr = baseUnit.GetNodeAt<AstType>(location.Line, location.Column - 1); expr = baseUnit.GetNodeAt<AstType>(location.Line, location.Column - 1);
} }
if (expr == null)
expr = baseUnit.GetNodeAt<Identifier>(location.Line, location.Column - 1);
// try insertStatement // try insertStatement
if (expr == null && baseUnit.GetNodeAt<EmptyStatement>(location.Line, location.Column) != null) { if (expr == null && baseUnit.GetNodeAt<EmptyStatement>(location.Line, location.Column) != null) {
tmpUnit = baseUnit = ParseStub("a();", false); tmpUnit = baseUnit = ParseStub("a();", false);

36
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs

@ -415,9 +415,9 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
wrapper.Append (';'); wrapper.Append (';');
} }
protected CompilationUnit ParseStub (string continuation, bool appendSemicolon = true, string afterContinuation = null) protected CompilationUnit ParseStub(string continuation, bool appendSemicolon = true, string afterContinuation = null)
{ {
var mt = GetMemberTextToCaret (); var mt = GetMemberTextToCaret();
if (mt == null) { if (mt == null) {
return null; return null;
} }
@ -429,7 +429,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
var wrapper = new StringBuilder (); var wrapper = new StringBuilder ();
bool wrapInClass = memberLocation != new TextLocation (1, 1); bool wrapInClass = memberLocation != new TextLocation (1, 1);
if (wrapInClass) { if (wrapInClass) {
var nodeAtLocation = Unit.GetNodeAt (memberLocation, n => n is TypeDeclaration || n is NamespaceDeclaration); var nodeAtLocation = Unit.GetNodeAt(memberLocation, n => n is TypeDeclaration || n is NamespaceDeclaration);
if (nodeAtLocation != null) { if (nodeAtLocation != null) {
foreach (var n in nodeAtLocation.AncestorsAndSelf) { foreach (var n in nodeAtLocation.AncestorsAndSelf) {
if (memberLocation == n.StartLocation) { if (memberLocation == n.StartLocation) {
@ -438,25 +438,25 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
if (n is TypeDeclaration) { if (n is TypeDeclaration) {
var t = (TypeDeclaration)n; var t = (TypeDeclaration)n;
switch (t.ClassType) { switch (t.ClassType) {
case ClassType.Class: case ClassType.Class:
wrapper.Append ("class"); wrapper.Append("class");
break; break;
case ClassType.Struct: case ClassType.Struct:
wrapper.Append ("struct"); wrapper.Append("struct");
break; break;
case ClassType.Interface: case ClassType.Interface:
wrapper.Append ("interface"); wrapper.Append("interface");
break; break;
case ClassType.Enum: case ClassType.Enum:
wrapper.Append ("enum"); wrapper.Append("enum");
break; break;
} }
wrapper.Append (" " + t.Name + " {"); wrapper.Append(" " + t.Name + " {");
wrapper.AppendLine (); wrapper.AppendLine();
closingBrackets++; closingBrackets++;
generatedLines++; generatedLines++;
} else { } else {
Console.WriteLine (n); Console.WriteLine(n);
} }
} }
} }

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

@ -265,6 +265,8 @@
<Compile Include="Refactoring\ContextAction\UseVarKeyword.cs" /> <Compile Include="Refactoring\ContextAction\UseVarKeyword.cs" />
<Compile Include="Refactoring\DocumentScript.cs" /> <Compile Include="Refactoring\DocumentScript.cs" />
<Compile Include="Refactoring\IContextAction.cs" /> <Compile Include="Refactoring\IContextAction.cs" />
<Compile Include="Refactoring\PatternHelper.cs" />
<Compile Include="Refactoring\RefactoringAstHelper.cs" />
<Compile Include="Refactoring\RefactoringContext.cs" /> <Compile Include="Refactoring\RefactoringContext.cs" />
<Compile Include="Refactoring\Script.cs" /> <Compile Include="Refactoring\Script.cs" />
<Compile Include="Refactoring\TypeSystemAstBuilder.cs" /> <Compile Include="Refactoring\TypeSystemAstBuilder.cs" />
@ -322,6 +324,19 @@
<Compile Include="Refactoring\ContextAction\RemoveRegion.cs" /> <Compile Include="Refactoring\ContextAction\RemoveRegion.cs" />
<Compile Include="Refactoring\ContextAction\GenerateProperty.cs" /> <Compile Include="Refactoring\ContextAction\GenerateProperty.cs" />
<Compile Include="Ast\Roles.cs" /> <Compile Include="Ast\Roles.cs" />
<Compile Include="Refactoring\Inspector\ConditionalToNullCoalescingInspector.cs" />
<Compile Include="Refactoring\IInspector.cs" />
<Compile Include="Refactoring\InspectionIssue.cs" />
<Compile Include="Refactoring\BaseRefactoringContext.cs" />
<Compile Include="Refactoring\Inspector\GatherVisitorBase.cs" />
<Compile Include="Refactoring\Inspector\NotImplementedExceptionInspector.cs" />
<Compile Include="Refactoring\Inspector\RedundantInternalInspector.cs" />
<Compile Include="Refactoring\Inspector\RedundantNamespaceUsageInspector.cs" />
<Compile Include="Refactoring\Inspector\RedundantPrivateInspector.cs" />
<Compile Include="Refactoring\Inspector\RedundantThisInspector.cs" />
<Compile Include="Refactoring\Inspector\RedundantUsingInspector.cs" />
<Compile Include="Refactoring\Inspector\StringIsNullOrEmptyInspector.cs" />
<Compile Include="Refactoring\Inspector\UseVarKeywordInspector.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> <ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
@ -332,6 +347,7 @@
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup> <ItemGroup>
<Folder Include="Completion\" /> <Folder Include="Completion\" />
<Folder Include="Refactoring\Inspector\" />
</ItemGroup> </ItemGroup>
<ProjectExtensions> <ProjectExtensions>
<MonoDevelop> <MonoDevelop>

103
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs

@ -0,0 +1,103 @@
//
// BaseRefactoringContext.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class BaseRefactoringContext
{
protected readonly CSharpAstResolver resolver;
readonly CancellationToken cancellationToken;
public virtual bool Supports(Version version)
{
return true;
}
public CancellationToken CancellationToken {
get { return cancellationToken; }
}
public virtual AstNode RootNode {
get { return resolver.RootNode; }
}
public ICompilation Compilation {
get { return resolver.Compilation; }
}
public BaseRefactoringContext (ICSharpCode.NRefactory.CSharp.Resolver.CSharpAstResolver resolver, System.Threading.CancellationToken cancellationToken)
{
this.resolver = resolver;
this.cancellationToken = cancellationToken;
}
#region Resolving
public ResolveResult Resolve (AstNode node)
{
return resolver.Resolve (node, cancellationToken);
}
public CSharpResolver GetResolverStateBefore(AstNode node)
{
return resolver.GetResolverStateBefore (node, cancellationToken);
}
public CSharpResolver GetResolverStateAfter(AstNode node)
{
return resolver.GetResolverStateAfter (node, cancellationToken);
}
public IType ResolveType (AstType type)
{
return resolver.Resolve (type, cancellationToken).Type;
}
public IType GetExpectedType (Expression expression)
{
return resolver.GetExpectedType(expression, cancellationToken);
}
public Conversion GetConversion (Expression expression)
{
return resolver.GetConversion(expression, cancellationToken);
}
#endregion
public abstract Script StartScript();
}
}

19
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CheckIfParameterIsNull.cs

@ -46,10 +46,10 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (bodyStatement == null) if (bodyStatement == null)
return false; return false;
if (parameter.Type is PrimitiveType) var type = context.ResolveType(parameter.Type);
return (((PrimitiveType)parameter.Type).Keyword == "object" || ((PrimitiveType)parameter.Type).Keyword == "string") && !HasNullCheck (parameter); if (type.IsReferenceType == false)
return false;
// TODO: check for structs
return !HasNullCheck (parameter); return !HasNullCheck (parameter);
} }
@ -83,25 +83,20 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
class CheckNullVisitor : DepthFirstAstVisitor<object, object> class CheckNullVisitor : DepthFirstAstVisitor<object, object>
{ {
ParameterDeclaration parameter; readonly Expression pattern;
public bool ContainsNullCheck { internal bool ContainsNullCheck;
get;
set;
}
public CheckNullVisitor (ParameterDeclaration parameter) public CheckNullVisitor (ParameterDeclaration parameter)
{ {
this.parameter = parameter; this.pattern = PatternHelper.CommutativeOperator(new IdentifierExpression(parameter.Name), BinaryOperatorType.Any, new NullReferenceExpression());
} }
public override object VisitIfElseStatement (IfElseStatement ifElseStatement, object data) public override object VisitIfElseStatement (IfElseStatement ifElseStatement, object data)
{ {
if (ifElseStatement.Condition is BinaryOperatorExpression) { if (ifElseStatement.Condition is BinaryOperatorExpression) {
var binOp = ifElseStatement.Condition as BinaryOperatorExpression; var binOp = ifElseStatement.Condition as BinaryOperatorExpression;
if ((binOp.Operator == BinaryOperatorType.Equality || binOp.Operator == BinaryOperatorType.InEquality) && if ((binOp.Operator == BinaryOperatorType.Equality || binOp.Operator == BinaryOperatorType.InEquality) && pattern.IsMatch(binOp)) {
binOp.Left.IsMatch (new IdentifierExpression (parameter.Name)) && binOp.Right.IsMatch (new NullReferenceExpression ()) ||
binOp.Right.IsMatch (new IdentifierExpression (parameter.Name)) && binOp.Left.IsMatch (new NullReferenceExpression ())) {
ContainsNullCheck = true; ContainsNullCheck = true;
} }
} }

6
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/ConvertForeachToFor.cs

@ -54,6 +54,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var result = context.Resolve (foreachStatement.InExpression); var result = context.Resolve (foreachStatement.InExpression);
var countProperty = GetCountProperty (result.Type); var countProperty = GetCountProperty (result.Type);
// TODO: use another variable name if 'i' is already in use
var initializer = new VariableDeclarationStatement (new PrimitiveType ("int"), "i", new PrimitiveExpression (0)); var initializer = new VariableDeclarationStatement (new PrimitiveType ("int"), "i", new PrimitiveExpression (0));
var id1 = new IdentifierExpression ("i"); var id1 = new IdentifierExpression ("i");
var id2 = id1.Clone (); var id2 = id1.Clone ();
@ -88,7 +89,10 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (astNode == null) if (astNode == null)
return null; return null;
var result = (astNode as ForeachStatement) ?? astNode.Parent as ForeachStatement; var result = (astNode as ForeachStatement) ?? astNode.Parent as ForeachStatement;
if (result == null || context.Resolve (result.InExpression) == null) if (result == null)
return null;
var collection = context.Resolve (result.InExpression);
if (collection.Type.Kind != TypeKind.Array && !collection.Type.GetProperties(p => p.IsIndexer).Any())
return null; return null;
return result; return result;
} }

36
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IInspector.cs

@ -0,0 +1,36 @@
//
// IInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public interface IInspector
{
IEnumerable<InspectionIssue> Run (BaseRefactoringContext context);
}
}

61
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/InspectionIssue.cs

@ -0,0 +1,61 @@
//
// InspectionIssue.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// 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.Refactoring
{
public class InspectionIssue
{
public string Title {
get;
private set;
}
public TextLocation Start {
get;
private set;
}
public TextLocation End {
get;
private set;
}
public System.Action Fix {
get;
private set;
}
public InspectionIssue (string title, TextLocation start, TextLocation end, System.Action fix)
{
this.Title = title;
this.Start = start;
this.End = end;
this.Fix = fix;
}
}
}

98
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs

@ -0,0 +1,98 @@
//
// ConditionalToNullCoalescingInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Checks for "a != null ? a : other"<expr>
/// Converts to: "a ?? other"<expr>
/// </summary>
public class ConditionalToNullCoalescingInspector : IInspector
{
static readonly Pattern pattern = new Choice {
// a != null ? a : other
new ConditionalExpression(
PatternHelper.CommutativeOperator(new AnyNode("a"), BinaryOperatorType.InEquality, new NullReferenceExpression()),
new Backreference("a"),
new AnyNode("other")
),
// a == null ? other : a
new ConditionalExpression(
PatternHelper.CommutativeOperator(new AnyNode("a"), BinaryOperatorType.Equality, new NullReferenceExpression()),
new AnyNode("other"),
new Backreference("a")
),
};
string title = "Convert to '??' expression";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly ConditionalToNullCoalescingInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, ConditionalToNullCoalescingInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitConditionalExpression(ConditionalExpression conditionalExpression)
{
Match m = pattern.Match(conditionalExpression);
if (m.Success) {
var a = m.Get<Expression>("a").Single();
var other = m.Get<Expression>("other").Single();
AddIssue(conditionalExpression, inspector.Title, delegate {
using (var script = ctx.StartScript ()) {
var expr = new BinaryOperatorExpression (a.Clone (), BinaryOperatorType.NullCoalescing, other.Clone ());
script.Replace (conditionalExpression, expr);
}
});
}
base.VisitConditionalExpression (conditionalExpression);
}
}
}
}

63
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/GatherVisitorBase.cs

@ -0,0 +1,63 @@
//
// GatherVisitorBase.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp
{
class GatherVisitorBase : DepthFirstAstVisitor
{
protected readonly BaseRefactoringContext ctx;
public readonly List<InspectionIssue> FoundIssues = new List<InspectionIssue> ();
public GatherVisitorBase (BaseRefactoringContext ctx)
{
this.ctx = ctx;
}
protected override void VisitChildren (AstNode node)
{
if (ctx.CancellationToken.IsCancellationRequested)
return;
base.VisitChildren (node);
}
protected void AddIssue (AstNode node, string title, System.Action fix = null)
{
FoundIssues.Add (new InspectionIssue (title, node.StartLocation, node.EndLocation, fix));
}
protected void AddIssue(TextLocation start, TextLocation end, string title, System.Action fix = null)
{
FoundIssues.Add (new InspectionIssue (title, start, end, fix));
}
}
}

77
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/NotImplementedExceptionInspector.cs

@ -0,0 +1,77 @@
//
// NotImplementedExceptionInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// This inspector just shows that there is a not implemented exception. It doesn't offer a fix.
/// Should only be shown in overview bar, no underlining.
/// </summary>
public class NotImplementedExceptionInspector : IInspector
{
string title = "NotImplemented exception thrown";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly NotImplementedExceptionInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, NotImplementedExceptionInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitThrowStatement(ThrowStatement throwStatement)
{
var result = ctx.Resolve (throwStatement.Expression);
if (result.Type.Equals (ctx.Compilation.FindType (typeof(System.NotImplementedException))))
AddIssue (throwStatement, inspector.Title);
base.VisitThrowStatement(throwStatement);
}
}
}
}

82
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantInternalInspector.cs

@ -0,0 +1,82 @@
//
// RedundantInternalInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Finds redundant internal modifiers.
/// </summary>
public class RedundantInternalInspector : IInspector
{
string title = "Remove redundant 'internal' modifier";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly RedundantInternalInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, RedundantInternalInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
foreach (var token_ in typeDeclaration.ModifierTokens) {
var token = token_;
if (token.Modifier == Modifiers.Internal) {
AddIssue(token, inspector.Title, delegate {
using (var script = ctx.StartScript ()) {
script.Remove(token);
}
});
}
}
}
}
}
}

95
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantNamespaceUsageInspector.cs

@ -0,0 +1,95 @@
//
// RedundantNamespaceUsageInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Finds redundant namespace usages.
/// </summary>
public class RedundantNamespaceUsageInspector : IInspector
{
string title = "Remove redundant namespace usage";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly RedundantNamespaceUsageInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, RedundantNamespaceUsageInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
{
base.VisitMemberReferenceExpression(memberReferenceExpression);
var result = ctx.Resolve(memberReferenceExpression.Target);
if (!(result is NamespaceResolveResult)) {
return;
}
var wholeResult = ctx.Resolve(memberReferenceExpression);
if (!(wholeResult is TypeResolveResult)) {
return;
}
var state = ctx.GetResolverStateBefore(memberReferenceExpression);
var lookupName = state.LookupSimpleNameOrTypeName(memberReferenceExpression.MemberName, new List<IType> (), SimpleNameLookupMode.Expression);
if (lookupName is TypeResolveResult && !lookupName.IsError && wholeResult.Type.Equals(lookupName.Type)) {
AddIssue(memberReferenceExpression.StartLocation, memberReferenceExpression.MemberNameToken.StartLocation, inspector.Title, delegate {
using (var script = ctx.StartScript ()) {
script.Replace(memberReferenceExpression, RefactoringAstHelper.RemoveTarget(memberReferenceExpression));
}
}
);
}
}
}
}
}

143
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantPrivateInspector.cs

@ -0,0 +1,143 @@
//
// RedundantPrivateInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Finds redundant internal modifiers.
/// </summary>
public class RedundantPrivateInspector : IInspector
{
string title = "Remove redundant 'private' modifier";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly RedundantPrivateInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, RedundantPrivateInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
void CheckNode(EntityDeclaration node)
{
foreach (var token_ in node.ModifierTokens) {
var token = token_;
if (token.Modifier == Modifiers.Private) {
AddIssue(token, inspector.Title, delegate {
using (var script = ctx.StartScript ()) {
script.Remove(token);
}
});
}
}
}
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
base.VisitMethodDeclaration(methodDeclaration);
CheckNode(methodDeclaration);
}
public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration)
{
base.VisitFieldDeclaration(fieldDeclaration);
CheckNode(fieldDeclaration);
}
public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
base.VisitPropertyDeclaration(propertyDeclaration);
CheckNode(propertyDeclaration);
}
public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration)
{
base.VisitIndexerDeclaration(indexerDeclaration);
CheckNode(indexerDeclaration);
}
public override void VisitEventDeclaration(EventDeclaration eventDeclaration)
{
base.VisitEventDeclaration(eventDeclaration);
CheckNode(eventDeclaration);
}
public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration)
{
base.VisitCustomEventDeclaration(eventDeclaration);
CheckNode(eventDeclaration);
}
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
base.VisitConstructorDeclaration(constructorDeclaration);
CheckNode(constructorDeclaration);
}
public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
base.VisitOperatorDeclaration(operatorDeclaration);
CheckNode(operatorDeclaration);
}
public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration)
{
base.VisitFixedFieldDeclaration(fixedFieldDeclaration);
CheckNode(fixedFieldDeclaration);
}
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
if (!(typeDeclaration.Parent is TypeDeclaration)) {
CheckNode(typeDeclaration);
}
base.VisitTypeDeclaration(typeDeclaration);
}
}
}
}

119
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantThisInspector.cs

@ -0,0 +1,119 @@
//
// RedundantThisInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.CSharp.Resolver;
using System.Linq;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Finds redundant namespace usages.
/// </summary>
public class RedundantThisInspector : IInspector
{
string title = "Remove redundant 'this.'";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly RedundantThisInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, RedundantThisInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
static IMember GetMember (ResolveResult result)
{
if (result is MemberResolveResult) {
return ((MemberResolveResult)result).Member;
} else if (result is MethodGroupResolveResult) {
return ((MethodGroupResolveResult)result).Methods.FirstOrDefault ();
}
return null;
}
public override void VisitThisReferenceExpression (ThisReferenceExpression thisReferenceExpression)
{
base.VisitThisReferenceExpression (thisReferenceExpression);
var memberReference = thisReferenceExpression.Parent as MemberReferenceExpression;
if (memberReference == null) {
return;
}
var state = ctx.GetResolverStateAfter (thisReferenceExpression);
var wholeResult = ctx.Resolve (memberReference);
IMember member = GetMember (wholeResult);
if (member == null) {
return;
}
var result = state.LookupSimpleNameOrTypeName (memberReference.MemberName, EmptyList<IType>.Instance, SimpleNameLookupMode.Expression);
bool isRedundant;
if (result is MemberResolveResult) {
isRedundant = ((MemberResolveResult)result).Member.Region.Equals(member.Region);
} else if (result is MethodGroupResolveResult) {
isRedundant = ((MethodGroupResolveResult)result).Methods.Any(m => m.Region.Equals(member.Region));
} else {
return;
}
if (isRedundant) {
AddIssue(thisReferenceExpression.StartLocation, memberReference.MemberNameToken.StartLocation, inspector.Title, delegate {
using (var script = ctx.StartScript ()) {
script.Replace(memberReference, RefactoringAstHelper.RemoveTarget(memberReference));
}
}
);
}
}
}
}
}

77
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantUsingInspector.cs

@ -0,0 +1,77 @@
//
// RedundantUsingInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Finds redundant using declarations.
/// </summary>
public class RedundantUsingInspector : IInspector
{
string title = "Remove redundant using";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly RedundantUsingInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, RedundantUsingInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration)
{
base.VisitUsingDeclaration(usingDeclaration);
// TODO
// return cSharpResolver.usedScopes
// .OfType<ITypeOrNamespaceReference> ()
// .Any (u => u.ResolveNamespace (ctx).NamespaceName == ns) || additionalNamespaces.Contains (ns);
}
}
}
}

121
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/StringIsNullOrEmptyInspector.cs

@ -0,0 +1,121 @@
//
// StringIsNullOrEmptyInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Checks for str == null && str == ""
/// Converts to: string.IsNullOrEmpty (str)
/// </summary>
public class StringIsNullOrEmptyInspector : IInspector
{
static readonly Pattern pattern = new Choice {
// str == null || str == ""
new BinaryOperatorExpression (
PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ()),
BinaryOperatorType.ConditionalOr,
PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.Equality, new PrimitiveExpression (""))
),
// str == "" || str == null
new BinaryOperatorExpression (
PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")),
BinaryOperatorType.ConditionalOr,
PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ())
),
};
static readonly Pattern negPattern = new Choice {
// str != null && str != ""
new BinaryOperatorExpression (
PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.InEquality, new NullReferenceExpression ()),
BinaryOperatorType.ConditionalAnd,
PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression (""))
),
// str != "" && str != null
new BinaryOperatorExpression (
PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")),
BinaryOperatorType.ConditionalAnd,
PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.InEquality, new NullReferenceExpression ())
),
};
string title = "Use string.IsNullOrEmpty";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly StringIsNullOrEmptyInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, StringIsNullOrEmptyInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
{
base.VisitBinaryOperatorExpression(binaryOperatorExpression);
Match m = pattern.Match(binaryOperatorExpression);
bool isNegated = false;
if (!m.Success) {
m = negPattern.Match(binaryOperatorExpression);
isNegated = true;
}
if (m.Success) {
var str = m.Get<Expression>("str").Single();
AddIssue(binaryOperatorExpression, inspector.Title, delegate {
using (var script = ctx.StartScript ()) {
Expression expr = new PrimitiveType ("string").Invoke("IsNullOrEmpty", str.Clone());
if (isNegated)
expr = new UnaryOperatorExpression (UnaryOperatorType.Not, expr);
script.Replace(binaryOperatorExpression, expr);
}
});
return;
}
}
}
}
}

113
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/UseVarKeywordInspector.cs

@ -0,0 +1,113 @@
//
// UseVarKeywordInspector.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2012 Xamarin <http://xamarin.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using System.Linq;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Checks for places where the 'var' keyword can be used. Note that the action is actually done with a context
/// action.
/// </summary>
public class UseVarKeywordInspector : IInspector
{
string title = "Use 'var' keyword";
public string Title {
get {
return title;
}
set {
title = value;
}
}
public IEnumerable<InspectionIssue> Run (BaseRefactoringContext context)
{
var visitor = new GatherVisitor (context, this);
context.RootNode.AcceptVisitor (visitor);
return visitor.FoundIssues;
}
class GatherVisitor : GatherVisitorBase
{
readonly UseVarKeywordInspector inspector;
public GatherVisitor (BaseRefactoringContext ctx, UseVarKeywordInspector inspector) : base (ctx)
{
this.inspector = inspector;
}
public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement)
{
base.VisitVariableDeclarationStatement(variableDeclarationStatement);
if (variableDeclarationStatement.Type is PrimitiveType) {
return;
}
if (variableDeclarationStatement.Type is SimpleType && ((SimpleType)variableDeclarationStatement.Type).Identifier == "var") {
return;
}
if (variableDeclarationStatement.Variables.Count != 1) {
return;
}
//only checks for cases where the type would be obvious - assignment of new, cast, etc.
//also check the type actually matches else the user might want to assign different subclasses later
var v = variableDeclarationStatement.Variables.Single();
var arrCreate = v.Initializer as ArrayCreateExpression;
if (arrCreate != null) {
var n = variableDeclarationStatement.Type as ComposedType;
//FIXME: check the specifier compatibility
if (n != null && n.ArraySpecifiers.Any() && n.BaseType.IsMatch(arrCreate.Type)) {
AddIssue(variableDeclarationStatement);
}
}
var objCreate = v.Initializer as ObjectCreateExpression;
if (objCreate != null && objCreate.Type.IsMatch(variableDeclarationStatement.Type)) {
AddIssue(variableDeclarationStatement);
}
var asCast = v.Initializer as AsExpression;
if (asCast != null && asCast.Type.IsMatch(variableDeclarationStatement.Type)) {
AddIssue(variableDeclarationStatement);
}
var cast = v.Initializer as CastExpression;
if (cast != null && cast.Type.IsMatch(variableDeclarationStatement.Type)) {
AddIssue(variableDeclarationStatement);
}
}
void AddIssue(VariableDeclarationStatement variableDeclarationStatement)
{
AddIssue(variableDeclarationStatement.Type, inspector.Title);
}
}
}
}

40
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/PatternHelper.cs

@ -0,0 +1,40 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Helper class for constructing pattern ASTs.
/// </summary>
public class PatternHelper
{
/// <summary>
/// Produces a choice pattern for <c>expr1 op expr2</c> or <c>expr2 op expr1</c>.
/// </summary>
public static Expression CommutativeOperator(Expression expr1, BinaryOperatorType op, Expression expr2)
{
return new Choice {
new BinaryOperatorExpression(expr1, op, expr2),
new BinaryOperatorExpression(expr2.Clone(), op, expr1.Clone())
};
}
}
}

49
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringAstHelper.cs

@ -0,0 +1,49 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Helper methods for constructing ASTs for refactoring.
/// These helpers work with frozen ASTs, i.e. they clone input nodes.
/// </summary>
public class RefactoringAstHelper
{
/// <summary>
/// Removes the target from a member reference while preserving the identifier and type arguments.
/// </summary>
public static IdentifierExpression RemoveTarget(MemberReferenceExpression mre)
{
IdentifierExpression ident = new IdentifierExpression(mre.MemberName);
ident.TypeArguments.AddRange(mre.TypeArguments.Select(t => t.Clone()));
return ident;
}
/// <summary>
/// Removes the target from a member reference while preserving the identifier and type arguments.
/// </summary>
public static SimpleType RemoveTarget(MemberType memberType)
{
return new SimpleType(memberType.MemberName, memberType.TypeArguments.Select(t => t.Clone()));
}
}
}

50
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs

@ -36,36 +36,14 @@ using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.NRefactory.CSharp.Refactoring namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
public abstract class RefactoringContext public abstract class RefactoringContext : BaseRefactoringContext
{ {
readonly protected CSharpAstResolver resolver; public RefactoringContext(CSharpAstResolver resolver, CancellationToken cancellationToken) : base (resolver, cancellationToken)
readonly CancellationToken cancellationToken;
public RefactoringContext(CSharpAstResolver resolver, CancellationToken cancellationToken)
{ {
this.resolver = resolver;
this.cancellationToken = cancellationToken;
}
public CancellationToken CancellationToken {
get { return cancellationToken; }
}
public virtual AstNode RootNode {
get { return resolver.RootNode; }
} }
public abstract TextLocation Location { get; } public abstract TextLocation Location { get; }
public virtual bool Supports(Version version)
{
return true;
}
public ICompilation Compilation {
get { return resolver.Compilation; }
}
public virtual AstType CreateShortType (IType fullType) public virtual AstType CreateShortType (IType fullType)
{ {
var csResolver = resolver.GetResolverStateBefore(GetNode()); var csResolver = resolver.GetResolverStateBefore(GetNode());
@ -133,28 +111,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public abstract string GetText (ISegment segment); public abstract string GetText (ISegment segment);
#endregion #endregion
#region Resolving
public ResolveResult Resolve (AstNode node)
{
return resolver.Resolve (node, cancellationToken);
}
public IType ResolveType (AstType type)
{
return resolver.Resolve (type, cancellationToken).Type;
}
public IType GetExpectedType (Expression expression)
{
return resolver.GetExpectedType(expression, cancellationToken);
}
public Conversion GetConversion (Expression expression)
{
return resolver.GetConversion(expression, cancellationToken);
}
#endregion
public virtual string GetNameProposal (string name, bool camelCase = true) public virtual string GetNameProposal (string name, bool camelCase = true)
{ {
string baseName = (camelCase ? char.ToLower (name [0]) : char.ToUpper (name [0])) + name.Substring (1); string baseName = (camelCase ? char.ToLower (name [0]) : char.ToUpper (name [0])) + name.Substring (1);
@ -175,8 +131,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
return baseName + (number > 0 ? (number + 1).ToString () : ""); return baseName + (number > 0 ? (number + 1).ToString () : "");
} }
public abstract Script StartScript();
} }
} }

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

@ -805,10 +805,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
var or = rr.PerformOverloadResolution(compilation, args, allowExpandingParams: false, conversions: this); var or = rr.PerformOverloadResolution(compilation, args, allowExpandingParams: false, conversions: this);
if (or.FoundApplicableCandidate) if (or.FoundApplicableCandidate) {
return Conversion.MethodGroupConversion((IMethod)or.GetBestCandidateWithSubstitutedTypeArguments()); IMethod method = (IMethod)or.GetBestCandidateWithSubstitutedTypeArguments();
else var thisRR = rr.TargetResult as ThisResolveResult;
bool isVirtual = method.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation);
return Conversion.MethodGroupConversion(method, isVirtual);
} else {
return Conversion.None; return Conversion.None;
}
} }
#endregion #endregion

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

@ -1167,13 +1167,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal readonly IParameterizedMember nonLiftedOperator; internal readonly IParameterizedMember nonLiftedOperator;
public LiftedUserDefinedOperator(IMethod nonLiftedMethod) public LiftedUserDefinedOperator(IMethod nonLiftedMethod)
: base(nonLiftedMethod.DeclaringType, (IMethod)nonLiftedMethod.MemberDefinition, : base(nonLiftedMethod, TypeParameterSubstitution.Identity)
EmptyList<IType>.Instance, new MakeNullableVisitor(nonLiftedMethod.Compilation))
{ {
this.nonLiftedOperator = nonLiftedMethod; this.nonLiftedOperator = nonLiftedMethod;
var substitution = new MakeNullableVisitor(nonLiftedMethod.Compilation);
this.Parameters = base.CreateParameters(substitution);
// Comparison operators keep the 'bool' return type even when lifted. // Comparison operators keep the 'bool' return type even when lifted.
if (IsComparisonOperator(nonLiftedMethod)) if (IsComparisonOperator(nonLiftedMethod))
this.ReturnType = nonLiftedMethod.ReturnType; this.ReturnType = nonLiftedMethod.ReturnType;
else
this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution);
} }
public IList<IParameter> NonLiftedParameters { public IList<IParameter> NonLiftedParameters {
@ -1726,13 +1729,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (typeArguments != null && typeArguments.Count > 0) { if (typeArguments != null && typeArguments.Count > 0) {
if (method.TypeParameters.Count != typeArguments.Count) if (method.TypeParameters.Count != typeArguments.Count)
continue; continue;
SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments); SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments));
if (IsEligibleExtensionMethod(targetType, method, false, out inferredTypes)) if (IsEligibleExtensionMethod(targetType, method, false, out inferredTypes))
outputGroup.Add(sm); outputGroup.Add(sm);
} else { } else {
if (IsEligibleExtensionMethod(targetType, method, true, out inferredTypes)) { if (IsEligibleExtensionMethod(targetType, method, true, out inferredTypes)) {
if (substituteInferredTypes && inferredTypes != null) { if (substituteInferredTypes && inferredTypes != null) {
outputGroup.Add(new SpecializedMethod(method.DeclaringType, method, inferredTypes)); outputGroup.Add(new SpecializedMethod(method, new TypeParameterSubstitution(null, inferredTypes)));
} else { } else {
outputGroup.Add(method); outputGroup.Add(method);
} }
@ -1854,7 +1857,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (mgrr != null) { if (mgrr != null) {
OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, conversions: conversions); OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, conversions: conversions);
if (or.BestCandidate != null) { if (or.BestCandidate != null) {
if (or.BestCandidate.IsStatic && !(mgrr.TargetResult is TypeResolveResult)) if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult))
return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetResult.Type)); return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetResult.Type));
else else
return or.CreateResolveResult(mgrr.TargetResult); return or.CreateResolveResult(mgrr.TargetResult);

42
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs

@ -27,13 +27,39 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary> /// </summary>
public sealed class FindReferencedEntities : IResolveVisitorNavigator public sealed class FindReferencedEntities : IResolveVisitorNavigator
{ {
readonly Action<AstNode, IEntity> referenceFound; readonly Action<AstNode, IMember> memberReferenceFound;
readonly Action<AstNode, IType> typeReferenceFound;
/// <summary>
/// Creates a new FindReferencedEntities instance that
/// looks for entity definitions.
/// The visitor will report type definitions and member definitions (not specialized members).
/// </summary>
public FindReferencedEntities(Action<AstNode, IEntity> referenceFound) public FindReferencedEntities(Action<AstNode, IEntity> referenceFound)
{ {
if (referenceFound == null) if (referenceFound == null)
throw new ArgumentNullException("referenceFound"); throw new ArgumentNullException("referenceFound");
this.referenceFound = referenceFound; this.memberReferenceFound = (node, member) => referenceFound(node, member.MemberDefinition);
this.typeReferenceFound = (node, type) => {
var def = type.GetDefinition();
if (def != null)
referenceFound(node, def);
};
}
/// <summary>
/// Creates a new FindReferencedEntities instance that
/// looks for types and members.
/// The visitor will report parameterized types and potentially specialized members.
/// </summary>
public FindReferencedEntities(Action<AstNode, IType> typeReferenceFound, Action<AstNode, IMember> memberReferenceFound)
{
if (typeReferenceFound == null)
throw new ArgumentNullException("typeReferenceFound");
if (memberReferenceFound == null)
throw new ArgumentNullException("memberReferenceFound");
this.typeReferenceFound = typeReferenceFound;
this.memberReferenceFound = memberReferenceFound;
} }
public ResolveVisitorNavigationMode Scan(AstNode node) public ResolveVisitorNavigationMode Scan(AstNode node)
@ -48,28 +74,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
MemberResolveResult mrr = result as MemberResolveResult; MemberResolveResult mrr = result as MemberResolveResult;
if (mrr != null) { if (mrr != null) {
referenceFound(node, mrr.Member.MemberDefinition); memberReferenceFound(node, mrr.Member);
} }
TypeResolveResult trr = result as TypeResolveResult; TypeResolveResult trr = result as TypeResolveResult;
if (trr != null) { if (trr != null) {
ITypeDefinition typeDef = trr.Type.GetDefinition(); typeReferenceFound(node, trr.Type);
if (typeDef != null)
referenceFound(node, typeDef);
} }
ForEachResolveResult ferr = result as ForEachResolveResult; ForEachResolveResult ferr = result as ForEachResolveResult;
if (ferr != null) { if (ferr != null) {
Resolved(node, ferr.GetEnumeratorCall); Resolved(node, ferr.GetEnumeratorCall);
if (ferr.CurrentProperty != null) if (ferr.CurrentProperty != null)
referenceFound(node, ferr.CurrentProperty.MemberDefinition); memberReferenceFound(node, ferr.CurrentProperty);
if (ferr.MoveNextMethod != null) if (ferr.MoveNextMethod != null)
referenceFound(node, ferr.MoveNextMethod.MemberDefinition); memberReferenceFound(node, ferr.MoveNextMethod);
} }
} }
public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType)
{ {
if (conversion.IsUserDefined || conversion.IsMethodGroupConversion) { if (conversion.IsUserDefined || conversion.IsMethodGroupConversion) {
referenceFound(expression, conversion.Method.MemberDefinition); memberReferenceFound(expression, conversion.Method);
} }
} }
} }

178
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.CSharp.Refactoring;
@ -42,9 +43,42 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
#region Properties #region Properties
/// <summary> /// <summary>
/// Gets/Sets whether to find type references even if an alias is being used. /// Specifies whether to find type references even if an alias is being used.
/// Aliases may be <c>var</c> or <c>using Alias = ...;</c>.
/// </summary> /// </summary>
public bool FindTypeReferencesEvenIfAliased { get; set; } public bool FindTypeReferencesEvenIfAliased { get; set; }
/// <summary>
/// Specifies whether find references should only look for specialized matches
/// with equal type parameter substitution to the member we are searching for.
/// </summary>
public bool FindOnlySpecializedReferences { get; set; }
/// <summary>
/// If this option is enabled, find references on a overridden member
/// will find calls to the base member.
/// </summary>
public bool FindCallsThroughVirtualBaseMethod { get; set; }
/// <summary>
/// If this option is enabled, find references on a member implementing
/// an interface will also find calls to the interface.
/// </summary>
public bool FindCallsThroughInterface { get; set; }
/// <summary>
/// If this option is enabled, find references will look for all references
/// to the virtual method slot.
/// </summary>
public bool WholeVirtualSlot { get; set; }
/// <summary>
/// Specifies whether to look for references in documentation comments.
/// This will find entity references in <c>cref</c> attributes and
/// parameter references in <c>&lt;param&gt;</c> and <c>&lt;paramref&gt;</c> tags.
/// TODO: implement this feature.
/// </summary>
public bool SearchInDocumentationComments { get; set; }
#endregion #endregion
#region GetEffectiveAccessibility #region GetEffectiveAccessibility
@ -110,6 +144,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
internal string searchTerm; internal string searchTerm;
internal FindReferences findReferences;
internal ICompilation declarationCompilation; internal ICompilation declarationCompilation;
internal Accessibility accessibility; internal Accessibility accessibility;
internal ITypeDefinition topLevelTypeDefinition; internal ITypeDefinition topLevelTypeDefinition;
@ -119,6 +154,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
FindReferenceNavigator n = factory(compilation); FindReferenceNavigator n = factory(compilation);
if (n != null) { if (n != null) {
n.callback = callback; n.callback = callback;
n.findReferences = findReferences;
return n; return n;
} else { } else {
return new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Skip, null); return new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Skip, null);
@ -145,6 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
abstract class FindReferenceNavigator : IResolveVisitorNavigator abstract class FindReferenceNavigator : IResolveVisitorNavigator
{ {
internal FoundReferenceCallback callback; internal FoundReferenceCallback callback;
internal FindReferences findReferences;
internal abstract bool CanMatch(AstNode node); internal abstract bool CanMatch(AstNode node);
internal abstract bool IsMatch(ResolveResult rr); internal abstract bool IsMatch(ResolveResult rr);
@ -185,6 +222,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
if (entity == null) if (entity == null)
throw new ArgumentNullException("entity"); throw new ArgumentNullException("entity");
if (entity is IMember)
entity = NormalizeMember((IMember)entity);
Accessibility effectiveAccessibility = GetEffectiveAccessibility(entity); Accessibility effectiveAccessibility = GetEffectiveAccessibility(entity);
ITypeDefinition topLevelTypeDefinition = entity.DeclaringTypeDefinition; ITypeDefinition topLevelTypeDefinition = entity.DeclaringTypeDefinition;
while (topLevelTypeDefinition != null && topLevelTypeDefinition.DeclaringTypeDefinition != null) while (topLevelTypeDefinition != null && topLevelTypeDefinition.DeclaringTypeDefinition != null)
@ -219,7 +258,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
scope = GetSearchScopeForOperator((IMethod)entity); scope = GetSearchScopeForOperator((IMethod)entity);
break; break;
case EntityType.Constructor: case EntityType.Constructor:
IMethod ctor = (IMethod)((IMethod)entity).MemberDefinition; IMethod ctor = (IMethod)entity;
scope = FindObjectCreateReferences(ctor); scope = FindObjectCreateReferences(ctor);
additionalScope = FindChainedConstructorReferences(ctor); additionalScope = FindChainedConstructorReferences(ctor);
break; break;
@ -233,11 +272,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
scope.accessibility = effectiveAccessibility; scope.accessibility = effectiveAccessibility;
scope.declarationCompilation = entity.Compilation; scope.declarationCompilation = entity.Compilation;
scope.topLevelTypeDefinition = topLevelTypeDefinition; scope.topLevelTypeDefinition = topLevelTypeDefinition;
scope.findReferences = this;
if (additionalScope != null) { if (additionalScope != null) {
if (additionalScope.accessibility == Accessibility.None) if (additionalScope.accessibility == Accessibility.None)
additionalScope.accessibility = effectiveAccessibility; additionalScope.accessibility = effectiveAccessibility;
additionalScope.declarationCompilation = entity.Compilation; additionalScope.declarationCompilation = entity.Compilation;
additionalScope.topLevelTypeDefinition = topLevelTypeDefinition; additionalScope.topLevelTypeDefinition = topLevelTypeDefinition;
additionalScope.findReferences = this;
return new[] { scope, additionalScope }; return new[] { scope, additionalScope };
} else { } else {
return new[] { scope }; return new[] { scope };
@ -385,17 +426,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (searchTerm.Length > 9 && searchTerm.EndsWith("Attribute", StringComparison.Ordinal)) { if (searchTerm.Length > 9 && searchTerm.EndsWith("Attribute", StringComparison.Ordinal)) {
// The type might be an attribute, so we also need to look for the short form: // The type might be an attribute, so we also need to look for the short form:
string shortForm = searchTerm.Substring(0, searchTerm.Length - 9); string shortForm = searchTerm.Substring(0, searchTerm.Length - 9);
additionalScope = new SearchScope( additionalScope = FindTypeDefinitionReferences(typeDefinition, shortForm);
shortForm,
delegate (ICompilation compilation) {
ITypeDefinition imported = compilation.Import(typeDefinition);
if (imported != null)
return new FindTypeDefinitionReferencesNavigator(imported, shortForm);
else
return null;
});
} }
} }
return FindTypeDefinitionReferences(typeDefinition, searchTerm);
}
SearchScope FindTypeDefinitionReferences(ITypeDefinition typeDefinition, string searchTerm)
{
return new SearchScope( return new SearchScope(
searchTerm, searchTerm,
delegate (ICompilation compilation) { delegate (ICompilation compilation) {
@ -462,11 +500,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
SearchScope FindMemberReferences(IEntity member, Func<IMember, FindMemberReferencesNavigator> factory) SearchScope FindMemberReferences(IEntity member, Func<IMember, FindMemberReferencesNavigator> factory)
{ {
string searchTerm = member.Name; string searchTerm = member.Name;
IMember memberDefinition = ((IMember)member).MemberDefinition;
return new SearchScope( return new SearchScope(
searchTerm, searchTerm,
delegate(ICompilation compilation) { delegate(ICompilation compilation) {
IMember imported = compilation.Import(memberDefinition); IMember imported = compilation.Import((IMember)member);
return imported != null ? factory(imported) : null; return imported != null ? factory(imported) : null;
}); });
} }
@ -478,7 +515,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public FindMemberReferencesNavigator(IMember member) public FindMemberReferencesNavigator(IMember member)
{ {
this.member = member.MemberDefinition; this.member = member;
this.searchTerm = member.Name; this.searchTerm = member.Name;
} }
@ -506,8 +543,58 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && member == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(member, mrr.Member, mrr.IsVirtualCall);
}
}
IMember NormalizeMember(IMember member)
{
if (WholeVirtualSlot && member.IsOverride)
member = InheritanceHelper.GetBaseMembers(member, false).FirstOrDefault(m => !m.IsOverride) ?? member;
if (!FindOnlySpecializedReferences)
member = member.MemberDefinition;
return member;
}
bool IsMemberMatch(IMember member, IMember referencedMember, bool isVirtualCall)
{
referencedMember = NormalizeMember(referencedMember);
if (member.Equals(referencedMember))
return true;
if (!isVirtualCall)
return false;
bool isInterfaceCall = referencedMember.DeclaringTypeDefinition != null && referencedMember.DeclaringTypeDefinition.Kind == TypeKind.Interface;
if (FindCallsThroughVirtualBaseMethod && member.IsOverride && !WholeVirtualSlot && !isInterfaceCall) {
// Test if 'member' overrides 'referencedMember':
foreach (var baseMember in InheritanceHelper.GetBaseMembers(member, false)) {
if (FindOnlySpecializedReferences) {
if (baseMember.Equals(referencedMember))
return true;
} else {
if (baseMember.MemberDefinition.Equals(referencedMember))
return true;
}
if (!baseMember.IsOverride)
break;
}
return false;
} else if (FindCallsThroughInterface && isInterfaceCall) {
// Test if 'member' implements 'referencedMember':
if (FindOnlySpecializedReferences) {
return member.ImplementedInterfaceMembers.Contains(referencedMember);
} else {
return member.ImplementedInterfaceMembers.Any(m => m.MemberDefinition.Equals(referencedMember));
}
} }
return false;
}
bool PerformVirtualLookup(IMember member, IMember referencedMember)
{
if (FindCallsThroughVirtualBaseMethod && member.IsOverride && !WholeVirtualSlot)
return true;
var typeDef = referencedMember.DeclaringTypeDefinition;
return FindCallsThroughInterface && typeDef != null && typeDef.Kind == TypeKind.Interface;
} }
sealed class FindFieldReferences : FindMemberReferencesNavigator sealed class FindFieldReferences : FindMemberReferencesNavigator
@ -568,10 +655,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Find References to IEnumerator.Current #region Find References to IEnumerator.Current
SearchScope FindEnumeratorCurrentReferences(IProperty property) SearchScope FindEnumeratorCurrentReferences(IProperty property)
{ {
IProperty propertyDefinition = (IProperty)property.MemberDefinition;
return new SearchScope( return new SearchScope(
delegate(ICompilation compilation) { delegate(ICompilation compilation) {
IProperty imported = compilation.Import(propertyDefinition); IProperty imported = compilation.Import(property);
return imported != null ? new FindEnumeratorCurrentReferencesNavigator(imported) : null; return imported != null ? new FindEnumeratorCurrentReferencesNavigator(imported) : null;
}); });
} }
@ -593,7 +679,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
ForEachResolveResult ferr = rr as ForEachResolveResult; ForEachResolveResult ferr = rr as ForEachResolveResult;
return ferr != null && ferr.CurrentProperty != null && ferr.CurrentProperty.MemberDefinition == property; return ferr != null && ferr.CurrentProperty != null && findReferences.IsMemberMatch(property, ferr.CurrentProperty, true);
} }
} }
#endregion #endregion
@ -601,8 +687,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Find Method References #region Find Method References
SearchScope GetSearchScopeForMethod(IMethod method) SearchScope GetSearchScopeForMethod(IMethod method)
{ {
method = (IMethod)method.MemberDefinition;
Type specialNodeType; Type specialNodeType;
switch (method.Name) { switch (method.Name) {
case "Add": case "Add":
@ -708,18 +792,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
var ferr = rr as ForEachResolveResult; var ferr = rr as ForEachResolveResult;
if (ferr != null) { if (ferr != null) {
return IsMatch(ferr.GetEnumeratorCall) return IsMatch(ferr.GetEnumeratorCall)
|| (ferr.MoveNextMethod != null && method == ferr.MoveNextMethod.MemberDefinition); || (ferr.MoveNextMethod != null && findReferences.IsMemberMatch(method, ferr.MoveNextMethod, true));
} }
} }
var mrr = rr as MemberResolveResult; var mrr = rr as MemberResolveResult;
return mrr != null && method == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(method, mrr.Member, mrr.IsVirtualCall);
} }
internal override void NavigatorDone(CSharpAstResolver resolver, CancellationToken cancellationToken) internal override void NavigatorDone(CSharpAstResolver resolver, CancellationToken cancellationToken)
{ {
foreach (var expr in potentialMethodGroupConversions) { foreach (var expr in potentialMethodGroupConversions) {
var conversion = resolver.GetConversion(expr, cancellationToken); var conversion = resolver.GetConversion(expr, cancellationToken);
if (conversion.IsMethodGroupConversion && conversion.Method.MemberDefinition == method) { if (conversion.IsMethodGroupConversion && findReferences.IsMemberMatch(method, conversion.Method, conversion.IsVirtualMethodLookup)) {
IType targetType = resolver.GetExpectedType(expr, cancellationToken); IType targetType = resolver.GetExpectedType(expr, cancellationToken);
ResolveResult result = resolver.Resolve(expr, cancellationToken); ResolveResult result = resolver.Resolve(expr, cancellationToken);
ReportMatch(expr, new ConversionResolveResult(targetType, result, conversion)); ReportMatch(expr, new ConversionResolveResult(targetType, result, conversion));
@ -733,7 +817,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Find Indexer References #region Find Indexer References
SearchScope FindIndexerReferences(IProperty indexer) SearchScope FindIndexerReferences(IProperty indexer)
{ {
indexer = (IProperty)indexer.MemberDefinition;
return new SearchScope( return new SearchScope(
delegate (ICompilation compilation) { delegate (ICompilation compilation) {
IProperty imported = compilation.Import(indexer); IProperty imported = compilation.Import(indexer);
@ -761,7 +844,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && indexer == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(indexer, mrr.Member, mrr.IsVirtualCall);
} }
} }
#endregion #endregion
@ -834,7 +917,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
SearchScope FindOperator(IMethod op, Func<IMethod, FindReferenceNavigator> factory) SearchScope FindOperator(IMethod op, Func<IMethod, FindReferenceNavigator> factory)
{ {
op = (IMethod)op.MemberDefinition;
return new SearchScope( return new SearchScope(
delegate (ICompilation compilation) { delegate (ICompilation compilation) {
IMethod imported = compilation.Import(op); IMethod imported = compilation.Import(op);
@ -875,7 +957,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && op == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall);
} }
} }
@ -907,7 +989,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && op == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall);
} }
} }
@ -928,12 +1010,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && op == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall);
} }
public override void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) public override void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType)
{ {
if (conversion.IsUserDefined && conversion.Method.MemberDefinition == op) { if (conversion.IsUserDefined && findReferences.IsMemberMatch(op, conversion.Method, conversion.IsVirtualMethodLookup)) {
ReportMatch(expression, result); ReportMatch(expression, result);
} }
} }
@ -956,7 +1038,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
ConversionResolveResult crr = rr as ConversionResolveResult; ConversionResolveResult crr = rr as ConversionResolveResult;
return crr != null && crr.Conversion.IsUserDefined && crr.Conversion.Method.MemberDefinition == op; return crr != null && crr.Conversion.IsUserDefined
&& findReferences.IsMemberMatch(op, crr.Conversion.Method, crr.Conversion.IsVirtualMethodLookup);
} }
} }
#endregion #endregion
@ -964,7 +1047,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Find Constructor References #region Find Constructor References
SearchScope FindObjectCreateReferences(IMethod ctor) SearchScope FindObjectCreateReferences(IMethod ctor)
{ {
ctor = (IMethod)ctor.MemberDefinition;
string searchTerm = null; string searchTerm = null;
if (KnownTypeReference.GetCSharpNameByTypeCode(ctor.DeclaringTypeDefinition.KnownTypeCode) == null) { if (KnownTypeReference.GetCSharpNameByTypeCode(ctor.DeclaringTypeDefinition.KnownTypeCode) == null) {
// not a built-in type // not a built-in type
@ -1002,13 +1084,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && ctor == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(ctor, mrr.Member, mrr.IsVirtualCall);
} }
} }
SearchScope FindChainedConstructorReferences(IMethod ctor) SearchScope FindChainedConstructorReferences(IMethod ctor)
{ {
ctor = (IMethod)ctor.MemberDefinition;
SearchScope searchScope = new SearchScope( SearchScope searchScope = new SearchScope(
delegate (ICompilation compilation) { delegate (ICompilation compilation) {
IMethod imported = compilation.Import(ctor); IMethod imported = compilation.Import(ctor);
@ -1042,7 +1123,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && ctor == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(ctor, mrr.Member, mrr.IsVirtualCall);
} }
} }
#endregion #endregion
@ -1050,22 +1131,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Find Destructor References #region Find Destructor References
SearchScope GetSearchScopeForDestructor(IMethod dtor) SearchScope GetSearchScopeForDestructor(IMethod dtor)
{ {
dtor = (IMethod)dtor.MemberDefinition; var scope = new SearchScope (
string searchTerm = null;
if (KnownTypeReference.GetCSharpNameByTypeCode(dtor.DeclaringTypeDefinition.KnownTypeCode) == null) {
// not a built-in type
searchTerm = dtor.DeclaringTypeDefinition.Name;
}
return new SearchScope (
searchTerm,
delegate (ICompilation compilation) { delegate (ICompilation compilation) {
IMethod imported = compilation.Import(dtor); IMethod imported = compilation.Import(dtor);
if (imported != null) { if (imported != null) {
return new FindDestructorReferencesNavigator (imported); return new FindDestructorReferencesNavigator (imported);
} else { } else {
return null; return null;
} }
}); });
scope.accessibility = Accessibility.Private;
return scope;
} }
sealed class FindDestructorReferencesNavigator : FindReferenceNavigator sealed class FindDestructorReferencesNavigator : FindReferenceNavigator
@ -1085,7 +1161,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
internal override bool IsMatch(ResolveResult rr) internal override bool IsMatch(ResolveResult rr)
{ {
MemberResolveResult mrr = rr as MemberResolveResult; MemberResolveResult mrr = rr as MemberResolveResult;
return mrr != null && dtor == mrr.Member.MemberDefinition; return mrr != null && findReferences.IsMemberMatch(dtor, mrr.Member, mrr.IsVirtualCall);
} }
} }
#endregion #endregion

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs

@ -164,7 +164,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.TargetType, method, true, out inferredTypes)) this.TargetType, method, true, out inferredTypes))
{ {
if (substituteInferredTypes && inferredTypes != null) { if (substituteInferredTypes && inferredTypes != null) {
outputGroup.Add(new SpecializedMethod(method.DeclaringType, method, inferredTypes)); outputGroup.Add(new SpecializedMethod(method, new TypeParameterSubstitution(null, inferredTypes)));
} else { } else {
outputGroup.Add(method); outputGroup.Add(method);
} }

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

@ -778,11 +778,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
if (bestCandidate == null) if (bestCandidate == null)
return arguments; return arguments;
else
return GetArgumentsWithConversions(null);
}
IList<ResolveResult> GetArgumentsWithConversions(ResolveResult targetResolveResult)
{
var conversions = this.ArgumentConversions; var conversions = this.ArgumentConversions;
ResolveResult[] args = new ResolveResult[arguments.Length]; ResolveResult[] args = new ResolveResult[arguments.Length];
for (int i = 0; i < args.Length; i++) { for (int i = 0; i < args.Length; i++) {
var argument = arguments[i];
if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null)
argument = targetResolveResult;
if (conversions[i] == Conversion.IdentityConversion) { if (conversions[i] == Conversion.IdentityConversion) {
args[i] = arguments[i]; args[i] = argument;
} else { } else {
int parameterIndex = bestCandidate.ArgumentToParameterMap[i]; int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
IType parameterType; IType parameterType;
@ -792,9 +801,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
parameterType = SpecialType.UnknownType; parameterType = SpecialType.UnknownType;
} }
if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) { if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) {
args[i] = new CSharpResolver(compilation).ResolveCast(parameterType, arguments[i]); args[i] = new CSharpResolver(compilation).ResolveCast(parameterType, argument);
} else { } else {
args[i] = new ConversionResolveResult(parameterType, arguments[i], conversions[i]); args[i] = new ConversionResolveResult(parameterType, argument, conversions[i]);
} }
} }
} }
@ -807,7 +816,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return null; return null;
IMethod method = bestCandidate.Member as IMethod; IMethod method = bestCandidate.Member as IMethod;
if (method != null && method.TypeParameters.Count > 0) { if (method != null && method.TypeParameters.Count > 0) {
return new SpecializedMethod(method.DeclaringType, (IMethod)method.MemberDefinition, bestCandidate.InferredTypes); SpecializedMethod sm = method as SpecializedMethod;
if (sm != null) {
// Do not compose the substitutions, but merge them.
// This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce
return new SpecializedMethod(
(IMethod)method.MemberDefinition,
new TypeParameterSubstitution(sm.Substitution.ClassTypeArguments, bestCandidate.InferredTypes));
} else {
return new SpecializedMethod(method, new TypeParameterSubstitution(null, bestCandidate.InferredTypes));
}
} else { } else {
return bestCandidate.Member; return bestCandidate.Member;
} }
@ -830,9 +848,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
throw new InvalidOperationException(); throw new InvalidOperationException();
return new CSharpInvocationResolveResult( return new CSharpInvocationResolveResult(
targetResolveResult, this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType) : targetResolveResult,
member, member,
GetArgumentsWithConversions(), GetArgumentsWithConversions(targetResolveResult),
this.BestCandidateErrors, this.BestCandidateErrors,
this.IsExtensionMethodInvocation, this.IsExtensionMethodInvocation,
this.BestCandidateIsExpandedForm, this.BestCandidateIsExpandedForm,

16
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs

@ -650,7 +650,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult result = errorResult; ResolveResult result = errorResult;
if (variableInitializer.Parent is FieldDeclaration || variableInitializer.Parent is EventDeclaration) { if (variableInitializer.Parent is FieldDeclaration || variableInitializer.Parent is EventDeclaration) {
if (resolver.CurrentMember != null) { if (resolver.CurrentMember != null) {
result = new MemberResolveResult(null, resolver.CurrentMember); result = new MemberResolveResult(null, resolver.CurrentMember, false);
} }
} else { } else {
string identifier = variableInitializer.Name; string identifier = variableInitializer.Name;
@ -707,7 +707,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (resolverEnabled) { if (resolverEnabled) {
ResolveResult result = errorResult; ResolveResult result = errorResult;
if (resolver.CurrentMember != null) { if (resolver.CurrentMember != null) {
result = new MemberResolveResult(null, resolver.CurrentMember); result = new MemberResolveResult(null, resolver.CurrentMember, false);
} }
ResolveAndProcessConversion(fixedVariableInitializer.CountExpression, resolver.Compilation.FindType(KnownTypeCode.Int32)); ResolveAndProcessConversion(fixedVariableInitializer.CountExpression, resolver.Compilation.FindType(KnownTypeCode.Int32));
return result; return result;
@ -726,7 +726,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ScanChildren(member); ScanChildren(member);
if (resolver.CurrentMember != null) if (resolver.CurrentMember != null)
return new MemberResolveResult(null, resolver.CurrentMember); return new MemberResolveResult(null, resolver.CurrentMember, false);
else else
return errorResult; return errorResult;
} finally { } finally {
@ -772,7 +772,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
if (resolver.CurrentMember != null) if (resolver.CurrentMember != null)
return new MemberResolveResult(null, resolver.CurrentMember); return new MemberResolveResult(null, resolver.CurrentMember, false);
else else
return errorResult; return errorResult;
} finally { } finally {
@ -805,7 +805,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
if (resolver.CurrentMember != null) if (resolver.CurrentMember != null)
return new MemberResolveResult(null, resolver.CurrentMember); return new MemberResolveResult(null, resolver.CurrentMember, false);
else else
return errorResult; return errorResult;
} finally { } finally {
@ -885,7 +885,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (resolverEnabled && resolver.CurrentTypeDefinition != null) { if (resolverEnabled && resolver.CurrentTypeDefinition != null) {
ResolveAndProcessConversion(enumMemberDeclaration.Initializer, resolver.CurrentTypeDefinition.EnumUnderlyingType); ResolveAndProcessConversion(enumMemberDeclaration.Initializer, resolver.CurrentTypeDefinition.EnumUnderlyingType);
if (resolverEnabled && resolver.CurrentMember != null) if (resolverEnabled && resolver.CurrentMember != null)
return new MemberResolveResult(null, resolver.CurrentMember); return new MemberResolveResult(null, resolver.CurrentMember, false);
else else
return errorResult; return errorResult;
} else { } else {
@ -1310,8 +1310,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
if (resolverEnabled || !objectCreateExpression.Initializer.IsNull) { if (resolverEnabled || !objectCreateExpression.Initializer.IsNull) {
var typeResolveResult = Resolve(objectCreateExpression.Type); var typeResolveResult = Resolve(objectCreateExpression.Type);
if (typeResolveResult.IsError) if (typeResolveResult.IsError) {
ScanChildren (objectCreateExpression);
return typeResolveResult; return typeResolveResult;
}
IType type = typeResolveResult.Type; IType type = typeResolveResult.Type;
List<ResolveResult> initializerStatements = null; List<ResolveResult> initializerStatements = null;

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

@ -629,15 +629,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// Handle array types: // Handle array types:
ArrayType arrU = U as ArrayType; ArrayType arrU = U as ArrayType;
ArrayType arrV = V as ArrayType; ArrayType arrV = V as ArrayType;
ParameterizedType pV = V as ParameterizedType;
if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) { if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) {
MakeLowerBoundInference(arrU.ElementType, arrV.ElementType); MakeLowerBoundInference(arrU.ElementType, arrV.ElementType);
return; return;
} else if (arrU != null && IsIEnumerableCollectionOrList(pV) && arrU.Dimensions == 1) {
MakeLowerBoundInference(arrU.ElementType, pV.GetTypeArgument(0));
return;
} }
// Handle parameterized types: // Handle parameterized types:
ParameterizedType pV = V as ParameterizedType;
if (pV != null) { if (pV != null) {
ParameterizedType uniqueBaseType = null; ParameterizedType uniqueBaseType = null;
foreach (IType baseU in U.GetAllBaseTypes()) { foreach (IType baseU in U.GetAllBaseTypes()) {
@ -677,20 +674,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Log.Unindent(); Log.Unindent();
} }
} }
static bool IsIEnumerableCollectionOrList(ParameterizedType rt)
{
if (rt == null || rt.TypeParameterCount != 1)
return false;
switch (rt.GetDefinition().FullName) {
case "System.Collections.Generic.IList":
case "System.Collections.Generic.ICollection":
case "System.Collections.Generic.IEnumerable":
return true;
default:
return false;
}
}
#endregion #endregion
#region MakeUpperBoundInference (§7.5.2.10) #region MakeUpperBoundInference (§7.5.2.10)
@ -713,15 +696,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// Handle array types: // Handle array types:
ArrayType arrU = U as ArrayType; ArrayType arrU = U as ArrayType;
ArrayType arrV = V as ArrayType; ArrayType arrV = V as ArrayType;
ParameterizedType pU = U as ParameterizedType;
if (arrV != null && arrU != null && arrU.Dimensions == arrV.Dimensions) { if (arrV != null && arrU != null && arrU.Dimensions == arrV.Dimensions) {
MakeUpperBoundInference(arrU.ElementType, arrV.ElementType); MakeUpperBoundInference(arrU.ElementType, arrV.ElementType);
return; return;
} else if (arrV != null && IsIEnumerableCollectionOrList(pU) && arrV.Dimensions == 1) {
MakeUpperBoundInference(pU.GetTypeArgument(0), arrV.ElementType);
return;
} }
// Handle parameterized types: // Handle parameterized types:
ParameterizedType pU = U as ParameterizedType;
if (pU != null) { if (pU != null) {
ParameterizedType uniqueBaseType = null; ParameterizedType uniqueBaseType = null;
foreach (IType baseV in V.GetAllBaseTypes()) { foreach (IType baseV in V.GetAllBaseTypes()) {

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

@ -4789,5 +4789,27 @@ class B : A
Assert.AreEqual (2, provider.Data.Where (d => d.DisplayText == "Method").Count ()); Assert.AreEqual (2, provider.Data.Where (d => d.DisplayText == "Method").Count ());
} }
/// <summary>
/// Bug 3973 - code completion forgets context if text is deleted
/// </summary>
[Test()]
public void TestBug3973 ()
{
var provider = CreateProvider (
@"
using System;
class A
{
public static void Main (string[] args)
{
Console.$W$
}
}
");
Assert.IsNotNull (provider.Find ("WriteLine"), "'WriteLine' not found.");
}
} }
} }

13
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/NameContextTests.cs

@ -122,6 +122,19 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion
Assert.IsTrue (provider == null || provider.Count == 0, "provider should be empty."); Assert.IsTrue (provider == null || provider.Count == 0, "provider should be empty.");
} }
[Test()]
public void TestForLoopLocalVariableName ()
{
var provider = CodeCompletionBugTests.CreateProvider (@"class MyClass {
void Test()
{
$for (int f$
}
}");
Assert.IsTrue (provider == null || provider.Count == 0, "provider should be empty.");
}
[Test()] [Test()]
public void TestCatchExceptionName () public void TestCatchExceptionName ()
{ {

23
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/ConvertForeachToForTests.cs

@ -1,4 +1,4 @@
// //
// ConvertForeachToForTests.cs // ConvertForeachToForTests.cs
// //
// Author: // Author:
@ -64,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.ContextActions
} }
[Test()] [Test()]
public void TestEnumeration () public void TestListOfT ()
{ {
string result = RunContextAction ( string result = RunContextAction (
new ConvertForeachToFor (), new ConvertForeachToFor (),
@ -95,5 +95,24 @@ namespace ICSharpCode.NRefactory.CSharp.ContextActions
" }" + Environment.NewLine + " }" + Environment.NewLine +
"}", result); "}", result);
} }
[Test()]
public void TestEnumerableOfT ()
{
TestWrongContext (
new ConvertForeachToFor (),
"using System;" + Environment.NewLine +
"using System.Collections.Generic;" + Environment.NewLine +
"class TestClass" + Environment.NewLine +
"{" + Environment.NewLine +
" void Test (IEnumerable<string> args)" + Environment.NewLine +
" {" + Environment.NewLine +
" $foreach (var v in args) {" + Environment.NewLine +
" Console.WriteLine (v);" + Environment.NewLine +
" }" + Environment.NewLine +
" }" + Environment.NewLine +
"}"
);
}
} }
} }

66
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs

@ -33,24 +33,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
CompilationUnit compilationUnit; CompilationUnit compilationUnit;
CSharpParsedFile parsedFile; CSharpParsedFile parsedFile;
ICompilation compilation; ICompilation compilation;
FindReferences findReferences;
void Init(string code) void Init(string code)
{ {
compilationUnit = new CSharpParser().Parse(new StringReader(code), "test.cs"); compilationUnit = new CSharpParser().Parse(new StringReader(code), "test.cs");
parsedFile = compilationUnit.ToTypeSystem(); parsedFile = compilationUnit.ToTypeSystem();
compilation = TypeSystemHelper.CreateCompilation(parsedFile); compilation = TypeSystemHelper.CreateCompilation(parsedFile);
findReferences = new FindReferences();
} }
AstNode[] FindReferences(IEntity entity) AstNode[] FindReferences(IEntity entity)
{ {
var result = new List<AstNode>(); var result = new List<AstNode>();
var findReferences = new FindReferences();
var searchScopes = findReferences.GetSearchScopes(entity); var searchScopes = findReferences.GetSearchScopes(entity);
findReferences.FindReferencesInFile(searchScopes, parsedFile, compilationUnit, compilation, findReferences.FindReferencesInFile(searchScopes, parsedFile, compilationUnit, compilation,
(node, rr) => result.Add(node), CancellationToken.None); (node, rr) => result.Add(node), CancellationToken.None);
return result.OrderBy(n => n.StartLocation).ToArray(); return result.OrderBy(n => n.StartLocation).ToArray();
} }
#region Method Group
[Test] [Test]
public void FindMethodGroupReference() public void FindMethodGroupReference()
{ {
@ -107,7 +109,9 @@ class Test {
Assert.AreEqual(new [] { new TextLocation(4, 49), new TextLocation(7, 2) }, Assert.AreEqual(new [] { new TextLocation(4, 49), new TextLocation(7, 2) },
FindReferences(m_string).Select(n => n.StartLocation).ToArray()); FindReferences(m_string).Select(n => n.StartLocation).ToArray());
} }
#endregion
#region GetEnumerator
[Test] [Test]
public void FindReferenceToGetEnumeratorUsedImplicitlyInForeach() public void FindReferenceToGetEnumeratorUsedImplicitlyInForeach()
{ {
@ -129,7 +133,9 @@ class Test {
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is ForeachStatement)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is ForeachStatement));
} }
#endregion
#region Op_Implicit
[Test] [Test]
public void FindReferencesForOpImplicitInLocalVariableInitialization() public void FindReferencesForOpImplicitInLocalVariableInitialization()
{ {
@ -147,5 +153,63 @@ class Test {
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is ObjectCreateExpression)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is ObjectCreateExpression));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 6 && r is OperatorDeclaration)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 6 && r is OperatorDeclaration));
} }
#endregion
#region Inheritance
const string inheritanceTest = @"using System;
class A { public virtual void M() {} }
class B : A { public override void M() {} }
class C : A { public override void M() {} }
class Calls {
void Test(A a, B b, C c) {
a.M();
b.M();
c.M();
}
}";
[Test]
public void InheritanceTest1()
{
Init(inheritanceTest);
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B");
var BM = test.Methods.Single(m => m.Name == "M");
var actual = FindReferences(BM).ToList();
Assert.AreEqual(2, actual.Count);
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression));
}
[Test]
public void InheritanceTest2()
{
Init(inheritanceTest);
findReferences.FindCallsThroughVirtualBaseMethod = true;
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B");
var BM = test.Methods.Single(m => m.Name == "M");
var actual = FindReferences(BM).ToList();
Assert.AreEqual(3, actual.Count);
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 7 && r is InvocationExpression));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression));
}
[Test]
public void InheritanceTest3()
{
Init(inheritanceTest);
findReferences.WholeVirtualSlot = true;
var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B");
var BM = test.Methods.Single(m => m.Name == "M");
var actual = FindReferences(BM).ToList();
Assert.AreEqual(6, actual.Count);
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 2 && r is MethodDeclaration));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is MethodDeclaration));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 7 && r is InvocationExpression));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression));
Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 9 && r is InvocationExpression));
}
#endregion
} }
} }

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

@ -493,5 +493,33 @@ class Test : IVisitor<object, object> {
Assert.AreEqual("System.Object", typeArguments[0].ReflectionName); Assert.AreEqual("System.Object", typeArguments[0].ReflectionName);
Assert.AreEqual("System.Object", typeArguments[1].ReflectionName); Assert.AreEqual("System.Object", typeArguments[1].ReflectionName);
} }
[Test]
public void FirstParameterToExtensionMethod()
{
string program = @"
public class X {}
public static class Ex {
public static void F(this X x, int y, int z) {}
}
class C {
public void M() {
X a = null;
int b = 0, c = 0;
$a.F(b, c)$;
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.That(rr.IsExtensionMethodInvocation, Is.True);
Assert.That(rr.Arguments[0], Is.InstanceOf<LocalResolveResult>());
Assert.That(((LocalResolveResult)rr.Arguments[0]).Variable.Name, Is.EqualTo("a"));
Assert.That(rr.Arguments[1], Is.InstanceOf<LocalResolveResult>());
Assert.That(((LocalResolveResult)rr.Arguments[1]).Variable.Name, Is.EqualTo("b"));
Assert.That(rr.Arguments[2], Is.InstanceOf<LocalResolveResult>());
Assert.That(((LocalResolveResult)rr.Arguments[2]).Variable.Name, Is.EqualTo("c"));
Assert.That(rr.TargetResult, Is.InstanceOf<TypeResolveResult>());
}
} }
} }

13
src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs

@ -260,5 +260,18 @@ class Test {
Assert.AreEqual("Test", rr.Type.ReflectionName); Assert.AreEqual("Test", rr.Type.ReflectionName);
Assert.AreEqual(5, rr.InitializerStatements.Count); Assert.AreEqual(5, rr.InitializerStatements.Count);
} }
[Test]
public void CreateGeneric()
{
string program = @"using System;
class Test<T> where T : new() {
object x = $new T()$;
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual(TypeKind.TypeParameter, rr.Type.Kind);
Assert.AreEqual(TypeKind.TypeParameter, rr.Member.DeclaringType.Kind);
}
} }
} }

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

@ -70,6 +70,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Assert.IsTrue(success); Assert.IsTrue(success);
} }
[Test]
public void ArrayToReadOnlyList()
{
ITypeParameter tp = new DefaultTypeParameter(compilation, EntityType.Method, 0, "T");
IType stringType = compilation.FindType(KnownTypeCode.String);
ITypeDefinition readOnlyListType = compilation.FindType(KnownTypeCode.IReadOnlyListOfT).GetDefinition();
if (readOnlyListType == null)
Assert.Ignore(".NET 4.5 IReadOnlyList not available");
bool success;
Assert.AreEqual(
new [] { stringType },
ti.InferTypeArguments(new [] { tp },
new [] { new ResolveResult(new ArrayType(compilation, stringType)) },
new [] { new ParameterizedType(readOnlyListType, new [] { tp }) },
out success));
Assert.IsTrue(success);
}
[Test] [Test]
public void EnumerableToArrayInContravariantType() public void EnumerableToArrayInContravariantType()
{ {

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

@ -182,6 +182,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
IMethod m = testClass.Methods.Single(me => me.Name == "GetIndex"); IMethod m = testClass.Methods.Single(me => me.Name == "GetIndex");
Assert.AreEqual("T", m.TypeParameters[0].Name); Assert.AreEqual("T", m.TypeParameters[0].Name);
Assert.AreEqual(EntityType.Method, m.TypeParameters[0].OwnerType); Assert.AreEqual(EntityType.Method, m.TypeParameters[0].OwnerType);
Assert.AreSame(m, m.TypeParameters[0].Owner);
ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First(); ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First();
Assert.AreEqual("IEquatable", constraint.Name); Assert.AreEqual("IEquatable", constraint.Name);
@ -190,6 +191,53 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]); Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]);
} }
[Test]
public void GetIndexSpecializedTypeParameter()
{
var testClass = GetTypeDefinition(typeof(GenericClass<,>));
var methodDef = testClass.Methods.Single(me => me.Name == "GetIndex");
var m = new SpecializedMethod(methodDef, new TypeParameterSubstitution(
new[] { compilation.FindType(KnownTypeCode.Int16), compilation.FindType(KnownTypeCode.Int32) },
null
));
Assert.AreEqual("T", m.TypeParameters[0].Name);
Assert.AreEqual(EntityType.Method, m.TypeParameters[0].OwnerType);
Assert.AreSame(m, m.TypeParameters[0].Owner);
ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First();
Assert.AreEqual("IEquatable", constraint.Name);
Assert.AreEqual(1, constraint.TypeParameterCount);
Assert.AreEqual(1, constraint.TypeArguments.Count);
Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]);
}
[Test]
public void GetIndexDoubleSpecialization()
{
var testClass = GetTypeDefinition(typeof(GenericClass<,>));
// GenericClass<A, B>.GetIndex<T>
var methodDef = testClass.Methods.Single(me => me.Name == "GetIndex");
// GenericClass<B, A>.GetIndex<A>
var m1 = new SpecializedMethod(methodDef, new TypeParameterSubstitution(
new[] { testClass.TypeParameters[1], testClass.TypeParameters[0] },
new[] { testClass.TypeParameters[0] }
));
// GenericClass<string, int>.GetIndex<int>
var m2 = new SpecializedMethod(m1, new TypeParameterSubstitution(
new[] { compilation.FindType(KnownTypeCode.Int32), compilation.FindType(KnownTypeCode.String) },
null
));
// GenericClass<string, int>.GetIndex<int>
var m12 = new SpecializedMethod(methodDef, new TypeParameterSubstitution(
new[] { compilation.FindType(KnownTypeCode.String), compilation.FindType(KnownTypeCode.Int32) },
new[] { compilation.FindType(KnownTypeCode.Int32) }
));
Assert.AreEqual(m12, m2);
}
[Test] [Test]
public void GenericEnum() public void GenericEnum()
{ {

2
src/Libraries/NRefactory/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.Documentation
/// </summary> /// </summary>
public static class IdStringProvider public static class IdStringProvider
{ {
#region GetIDString #region GetIdString
/// <summary> /// <summary>
/// Gets the ID string (C# 4.0 spec, §A.3.1) for the specified entity. /// Gets the ID string (C# 4.0 spec, §A.3.1) for the specified entity.
/// </summary> /// </summary>

2
src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ITextSource.cs

@ -168,7 +168,7 @@ namespace ICSharpCode.NRefactory.Editor
/// <remarks> /// <remarks>
/// Verions can be used to efficiently detect whether a document has changed and needs reparsing; /// Verions can be used to efficiently detect whether a document has changed and needs reparsing;
/// or even to implement incremental parsers. /// or even to implement incremental parsers.
/// It is a separate class from ITextBuffer to allow the GC to collect the text buffer while /// It is a separate class from ITextSource to allow the GC to collect the text source while
/// the version checkpoint is still in use. /// the version checkpoint is still in use.
/// </remarks> /// </remarks>
public interface ITextSourceVersion public interface ITextSourceVersion

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

@ -88,11 +88,11 @@ namespace ICSharpCode.NRefactory.Semantics
return new UserDefinedConversion(false, operatorMethod, isLifted); return new UserDefinedConversion(false, operatorMethod, isLifted);
} }
public static Conversion MethodGroupConversion(IMethod chosenMethod) public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup)
{ {
if (chosenMethod == null) if (chosenMethod == null)
throw new ArgumentNullException("chosenMethod"); throw new ArgumentNullException("chosenMethod");
return new MethodGroupConv(chosenMethod); return new MethodGroupConv(chosenMethod, isVirtualMethodLookup);
} }
#endregion #endregion
@ -302,10 +302,12 @@ namespace ICSharpCode.NRefactory.Semantics
sealed class MethodGroupConv : Conversion sealed class MethodGroupConv : Conversion
{ {
readonly IMethod method; readonly IMethod method;
readonly bool isVirtualMethodLookup;
public MethodGroupConv(IMethod method) public MethodGroupConv(IMethod method, bool isVirtualMethodLookup)
{ {
this.method = method; this.method = method;
this.isVirtualMethodLookup = isVirtualMethodLookup;
} }
public override bool IsImplicit { public override bool IsImplicit {
@ -316,6 +318,10 @@ namespace ICSharpCode.NRefactory.Semantics
get { return true; } get { return true; }
} }
public override bool IsVirtualMethodLookup {
get { return isVirtualMethodLookup; }
}
public override IMethod Method { public override IMethod Method {
get { return method; } get { return method; }
} }
@ -434,6 +440,13 @@ namespace ICSharpCode.NRefactory.Semantics
get { return false; } get { return false; }
} }
/// <summary>
/// For method-group conversions, gets whether to perform a virtual method lookup at runtime.
/// </summary>
public virtual bool IsVirtualMethodLookup {
get { return false; }
}
/// <summary> /// <summary>
/// Gets whether this conversion is an anonymous function conversion. /// Gets whether this conversion is an anonymous function conversion.
/// </summary> /// </summary>

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

@ -34,12 +34,30 @@ namespace ICSharpCode.NRefactory.Semantics
readonly bool isConstant; readonly bool isConstant;
readonly object constantValue; readonly object constantValue;
readonly ResolveResult targetResult; readonly ResolveResult targetResult;
readonly bool isVirtualCall;
public MemberResolveResult(ResolveResult targetResult, IMember member) public MemberResolveResult(ResolveResult targetResult, IMember member)
: base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) : base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType)
{ {
this.targetResult = targetResult; this.targetResult = targetResult;
this.member = member; this.member = member;
var thisRR = targetResult as ThisResolveResult;
this.isVirtualCall = member.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation);
IField field = member as IField;
if (field != null) {
isConstant = field.IsConst;
if (isConstant)
constantValue = field.ConstantValue;
}
}
public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall)
: base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType)
{
this.targetResult = targetResult;
this.member = member;
this.isVirtualCall = isVirtualCall;
IField field = member as IField; IField field = member as IField;
if (field != null) { if (field != null) {
isConstant = field.IsConst; isConstant = field.IsConst;
@ -69,6 +87,13 @@ namespace ICSharpCode.NRefactory.Semantics
get { return member; } get { return member; }
} }
/// <summary>
/// Gets whether this MemberResolveResult is a virtual call.
/// </summary>
public bool IsVirtualCall {
get { return isVirtualCall; }
}
public override bool IsCompileTimeConstant { public override bool IsCompileTimeConstant {
get { return isConstant; } get { return isConstant; }
} }

12
src/Libraries/NRefactory/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs

@ -27,8 +27,18 @@ namespace ICSharpCode.NRefactory.Semantics
/// </summary> /// </summary>
public class ThisResolveResult : ResolveResult public class ThisResolveResult : ResolveResult
{ {
public ThisResolveResult(IType type) : base(type) bool causesNonVirtualInvocation;
public ThisResolveResult(IType type, bool causesNonVirtualInvocation = false) : base(type)
{ {
this.causesNonVirtualInvocation = causesNonVirtualInvocation;
}
/// <summary>
/// Gets whether this resolve result causes member invocations to be non-virtual.
/// </summary>
public bool CausesNonVirtualInvocation {
get { return causesNonVirtualInvocation; }
} }
} }
} }

17
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -246,19 +246,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
{ {
if (type == null) if (type == null)
throw new ArgumentNullException("type"); throw new ArgumentNullException("type");
ITypeDefinition def = type.GetDefinition(); if (type.Kind == TypeKind.Delegate)
if (def != null && def.Kind == TypeKind.Delegate) { return type.GetMethods(m => m.Name == "Invoke", GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault();
foreach (IMember member in def.Members) { else
if (member.Name == "Invoke" && member is IMethod) { return null;
ParameterizedType pt = type as ParameterizedType;
if (pt != null) {
return new SpecializedMethod(pt, (IMethod)member);
}
return (IMethod)member;
}
}
}
return null;
} }
#endregion #endregion

1
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/IMember.cs

@ -20,6 +20,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.TypeSystem namespace ICSharpCode.NRefactory.TypeSystem
{ {

32
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.Documentation; using ICSharpCode.NRefactory.Documentation;
using ICSharpCode.NRefactory.Utils; using ICSharpCode.NRefactory.Utils;
@ -65,18 +66,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
} }
public override DocumentationComment Documentation {
get {
IUnresolvedDocumentationProvider docProvider = unresolved.ParsedFile as IUnresolvedDocumentationProvider;
if (docProvider != null) {
var doc = docProvider.GetDocumentation(unresolved, this);
if (doc != null)
return doc;
}
return base.Documentation;
}
}
IList<IMember> FindImplementedInterfaceMembers() IList<IMember> FindImplementedInterfaceMembers()
{ {
if (unresolved.IsExplicitInterfaceImplementation) { if (unresolved.IsExplicitInterfaceImplementation) {
@ -87,8 +76,25 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
result.Add(member); result.Add(member);
} }
return result.ToArray(); return result.ToArray();
} else if (unresolved.IsStatic) {
return EmptyList<IMember>.Instance;
} else { } else {
throw new NotImplementedException(); // TODO: implement interface member mappings correctly
return InheritanceHelper.GetBaseMembers(this, true)
.Where(m => m.DeclaringTypeDefinition != null && m.DeclaringTypeDefinition.Kind == TypeKind.Interface)
.ToArray();
}
}
public override DocumentationComment Documentation {
get {
IUnresolvedDocumentationProvider docProvider = unresolved.ParsedFile as IUnresolvedDocumentationProvider;
if (docProvider != null) {
var doc = docProvider.GetDocumentation(unresolved, this);
if (doc != null)
return doc;
}
return base.Documentation;
} }
} }

2
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs

@ -261,7 +261,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
if (this.HasDefaultConstructorConstraint || this.HasValueTypeConstraint) { if (this.HasDefaultConstructorConstraint || this.HasValueTypeConstraint) {
if (filter == null || filter(dummyConstructor)) { if (filter == null || filter(dummyConstructor)) {
var resolvedCtor = GetDummyConstructor(compilation); var resolvedCtor = GetDummyConstructor(compilation);
IMethod m = new SpecializedMethod(this, resolvedCtor, EmptyList<IType>.Instance); IMethod m = new SpecializedMethod(resolvedCtor, TypeParameterSubstitution.Identity) { DeclaringType = this };
return new [] { m }; return new [] { m };
} }
} }

3
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs

@ -781,7 +781,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
IType coClass = ComHelper.GetCoClass(this); IType coClass = ComHelper.GetCoClass(this);
using (var busyLock = BusyManager.Enter(this)) { using (var busyLock = BusyManager.Enter(this)) {
if (busyLock.Success) { if (busyLock.Success) {
return coClass.GetConstructors(filter, options).Select(m => new SpecializedMethod(this, m)); return coClass.GetConstructors(filter, options)
.Select(m => new SpecializedMethod(m, TypeParameterSubstitution.Identity) { DeclaringType = this });
} }
} }
return EmptyList<IMethod>.Instance; return EmptyList<IMethod>.Instance;

10
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs

@ -132,7 +132,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
else else
substitution = new TypeParameterSubstitution(null, methodTypeArguments); substitution = new TypeParameterSubstitution(null, methodTypeArguments);
} }
yield return new SpecializedMethod(baseType, m, methodTypeArguments, substitution); yield return new SpecializedMethod(m, substitution);
} }
} else { } else {
foreach (IMethod m in declaredMethods) { foreach (IMethod m in declaredMethods) {
@ -162,7 +162,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ParameterizedType pt = baseType as ParameterizedType; ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) { if (pt != null) {
var substitution = pt.GetSubstitution(); var substitution = pt.GetSubstitution();
return declaredCtors.Select(m => new SpecializedMethod(pt, m, null, substitution)); return declaredCtors.Select(m => new SpecializedMethod(m, substitution) { DeclaringType = pt });
} else { } else {
return declaredCtors; return declaredCtors;
} }
@ -189,7 +189,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ParameterizedType pt = baseType as ParameterizedType; ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) { if (pt != null) {
var substitution = pt.GetSubstitution(); var substitution = pt.GetSubstitution();
return declaredProperties.Select(m => new SpecializedProperty(pt, m, substitution)); return declaredProperties.Select(m => new SpecializedProperty(m, substitution) { DeclaringType = pt });
} else { } else {
return declaredProperties; return declaredProperties;
} }
@ -216,7 +216,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ParameterizedType pt = baseType as ParameterizedType; ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) { if (pt != null) {
var substitution = pt.GetSubstitution(); var substitution = pt.GetSubstitution();
return declaredFields.Select(m => new SpecializedField(pt, m, substitution)); return declaredFields.Select(m => new SpecializedField(m, substitution) { DeclaringType = pt });
} else { } else {
return declaredFields; return declaredFields;
} }
@ -243,7 +243,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ParameterizedType pt = baseType as ParameterizedType; ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) { if (pt != null) {
var substitution = pt.GetSubstitution(); var substitution = pt.GetSubstitution();
return declaredEvents.Select(m => new SpecializedEvent(pt, m, substitution)); return declaredEvents.Select(m => new SpecializedEvent(m, substitution) { DeclaringType = pt });
} else { } else {
return declaredEvents; return declaredEvents;
} }

23
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs

@ -27,18 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
readonly IEvent eventDefinition; readonly IEvent eventDefinition;
public SpecializedEvent(IType declaringType, IEvent eventDefinition) public SpecializedEvent(IEvent eventDefinition, TypeParameterSubstitution substitution)
: base(declaringType, eventDefinition) : base(eventDefinition)
{ {
this.eventDefinition = eventDefinition; AddSubstitution(substitution);
Initialize(GetSubstitution(declaringType)); this.eventDefinition = (IEvent)base.MemberDefinition;
}
internal SpecializedEvent(IType declaringType, IEvent eventDefinition, TypeVisitor substitution)
: base(declaringType, eventDefinition)
{
this.eventDefinition = eventDefinition;
Initialize(substitution);
} }
public bool CanAdd { public bool CanAdd {
@ -53,16 +46,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return eventDefinition.CanInvoke; } get { return eventDefinition.CanInvoke; }
} }
IMethod addAccessor, removeAccessor, invokeAccessor;
public IMethod AddAccessor { public IMethod AddAccessor {
get { return WrapAccessor(eventDefinition.AddAccessor); } get { return WrapAccessor(ref this.addAccessor, eventDefinition.AddAccessor); }
} }
public IMethod RemoveAccessor { public IMethod RemoveAccessor {
get { return WrapAccessor(eventDefinition.RemoveAccessor); } get { return WrapAccessor(ref this.removeAccessor, eventDefinition.RemoveAccessor); }
} }
public IMethod InvokeAccessor { public IMethod InvokeAccessor {
get { return WrapAccessor(eventDefinition.InvokeAccessor); } get { return WrapAccessor(ref this.invokeAccessor, eventDefinition.InvokeAccessor); }
} }
} }
} }

15
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs

@ -27,18 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
readonly IField fieldDefinition; readonly IField fieldDefinition;
public SpecializedField(IType declaringType, IField fieldDefinition) public SpecializedField(IField fieldDefinition, TypeParameterSubstitution substitution)
: base(declaringType, fieldDefinition) : base(fieldDefinition)
{ {
this.fieldDefinition = fieldDefinition; AddSubstitution(substitution);
Initialize(GetSubstitution(declaringType)); this.fieldDefinition = (IField)base.MemberDefinition;
}
internal SpecializedField(IType declaringType, IField fieldDefinition, TypeVisitor substitution)
: base(declaringType, fieldDefinition)
{
this.fieldDefinition = fieldDefinition;
Initialize(substitution);
} }
public bool IsReadOnly { public bool IsReadOnly {

184
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -31,58 +32,115 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
/// </summary> /// </summary>
public abstract class SpecializedMember : IMember public abstract class SpecializedMember : IMember
{ {
readonly IType declaringType;
readonly IMember memberDefinition; readonly IMember memberDefinition;
TypeParameterSubstitution substitution;
IType declaringType;
IType returnType; IType returnType;
protected SpecializedMember(IType declaringType, IMember memberDefinition) protected SpecializedMember(IMember memberDefinition)
{ {
if (declaringType == null)
throw new ArgumentNullException("declaringType");
if (memberDefinition == null) if (memberDefinition == null)
throw new ArgumentNullException("memberDefinition"); throw new ArgumentNullException("memberDefinition");
this.declaringType = declaringType; SpecializedMember sm = memberDefinition as SpecializedMember;
this.memberDefinition = memberDefinition; if (sm != null) {
this.memberDefinition = sm.memberDefinition;
this.substitution = sm.substitution;
} else {
this.memberDefinition = memberDefinition;
this.substitution = TypeParameterSubstitution.Identity;
}
} }
protected virtual void Initialize(TypeVisitor substitution) /// <summary>
/// Performs a substitution. This method may only be called by constructors in derived classes.
/// </summary>
protected void AddSubstitution(TypeParameterSubstitution newSubstitution)
{ {
this.returnType = Substitute(memberDefinition.ReturnType, substitution); Debug.Assert(declaringType == null);
Debug.Assert(returnType == null);
this.substitution = TypeParameterSubstitution.Compose(newSubstitution, this.substitution);
} }
public virtual IMemberReference ToMemberReference() public static SpecializedMember Create(IMember memberDefinition, TypeParameterSubstitution substitution)
{ {
return new SpecializingMemberReference(declaringType.ToTypeReference(), memberDefinition.ToMemberReference()); if (memberDefinition == null) {
return null;
} else if (memberDefinition is IMethod) {
return new SpecializedMethod((IMethod)memberDefinition, substitution);
} else if (memberDefinition is IProperty) {
return new SpecializedProperty((IProperty)memberDefinition, substitution);
} else if (memberDefinition is IField) {
return new SpecializedField((IField)memberDefinition, substitution);
} else if (memberDefinition is IEvent) {
return new SpecializedEvent((IEvent)memberDefinition, substitution);
} else {
throw new NotSupportedException("Unknown IMember: " + memberDefinition);
}
} }
internal static TypeVisitor GetSubstitution(IType declaringType) public IMemberReference ToMemberReference()
{ {
ParameterizedType pt = declaringType as ParameterizedType; return new SpecializingMemberReference(
if (pt != null) memberDefinition.ToMemberReference(),
return pt.GetSubstitution(); ToTypeReference(substitution.ClassTypeArguments),
else ToTypeReference(substitution.MethodTypeArguments));
return null;
} }
internal static IType Substitute(IType type, TypeVisitor substitution) static IList<ITypeReference> ToTypeReference(IList<IType> typeArguments)
{ {
if (substitution == null) if (typeArguments == null)
return type; return null;
else else
return type.AcceptVisitor(substitution); return typeArguments.Select(t => t.ToTypeReference()).ToArray();
} }
internal IMethod WrapAccessor(IMethod accessorDefinition) internal IMethod WrapAccessor(ref IMethod cachingField, IMethod accessorDefinition)
{ {
if (accessorDefinition == null) if (accessorDefinition == null)
return null; return null;
var result = LazyInit.VolatileRead(ref cachingField);
if (result != null)
return result;
else else
return new SpecializedMethod(declaringType, accessorDefinition); return LazyInit.GetOrSet(ref cachingField, new SpecializedMethod(accessorDefinition, substitution));
}
/// <summary>
/// Gets the substitution belonging to this specialized member.
/// </summary>
public TypeParameterSubstitution Substitution {
get { return substitution; }
} }
public IType DeclaringType { public IType DeclaringType {
get { return declaringType; } get {
var result = LazyInit.VolatileRead(ref this.declaringType);
if (result != null)
return result;
IType definitionDeclaringType = memberDefinition.DeclaringType;
ITypeDefinition definitionDeclaringTypeDef = definitionDeclaringType as ITypeDefinition;
if (definitionDeclaringTypeDef != null && definitionDeclaringType.TypeParameterCount > 0) {
if (substitution.ClassTypeArguments != null && substitution.ClassTypeArguments.Count == definitionDeclaringType.TypeParameterCount) {
result = new ParameterizedType(definitionDeclaringTypeDef, substitution.ClassTypeArguments);
} else {
result = new ParameterizedType(definitionDeclaringTypeDef, definitionDeclaringTypeDef.TypeParameters).AcceptVisitor(substitution);
}
} else {
result = definitionDeclaringType.AcceptVisitor(substitution);
}
return LazyInit.GetOrSet(ref this.declaringType, result);
}
internal set {
// This setter is used as an optimization when the code constructing
// the SpecializedMember already knows the declaring type.
Debug.Assert(this.declaringType == null);
Debug.Assert(value != null);
// As this setter is used only during construction before the member is published
// to other threads, we don't need a volatile write.
this.declaringType = value;
}
} }
public IMember MemberDefinition { public IMember MemberDefinition {
@ -94,8 +152,21 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
public IType ReturnType { public IType ReturnType {
get { return returnType; } get {
protected set { returnType = value; } var result = LazyInit.VolatileRead(ref this.returnType);
if (result != null)
return result;
else
return LazyInit.GetOrSet(ref this.returnType, memberDefinition.ReturnType.AcceptVisitor(substitution));
}
protected set {
// This setter is used for LiftedUserDefinedOperator, a special case of specialized member
// (not a normal type parameter substitution).
// As this setter is used only during construction before the member is published
// to other threads, we don't need a volatile write.
this.returnType = value;
}
} }
public bool IsVirtual { public bool IsVirtual {
@ -143,19 +214,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
var definitionImplementations = memberDefinition.ImplementedInterfaceMembers; var definitionImplementations = memberDefinition.ImplementedInterfaceMembers;
IMember[] result = new IMember[definitionImplementations.Count]; IMember[] result = new IMember[definitionImplementations.Count];
for (int i = 0; i < result.Length; i++) { for (int i = 0; i < result.Length; i++) {
result[i] = Specialize(definitionImplementations[i]); result[i] = SpecializedMember.Create(definitionImplementations[i], substitution);
} }
return result; return result;
} }
/// <summary>
/// Specialize another member using the same type arguments as this member.
/// </summary>
protected virtual IMember Specialize(IMember otherMember)
{
return SpecializingMemberReference.CreateSpecializedMember(declaringType, memberDefinition, null);
}
public bool IsExplicitInterfaceImplementation { public bool IsExplicitInterfaceImplementation {
get { return memberDefinition.IsExplicitInterfaceImplementation; } get { return memberDefinition.IsExplicitInterfaceImplementation; }
} }
@ -241,13 +304,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
SpecializedMember other = obj as SpecializedMember; SpecializedMember other = obj as SpecializedMember;
if (other == null) if (other == null)
return false; return false;
return this.declaringType.Equals(other.declaringType) && this.memberDefinition.Equals(other.memberDefinition); return this.memberDefinition.Equals(other.memberDefinition) && this.substitution.Equals(other.substitution);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
unchecked { unchecked {
return 1000000007 * declaringType.GetHashCode() + 1000000009 * memberDefinition.GetHashCode(); return 1000000007 * memberDefinition.GetHashCode() + 1000000009 * substitution.GetHashCode();
} }
} }
@ -256,11 +319,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
StringBuilder b = new StringBuilder("["); StringBuilder b = new StringBuilder("[");
b.Append(GetType().Name); b.Append(GetType().Name);
b.Append(' '); b.Append(' ');
b.Append(declaringType.ToString()); b.Append(this.DeclaringType.ToString());
b.Append('.'); b.Append('.');
b.Append(this.Name); b.Append(this.Name);
b.Append(':'); b.Append(':');
b.Append(returnType.ToString()); b.Append(this.ReturnType.ToString());
b.Append(']'); b.Append(']');
return b.ToString(); return b.ToString();
} }
@ -270,36 +333,48 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
IList<IParameter> parameters; IList<IParameter> parameters;
protected SpecializedParameterizedMember(IType declaringType, IParameterizedMember memberDefinition) protected SpecializedParameterizedMember(IParameterizedMember memberDefinition)
: base(declaringType, memberDefinition) : base(memberDefinition)
{ {
} }
protected override void Initialize(TypeVisitor substitution) public IList<IParameter> Parameters {
{ get {
base.Initialize(substitution); var result = LazyInit.VolatileRead(ref this.parameters);
if (result != null)
return result;
else
return LazyInit.GetOrSet(ref this.parameters, CreateParameters(this.Substitution));
}
protected set {
// This setter is used for LiftedUserDefinedOperator, a special case of specialized member
// (not a normal type parameter substitution).
// As this setter is used only during construction before the member is published
// to other threads, we don't need a volatile write.
this.parameters = value;
}
}
protected IList<IParameter> CreateParameters(TypeVisitor substitution)
{
var paramDefs = ((IParameterizedMember)this.MemberDefinition).Parameters; var paramDefs = ((IParameterizedMember)this.MemberDefinition).Parameters;
if (paramDefs.Count == 0) { if (paramDefs.Count == 0) {
this.parameters = EmptyList<IParameter>.Instance; return EmptyList<IParameter>.Instance;
} else { } else {
var parameters = new IParameter[paramDefs.Count]; var parameters = new IParameter[paramDefs.Count];
for (int i = 0; i < parameters.Length; i++) { for (int i = 0; i < parameters.Length; i++) {
IType newType = Substitute(paramDefs[i].Type, substitution); IType newType = paramDefs[i].Type.AcceptVisitor(substitution);
if (newType != paramDefs[i].Type) { if (newType != paramDefs[i].Type) {
parameters[i] = new SpecializedParameter(paramDefs[i], newType); parameters[i] = new SpecializedParameter(paramDefs[i], newType);
} else { } else {
parameters[i] = paramDefs[i]; parameters[i] = paramDefs[i];
} }
} }
this.parameters = Array.AsReadOnly(parameters); return Array.AsReadOnly(parameters);
} }
} }
public IList<IParameter> Parameters {
get { return parameters; }
}
public override string ToString() public override string ToString()
{ {
StringBuilder b = new StringBuilder("["); StringBuilder b = new StringBuilder("[");
@ -309,9 +384,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
b.Append('.'); b.Append('.');
b.Append(this.Name); b.Append(this.Name);
b.Append('('); b.Append('(');
for (int i = 0; i < parameters.Count; i++) { for (int i = 0; i < this.Parameters.Count; i++) {
if (i > 0) b.Append(", "); if (i > 0) b.Append(", ");
b.Append(parameters[i].ToString()); b.Append(this.Parameters[i].ToString());
} }
b.Append("):"); b.Append("):");
b.Append(this.ReturnType.ToString()); b.Append(this.ReturnType.ToString());
@ -326,7 +401,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
public SpecializedParameter(IParameter originalParameter, IType newType) public SpecializedParameter(IParameter originalParameter, IType newType)
{ {
this.originalParameter = originalParameter; if (originalParameter is SpecializedParameter)
this.originalParameter = ((SpecializedParameter)originalParameter).originalParameter;
else
this.originalParameter = originalParameter;
this.newType = newType; this.newType = newType;
} }

103
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs

@ -32,50 +32,36 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
public class SpecializedMethod : SpecializedParameterizedMember, IMethod public class SpecializedMethod : SpecializedParameterizedMember, IMethod
{ {
readonly IMethod methodDefinition; readonly IMethod methodDefinition;
readonly IList<IType> typeArguments; readonly ITypeParameter[] specializedTypeParameters;
readonly IList<ITypeParameter> specializedTypeParameters;
public SpecializedMethod(IType declaringType, IMethod methodDefinition, IList<IType> typeArguments = null) public SpecializedMethod(IMethod methodDefinition, TypeParameterSubstitution substitution)
: this(declaringType, methodDefinition, typeArguments, GetSubstitution(declaringType, typeArguments)) : base(methodDefinition)
{ {
} // The base ctor might have unpacked a SpecializedMember
// (in case we are specializing an already-specialized method)
internal protected SpecializedMethod(IType declaringType, IMethod methodDefinition, IList<IType> typeArguments, TypeVisitor substitution) methodDefinition = (IMethod)base.MemberDefinition;
: base(declaringType, methodDefinition)
{
if (declaringType == null)
throw new ArgumentNullException("declaringType");
if (methodDefinition == null)
throw new ArgumentNullException("methodDefinition");
this.methodDefinition = methodDefinition; this.methodDefinition = methodDefinition;
this.typeArguments = typeArguments ?? EmptyList<IType>.Instance;
if (methodDefinition.TypeParameters.Any(ConstraintNeedsSpecialization)) { if (methodDefinition.TypeParameters.Any(ConstraintNeedsSpecialization)) {
// The method is generic, and we need to specialize the type parameters // The method is generic, and we need to specialize the type parameters
specializedTypeParameters = new ITypeParameter[methodDefinition.TypeParameters.Count]; specializedTypeParameters = new ITypeParameter[methodDefinition.TypeParameters.Count];
for (int i = 0; i < specializedTypeParameters.Count; i++) { for (int i = 0; i < specializedTypeParameters.Length; i++) {
ITypeParameter tp = methodDefinition.TypeParameters[i]; ITypeParameter tp = methodDefinition.TypeParameters[i];
if (ConstraintNeedsSpecialization(tp)) if (ConstraintNeedsSpecialization(tp))
tp = new SpecializedTypeParameter(tp, this, substitution); tp = new SpecializedTypeParameter(tp, this);
specializedTypeParameters[i] = tp; specializedTypeParameters[i] = tp;
} }
// add substitution that replaces the base method's type parameters with our specialized version
AddSubstitution(new TypeParameterSubstitution(null, specializedTypeParameters));
} }
if (typeArguments != null && typeArguments.Count > 0) { // Add the main substitution after the method type parameter specialization.
if (typeArguments.Count != methodDefinition.TypeParameters.Count) AddSubstitution(substitution);
throw new ArgumentException("Incorrect number of type arguments"); if (specializedTypeParameters != null) {
} else if (specializedTypeParameters != null) { // Set the substitution on the type parameters to the final composed substitution
// No type arguments were specified, but the method is generic. foreach (var tp in specializedTypeParameters.OfType<SpecializedTypeParameter>()) {
// -> substitute original type parameters with the specialized ones if (tp.Owner == this)
substitution = GetSubstitution(declaringType, specializedTypeParameters.ToArray<IType>()); tp.substitution = base.Substitution;
for (int i = 0; i < specializedTypeParameters.Count; i++) {
if (ConstraintNeedsSpecialization(methodDefinition.TypeParameters[i])) {
((SpecializedTypeParameter)specializedTypeParameters[i]).substitution = substitution;
}
} }
} }
Initialize(substitution);
} }
static bool ConstraintNeedsSpecialization(ITypeParameter tp) static bool ConstraintNeedsSpecialization(ITypeParameter tp)
@ -95,53 +81,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
return null; return null;
} }
public override IMemberReference ToMemberReference()
{
return new SpecializingMemberReference(
this.DeclaringType.ToTypeReference(),
this.MemberDefinition.ToMemberReference(),
typeArguments.Select(ta => ta.ToTypeReference()).ToList()
);
}
protected override IMember Specialize(IMember otherMember)
{
return SpecializingMemberReference.CreateSpecializedMember(this.DeclaringType, this.MemberDefinition, typeArguments);
}
/// <summary> /// <summary>
/// Gets the type arguments passed to this method. /// Gets the type arguments passed to this method.
/// If only the type parameters for the class were specified and the generic method /// If only the type parameters for the class were specified and the generic method
/// itself is not specialized yet, this property will return an empty list. /// itself is not specialized yet, this property will return an empty list.
/// </summary> /// </summary>
public IList<IType> TypeArguments { public IList<IType> TypeArguments {
get { return typeArguments; } get { return this.Substitution.MethodTypeArguments ?? EmptyList<IType>.Instance; }
}
public override int GetHashCode()
{
int hashCode = base.GetHashCode();
unchecked {
for (int i = 0; i < typeArguments.Count; i++) {
hashCode *= 362631391;
hashCode += typeArguments[i].GetHashCode();
}
}
return hashCode;
}
public override bool Equals(object obj)
{
SpecializedMethod other = obj as SpecializedMethod;
if (!base.Equals(other))
return false;
if (typeArguments.Count != other.typeArguments.Count)
return false;
for (int i = 0; i < typeArguments.Count; i++) {
if (!typeArguments[i].Equals(other.typeArguments[i]))
return false;
}
return true;
} }
public IList<IUnresolvedMethod> Parts { public IList<IUnresolvedMethod> Parts {
@ -182,11 +128,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
b.Append(this.DeclaringType.ToString()); b.Append(this.DeclaringType.ToString());
b.Append('.'); b.Append('.');
b.Append(this.Name); b.Append(this.Name);
if (typeArguments.Count > 0) { if (this.TypeArguments.Count > 0) {
b.Append('['); b.Append('[');
for (int i = 0; i < typeArguments.Count; i++) { for (int i = 0; i < this.TypeArguments.Count; i++) {
if (i > 0) b.Append(", "); if (i > 0) b.Append(", ");
b.Append(typeArguments[i].ToString()); b.Append(this.TypeArguments[i].ToString());
} }
b.Append(']'); b.Append(']');
} }
@ -205,14 +151,15 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
readonly ITypeParameter baseTp; readonly ITypeParameter baseTp;
// not readonly: The substition may be replaced at the end of SpecializedMethod constructor // The substition is set at the end of SpecializedMethod constructor
internal TypeVisitor substitution; internal TypeVisitor substitution;
public SpecializedTypeParameter(ITypeParameter baseTp, IMethod specializedOwner, TypeVisitor substitution) public SpecializedTypeParameter(ITypeParameter baseTp, IMethod specializedOwner)
: base(specializedOwner, baseTp.Index, baseTp.Name, baseTp.Variance, baseTp.Attributes, baseTp.Region) : base(specializedOwner, baseTp.Index, baseTp.Name, baseTp.Variance, baseTp.Attributes, baseTp.Region)
{ {
// We don't have to consider already-specialized baseTps because
// we read the baseTp directly from the unpacked memberDefinition.
this.baseTp = baseTp; this.baseTp = baseTp;
this.substitution = substitution;
} }
public override int GetHashCode() public override int GetHashCode()

21
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs

@ -27,18 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{ {
readonly IProperty propertyDefinition; readonly IProperty propertyDefinition;
public SpecializedProperty(IType declaringType, IProperty propertyDefinition) public SpecializedProperty(IProperty propertyDefinition, TypeParameterSubstitution substitution)
: base(declaringType, propertyDefinition) : base(propertyDefinition)
{ {
this.propertyDefinition = propertyDefinition; AddSubstitution(substitution);
Initialize(GetSubstitution(declaringType)); this.propertyDefinition = (IProperty)base.MemberDefinition;
}
internal SpecializedProperty(IType declaringType, IProperty propertyDefinition, TypeVisitor substitution)
: base(declaringType, propertyDefinition)
{
this.propertyDefinition = propertyDefinition;
Initialize(substitution);
} }
public bool CanGet { public bool CanGet {
@ -49,12 +42,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return propertyDefinition.CanSet; } get { return propertyDefinition.CanSet; }
} }
IMethod getter, setter;
public IMethod Getter { public IMethod Getter {
get { return WrapAccessor(propertyDefinition.Getter); } get { return WrapAccessor(ref this.getter, propertyDefinition.Getter); }
} }
public IMethod Setter { public IMethod Setter {
get { return WrapAccessor(propertyDefinition.Setter); } get { return WrapAccessor(ref this.setter, propertyDefinition.Setter); }
} }
public bool IsIndexer { public bool IsIndexer {

45
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs

@ -24,50 +24,29 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
[Serializable] [Serializable]
public sealed class SpecializingMemberReference : IMemberReference public sealed class SpecializingMemberReference : IMemberReference
{ {
ITypeReference declaringTypeReference;
IMemberReference memberDefinitionReference; IMemberReference memberDefinitionReference;
IList<ITypeReference> typeArgumentReferences; IList<ITypeReference> classTypeArgumentReferences;
IList<ITypeReference> methodTypeArgumentReferences;
public SpecializingMemberReference(ITypeReference declaringTypeReference, IMemberReference memberDefinitionReference, IList<ITypeReference> typeArgumentReferences = null) public SpecializingMemberReference(IMemberReference memberDefinitionReference, IList<ITypeReference> classTypeArgumentReferences = null, IList<ITypeReference> methodTypeArgumentReferences = null)
{ {
if (declaringTypeReference == null)
throw new ArgumentNullException("declaringTypeReference");
if (memberDefinitionReference == null) if (memberDefinitionReference == null)
throw new ArgumentNullException("memberDefinitionReference"); throw new ArgumentNullException("memberDefinitionReference");
this.declaringTypeReference = declaringTypeReference;
this.memberDefinitionReference = memberDefinitionReference; this.memberDefinitionReference = memberDefinitionReference;
this.typeArgumentReferences = typeArgumentReferences; this.classTypeArgumentReferences = classTypeArgumentReferences;
this.methodTypeArgumentReferences = methodTypeArgumentReferences;
} }
public IMember Resolve(ITypeResolveContext context) public IMember Resolve(ITypeResolveContext context)
{ {
var declaringType = declaringTypeReference.Resolve(context);
var memberDefinition = memberDefinitionReference.Resolve(context); var memberDefinition = memberDefinitionReference.Resolve(context);
IType[] typeArguments = null; return SpecializedMember.Create(
if (typeArgumentReferences != null) { memberDefinition,
typeArguments = new IType[typeArgumentReferences.Count]; new TypeParameterSubstitution(
for (int i = 0; i < typeArguments.Length; i++) { classTypeArgumentReferences != null ? classTypeArgumentReferences.Resolve(context) : null,
typeArguments[i] = typeArgumentReferences[i].Resolve(context); methodTypeArgumentReferences != null ? methodTypeArgumentReferences.Resolve(context) : null
} )
} );
return CreateSpecializedMember(declaringType, memberDefinition, typeArguments);
}
internal static IMember CreateSpecializedMember(IType declaringType, IMember memberDefinition, IList<IType> typeArguments)
{
if (memberDefinition == null) {
return null;
} else if (memberDefinition is IMethod) {
return new SpecializedMethod(declaringType, (IMethod)memberDefinition, typeArguments);
} else if (memberDefinition is IProperty) {
return new SpecializedProperty(declaringType, (IProperty)memberDefinition);
} else if (memberDefinition is IField) {
return new SpecializedField(declaringType, (IField)memberDefinition);
} else if (memberDefinition is IEvent) {
return new SpecializedEvent(declaringType, (IEvent)memberDefinition);
} else {
throw new NotSupportedException("Unknown IMember: " + memberDefinition);
}
} }
} }
} }

94
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs

@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
/// </summary> /// </summary>
public class TypeParameterSubstitution : TypeVisitor public class TypeParameterSubstitution : TypeVisitor
{ {
/// <summary>
/// The identity function.
/// </summary>
public static readonly TypeParameterSubstitution Identity = new TypeParameterSubstitution(null, null);
readonly IList<IType> classTypeArguments; readonly IList<IType> classTypeArguments;
readonly IList<IType> methodTypeArguments; readonly IList<IType> methodTypeArguments;
@ -47,6 +52,95 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
this.methodTypeArguments = methodTypeArguments; this.methodTypeArguments = methodTypeArguments;
} }
/// <summary>
/// Gets the list of class type arguments.
/// Returns <c>null</c> if this substitution keeps class type parameter unmodified.
/// </summary>
public IList<IType> ClassTypeArguments {
get { return classTypeArguments; }
}
public IList<IType> MethodTypeArguments {
get { return methodTypeArguments; }
}
#region Compose
/// <summary>
/// Computes a single TypeParameterSubstitution so that for all types <c>t</c>:
/// <c>t.AcceptVisitor(Compose(g, f)) equals t.AcceptVisitor(f).AcceptVisitor(g)</c>
/// </summary>
/// <remarks>If you consider type parameter substitution to be a function, this is function composition.</remarks>
public static TypeParameterSubstitution Compose(TypeParameterSubstitution g, TypeParameterSubstitution f)
{
if (g == null)
return f;
if (f == null || (f.classTypeArguments == null && f.methodTypeArguments == null))
return g;
// The composition is a copy of 'f', with 'g' applied on the array elements.
// If 'f' has a null list (keeps type parameters unmodified), we have to treat it as
// the identity function, and thus use the list from 'g'.
var classTypeArguments = f.classTypeArguments != null ? GetComposedTypeArguments(f.classTypeArguments, g) : g.classTypeArguments;
var methodTypeArguments = f.methodTypeArguments != null ? GetComposedTypeArguments(f.methodTypeArguments, g) : g.methodTypeArguments;
return new TypeParameterSubstitution(classTypeArguments, methodTypeArguments);
}
static IList<IType> GetComposedTypeArguments(IList<IType> input, TypeParameterSubstitution substitution)
{
IType[] result = new IType[input.Count];
for (int i = 0; i < result.Length; i++) {
result[i] = input[i].AcceptVisitor(substitution);
}
return result;
}
#endregion
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
TypeParameterSubstitution other = obj as TypeParameterSubstitution;
if (other == null)
return false;
return TypeListEquals(classTypeArguments, other.classTypeArguments)
&& TypeListEquals(methodTypeArguments, other.methodTypeArguments);
}
public override int GetHashCode()
{
unchecked {
return 1124131 * TypeListHashCode(classTypeArguments) + 1821779 * TypeListHashCode(methodTypeArguments);
}
}
static bool TypeListEquals(IList<IType> a, IList<IType> b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
if (a.Count != b.Count)
return false;
for (int i = 0; i < a.Count; i++) {
if (!a[i].Equals(b[i]))
return false;
}
return true;
}
static int TypeListHashCode(IList<IType> obj)
{
if (obj == null)
return 0;
unchecked {
int hashCode = 1;
foreach (var element in obj) {
hashCode *= 27;
hashCode += element.GetHashCode();
}
return hashCode;
}
}
#endregion
public override IType VisitTypeParameter(ITypeParameter type) public override IType VisitTypeParameter(ITypeParameter type)
{ {
int index = type.Index; int index = type.Index;

17
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/InheritanceHelper.cs

@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.TypeSystem namespace ICSharpCode.NRefactory.TypeSystem
{ {
@ -50,13 +51,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
if (member == null) if (member == null)
throw new ArgumentNullException("member"); throw new ArgumentNullException("member");
member = member.MemberDefinition;
if (member.IsExplicitInterfaceImplementation && member.ImplementedInterfaceMembers.Count == 1) { if (member.IsExplicitInterfaceImplementation && member.ImplementedInterfaceMembers.Count == 1) {
// C#-style explicit interface implementation // C#-style explicit interface implementation
yield return member.ImplementedInterfaceMembers[0]; member = member.ImplementedInterfaceMembers[0];
member = member.ImplementedInterfaceMembers[0].MemberDefinition; yield return member;
} }
SpecializedMember specializedMember = member as SpecializedMember;
member = member.MemberDefinition;
IEnumerable<IType> allBaseTypes; IEnumerable<IType> allBaseTypes;
if (includeImplementedInterfaces) { if (includeImplementedInterfaces) {
allBaseTypes = member.DeclaringTypeDefinition.GetAllBaseTypes(); allBaseTypes = member.DeclaringTypeDefinition.GetAllBaseTypes();
@ -68,8 +71,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
continue; continue;
foreach (IMember baseMember in baseType.GetMembers(m => m.Name == member.Name, GetMemberOptions.IgnoreInheritedMembers)) { foreach (IMember baseMember in baseType.GetMembers(m => m.Name == member.Name, GetMemberOptions.IgnoreInheritedMembers)) {
if (SignatureComparer.Ordinal.Equals(member, baseMember)) if (SignatureComparer.Ordinal.Equals(member, baseMember)) {
yield return baseMember; if (specializedMember != null)
yield return SpecializedMember.Create(baseMember, specializedMember.Substitution);
else
yield return baseMember;
}
} }
} }
} }

Loading…
Cancel
Save