Browse Source

Add MethodGroupResolveResult.GetEligibleExtensionMethods() method.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
b4003145b3
  1. 6
      ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs
  2. 35
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  3. 32
      ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
  4. 3
      ICSharpCode.NRefactory.Tests/CSharp/Refactoring/TypeSystemAstBuilderTests.cs
  5. 6
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  6. 53
      ICSharpCode.NRefactory.Tests/TypeSystem/BlobLoaderTests.cs
  7. 2
      ICSharpCode.NRefactory/Editor/ITextSource.cs
  8. 60
      ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  9. 3
      ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

6
ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs

@ -29,6 +29,7 @@ using System.Linq; @@ -29,6 +29,7 @@ using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.NRefactory.Editor;
using Mono.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
@ -3550,6 +3551,11 @@ namespace ICSharpCode.NRefactory.CSharp @@ -3550,6 +3551,11 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
public CompilationUnit Parse (ITextSource textSource, string fileName, int lineModifier = 0)
{
return Parse (textSource.CreateReader(), fileName, lineModifier);
}
public CompilationUnit Parse (TextReader reader, string fileName, int lineModifier = 0)
{
// TODO: can we optimize this to avoid the text->stream->text roundtrip?

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

@ -1631,6 +1631,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1631,6 +1631,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// An empty list will return all matching extension method definitions;
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
/// with the matching number of type parameters.</param>
/// <param name="substituteInferredTypes">
/// Specifies whether to produce a <see cref="SpecializedMethod"/>
/// when type arguments could be inferred from <paramref name="targetType"/>. This parameter
/// is only used for inferred types and has no effect if <paramref name="typeArguments"/> is non-empty.
/// </param>
/// <remarks>
/// The results are stored in nested lists because they are grouped by using scope.
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
@ -1640,7 +1645,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1640,7 +1645,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// new List { all extensions from SomeExtensions }
/// }
/// </remarks>
public List<List<IMethod>> GetExtensionMethods(IType targetType, string name = null, IList<IType> typeArguments = null)
public List<List<IMethod>> GetExtensionMethods(IType targetType, string name = null, IList<IType> typeArguments = null, bool substituteInferredTypes = false)
{
List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
foreach (var inputGroup in GetAllExtensionMethods()) {
@ -1649,15 +1654,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1649,15 +1654,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (name != null && method.Name != name)
continue;
IType[] inferredTypes;
if (typeArguments != null && typeArguments.Count > 0) {
if (method.TypeParameters.Count != typeArguments.Count)
continue;
SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments);
if (IsEligibleExtensionMethod(targetType, method, false))
if (IsEligibleExtensionMethod(targetType, method, false, out inferredTypes))
outputGroup.Add(sm);
} else {
if (IsEligibleExtensionMethod(targetType, method, true))
outputGroup.Add(method);
if (IsEligibleExtensionMethod(targetType, method, true, out inferredTypes)) {
if (substituteInferredTypes && inferredTypes != null) {
outputGroup.Add(new SpecializedMethod(method.DeclaringType, method, inferredTypes));
} else {
outputGroup.Add(method);
}
}
}
}
if (outputGroup.Count > 0)
@ -1666,8 +1677,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1666,8 +1677,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return extensionMethodGroups;
}
bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference)
internal bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
{
return IsEligibleExtensionMethod(compilation, conversions, targetType, method, useTypeInference, out outInferredTypes);
}
internal static bool IsEligibleExtensionMethod(ICompilation compilation, Conversions conversions, IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
{
outInferredTypes = null;
if (targetType == null)
return true;
if (method.Parameters.Count == 0)
@ -1679,15 +1696,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1679,15 +1696,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult[] arguments = { new ResolveResult(targetType) };
IType[] parameterTypes = { method.Parameters[0].Type };
bool success;
IType[] inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success);
var inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success);
var substitution = new TypeParameterSubstitution(null, inferredTypes);
// Validate that the types that could be inferred (aren't unknown) satisfy the constraints:
bool hasInferredTypes = false;
for (int i = 0; i < inferredTypes.Length; i++) {
if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) {
hasInferredTypes = true;
if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions))
return false;
} else {
inferredTypes[i] = method.TypeParameters[i]; // do not substitute types that could not be inferred
}
}
if (hasInferredTypes)
outInferredTypes = inferredTypes;
thisParameterType = thisParameterType.AcceptVisitor(substitution);
}
Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);

32
ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs

@ -25,6 +25,7 @@ using System.Text; @@ -25,6 +25,7 @@ using System.Text;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
@ -126,7 +127,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -126,7 +127,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Gets all candidate extension methods.
/// Note: this includes candidates that are not eligible due to a
/// Note: this includes candidates that are not eligible due to an inapplicable
/// this argument.
/// </summary>
/// <remarks>
/// The results are stored in nested lists because they are grouped by using scope.
@ -137,7 +139,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -137,7 +139,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// new List { all extensions from SomeExtensions }
/// }
/// </remarks>
public IList<List<IMethod>> GetExtensionMethods()
public IEnumerable<IEnumerable<IMethod>> GetExtensionMethods()
{
if (resolver != null) {
Debug.Assert(extensionMethods == null);
@ -147,7 +149,31 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -147,7 +149,31 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
resolver = null;
}
}
return extensionMethods ?? EmptyList<List<IMethod>>.Instance;
return extensionMethods ?? Enumerable.Empty<IEnumerable<IMethod>>();
}
public IEnumerable<IEnumerable<IMethod>> GetEligibleExtensionMethods(bool substituteInferredTypes)
{
var result = new List<List<IMethod>>();
foreach (var methodGroup in GetExtensionMethods()) {
var outputGroup = new List<IMethod>();
foreach (var method in methodGroup) {
IType[] inferredTypes;
if (CSharpResolver.IsEligibleExtensionMethod(
method.Compilation, Conversions.Get(method.Compilation),
this.TargetType, method, true, out inferredTypes))
{
if (substituteInferredTypes && inferredTypes != null) {
outputGroup.Add(new SpecializedMethod(method.DeclaringType, method, inferredTypes));
} else {
outputGroup.Add(method);
}
}
}
if (outputGroup.Count > 0)
result.Add(outputGroup);
}
return result;
}
public override string ToString()

3
ICSharpCode.NRefactory.Tests/CSharp/Refactoring/TypeSystemAstBuilderTests.cs

@ -160,6 +160,8 @@ namespace OtherNS { @@ -160,6 +160,8 @@ namespace OtherNS {
{
var type = new ParameterizedType(nestedClass, new[] { compilation.FindType(KnownTypeCode.Char), compilation.FindType(KnownTypeCode.String) });
Assert.AreEqual("Base<char>.Nested<string>", TypeToString(type));
// The short form "Nested<string>" refers to "Base<T>.Nested<string>",
// so we need to use the long form to specify that T=char.
Assert.AreEqual("Base<char>.Nested<string>", TypeToString(type, baseClass));
Assert.AreEqual("Base<char>.Nested<string>", TypeToString(type, nestedClass));
Assert.AreEqual("Base<char>.Nested<string>", TypeToString(type, derivedClass));
@ -177,6 +179,7 @@ namespace OtherNS { @@ -177,6 +179,7 @@ namespace OtherNS {
public void NestedTypeInDerivedClass()
{
var type1 = new ParameterizedType(nestedClass, new[] { derivedClass.TypeParameters[0], compilation.FindType(KnownTypeCode.String) });
// short form "Nested<string>" cannot be used as it would refer to "Base<S>.Nested<string>"
Assert.AreEqual("Base<T>.Nested<string>", TypeToString(type1, derivedClass));
var type2 = new ParameterizedType(nestedClass, new[] { derivedClass.TypeParameters[1], compilation.FindType(KnownTypeCode.String) });

6
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -174,6 +174,7 @@ @@ -174,6 +174,7 @@
<Compile Include="FormattingTests\TestStatementIndentation.cs" />
<Compile Include="FormattingTests\TestTypeLevelIndentation.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TypeSystem\BlobLoaderTests.cs" />
<Compile Include="TypeSystem\CecilLoaderTests.cs" />
<Compile Include="TypeSystem\GetAllBaseTypesTest.cs" />
<Compile Include="TypeSystem\GetMembersTests.cs" />
@ -225,6 +226,11 @@ @@ -225,6 +226,11 @@
<Compile Include="CSharp\ContextAction\AddAnotherAccessorTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj">
<Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project>
<Name>ICSharpCode.NRefactory.CSharp</Name>

53
ICSharpCode.NRefactory.Tests/TypeSystem/BlobLoaderTests.cs

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
// 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.TypeSystem.Implementation;
using Mono.Cecil;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.TypeSystem
{
[TestFixture]
public class BlobLoaderTests
{
[Test]
public void GetCompressedStackSecDecl()
{
// Compressed Stack from mscorlib 2.0
// [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode),
// StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
byte[] blob = Convert.FromBase64String(@"
LgKAhFN5c3RlbS5TZWN1cml0eS5QZXJtaXNzaW9ucy5TZWN1cml0eVBlcm1pc3Npb25BdHRyaWJ1dGUs
IG1zY29ybGliLCBWZXJzaW9uPTIuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49
Yjc3YTVjNTYxOTM0ZTA4OUABVFUyU3lzdGVtLlNlY3VyaXR5LlBlcm1pc3Npb25zLlNlY3VyaXR5UGVy
bWlzc2lvbkZsYWcFRmxhZ3MCAAAAgI5TeXN0ZW0uU2VjdXJpdHkuUGVybWlzc2lvbnMuU3Ryb25nTmFt
ZUlkZW50aXR5UGVybWlzc2lvbkF0dHJpYnV0ZSwgbXNjb3JsaWIsIFZlcnNpb249Mi4wLjAuMCwgQ3Vs
dHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5MAFUDglQdWJsaWNLZXki
MHgwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMDAwMDAwMDAwMA==");
var attributes = new CecilLoader().ReadSecurityDeclaration(new SecurityDeclaration(SecurityAction.LinkDemand, blob));
Assert.AreEqual(2, attributes.Count);
var compilation = new SimpleCompilation(CecilLoaderTests.Mscorlib);
var context = new SimpleTypeResolveContext(compilation.MainAssembly);
var permissionAttr = attributes[0].CreateResolvedAttribute(context);
var strongNameAttr = attributes[1].CreateResolvedAttribute(context);
Assert.AreEqual("System.Security.Permissions.SecurityPermissionAttribute", permissionAttr.AttributeType.FullName);
Assert.AreEqual("System.Security.Permissions.StrongNameIdentityPermissionAttribute", strongNameAttr.AttributeType.FullName);
}
}
}

2
ICSharpCode.NRefactory/Editor/ITextSource.cs

@ -24,7 +24,7 @@ namespace ICSharpCode.NRefactory.Editor @@ -24,7 +24,7 @@ namespace ICSharpCode.NRefactory.Editor
{
/// <summary>
/// A read-only view on a (potentially mutable) text source.
/// The IDocument interfaces derives from this interface.
/// The IDocument interface derives from this interface.
/// </summary>
public interface ITextSource
{

60
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -1266,31 +1266,49 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1266,31 +1266,49 @@ namespace ICSharpCode.NRefactory.TypeSystem
static readonly ITypeReference securityActionTypeReference = typeof(System.Security.Permissions.SecurityAction).ToTypeReference();
static readonly ITypeReference permissionSetAttributeTypeReference = typeof(System.Security.Permissions.PermissionSetAttribute).ToTypeReference();
/// <summary>
/// Reads a security declaration.
/// </summary>
[CLSCompliant(false)]
public IList<IUnresolvedAttribute> ReadSecurityDeclaration(SecurityDeclaration secDecl)
{
if (secDecl == null)
throw new ArgumentNullException("secDecl");
var result = new List<IUnresolvedAttribute>();
AddSecurityAttributes(secDecl, result);
return result;
}
void AddSecurityAttributes(Mono.Collections.Generic.Collection<SecurityDeclaration> securityDeclarations, IList<IUnresolvedAttribute> targetCollection)
{
foreach (var secDecl in securityDeclarations) {
byte[] blob = secDecl.GetBlob();
BlobReader reader = new BlobReader(blob, null);
var securityAction = new SimpleConstantValue(securityActionTypeReference, (int)secDecl.Action);
if (reader.ReadByte() == '.') {
// binary attribute
uint attributeCount = reader.ReadCompressedUInt32();
UnresolvedSecurityDeclaration unresolvedSecDecl = new UnresolvedSecurityDeclaration(securityAction, blob);
if (this.InterningProvider != null) {
unresolvedSecDecl = this.InterningProvider.Intern(unresolvedSecDecl);
}
for (uint i = 0; i < attributeCount; i++) {
targetCollection.Add(new UnresolvedSecurityAttribute(unresolvedSecDecl, (int)i));
}
} else {
// for backward compatibility with .NET 1.0: XML-encoded attribute
var attr = new DefaultUnresolvedAttribute(permissionSetAttributeTypeReference);
attr.ConstructorParameterTypes.Add(securityActionTypeReference);
attr.PositionalArguments.Add(securityAction);
string xml = System.Text.Encoding.Unicode.GetString(blob);
attr.AddNamedPropertyArgument("XML", KnownTypeReference.String, xml);
targetCollection.Add(attr);
AddSecurityAttributes(secDecl, targetCollection);
}
}
void AddSecurityAttributes(SecurityDeclaration secDecl, IList<IUnresolvedAttribute> targetCollection)
{
byte[] blob = secDecl.GetBlob();
BlobReader reader = new BlobReader(blob, null);
var securityAction = new SimpleConstantValue(securityActionTypeReference, (int)secDecl.Action);
if (reader.ReadByte() == '.') {
// binary attribute
uint attributeCount = reader.ReadCompressedUInt32();
UnresolvedSecurityDeclaration unresolvedSecDecl = new UnresolvedSecurityDeclaration(securityAction, blob);
if (this.InterningProvider != null) {
unresolvedSecDecl = this.InterningProvider.Intern(unresolvedSecDecl);
}
for (uint i = 0; i < attributeCount; i++) {
targetCollection.Add(new UnresolvedSecurityAttribute(unresolvedSecDecl, (int)i));
}
} else {
// for backward compatibility with .NET 1.0: XML-encoded attribute
var attr = new DefaultUnresolvedAttribute(permissionSetAttributeTypeReference);
attr.ConstructorParameterTypes.Add(securityActionTypeReference);
attr.PositionalArguments.Add(securityAction);
string xml = System.Text.Encoding.Unicode.GetString(blob);
attr.AddNamedPropertyArgument("XML", KnownTypeReference.String, xml);
targetCollection.Add(attr);
}
}

3
ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -96,6 +96,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -96,6 +96,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public static bool IsDerivedFrom(this ITypeDefinition type, ITypeDefinition baseType)
{
if (type.Compilation != baseType.Compilation) {
throw new InvalidOperationException("Both arguments to IsDerivedFrom() must be from the same compilation.");
}
return GetAllBaseTypeDefinitions(type).Contains(baseType);
}
#endregion

Loading…
Cancel
Save