ILSpy icon indicating copy to clipboard operation
ILSpy copied to clipboard

AnalyserScope goes to infinite loop when analysing public inner class

Open yzdeveloper opened this issue 4 months ago • 3 comments

Steps to reproduce

Open an assembly where an inner public class exists. Then select Analyse in the context menu. Analysis result would never shown.

Error message shown

No error message shown

Details

  • Product in use: ILSpy
  • Version in use: commit 84c5e6337014c5439349f2e29eb82b87f1813371
  • Any other relevant information to the issue, or your interest in contributing a fix. I have the fix....

yzdeveloper avatar Feb 14 '24 22:02 yzdeveloper

Fix:

	static void DetermineEffectiveAccessibility(IEntity input, out ITypeDefinition typeScope, out Accessibility accessibility)
	{
		if (input is ITypeDefinition td)
		{
			accessibility = Accessibility.Public;
			typeScope = td;
		}
		else
		{
			accessibility = input.Accessibility;
			typeScope = input.DeclaringTypeDefinition;
		}
		// Once we reach a private entity, we leave the loop with typeScope set to the class that
		// contains the private entity = the scope that needs to be searched.
		// Otherwise (if we don't find a private entity) we return the top-level class.
		var prevTypeScope = typeScope;
		while (typeScope != null && !accessibility.LessThanOrEqual(Accessibility.Private))
		{
			accessibility = accessibility.Intersect(typeScope.Accessibility);
			typeScope = typeScope.DeclaringTypeDefinition;
		}
		if (typeScope == null)
		{
			typeScope = prevTypeScope;
		}
	}

yzdeveloper avatar Feb 14 '24 22:02 yzdeveloper

Test:

[TestFixture]
public class AnalyzerScopeTests
{
	public class TestClass
	{

	}


	[Test]
	public void WhenPublicNestedClass_ThenNotInfiniteLoop()
	{
		// Given
		ILSpyX.AssemblyList assemblyList = new ILSpyX.AssemblyList();
		var file = new PEFile(this.GetType().Assembly.Location);
		var td = file.Metadata.TypeDefinitions.First(td => td.GetFullTypeName(file.Metadata).Name == nameof(TestClass));

		Decompiler.Metadata.IAssemblyResolver assemblyResolver = new UniversalAssemblyResolver(null, false, null);
		ICompilation compilation = new DecompilerTypeSystem(file, assemblyResolver);
		ITypeResolveContext context = new CSharpResolver(compilation);
		var module = ((IModuleReference)file).Resolve(context) as MetadataModule;
		IEntity entity = module.GetDefinition(td);

		// When 
		var task = Task.Run(() => {
			var target = new AnalyzerScope(assemblyList, entity);
		});

		var result = Task.WaitAny(new[] { task, Task.Delay(500) }); // 0.5 seconds

		// Then
		Assert.That(result == 0, "The constructor should completes in no longer than 0.5 seconds");
	}

}

yzdeveloper avatar Feb 14 '24 22:02 yzdeveloper

My push was rejected. I am not sure how to contribute...

yzdeveloper avatar Feb 14 '24 22:02 yzdeveloper