From 9a9485a49dd58e1dbf7ba8ff917f89214dd6b4ee Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 3 Aug 2012 18:15:18 +0200 Subject: [PATCH] Add ProjectReference class to NRefactory to simplify creating the type system for a solution with multiple projects. --- .../CSharpProjectContent.cs | 18 +++++- .../Resolver/ExplicitConversionsTest.cs | 1 + .../ICSharpCode.NRefactory.Tests.csproj | 1 + .../TypeSystem/CyclicProjectDependency.cs | 64 +++++++++++++++++++ .../ICSharpCode.NRefactory.csproj | 3 +- .../DefaultSolutionSnapshot.cs | 33 +++++++++- .../TypeSystem/IProjectContent.cs | 20 +++++- .../TypeSystem/ISolutionSnapshot.cs | 10 +++ .../Implementation/MinimalCorlib.cs | 3 - .../Implementation/SimpleCompilation.cs | 3 - .../TypeSystem/ProjectReference.cs | 56 ++++++++++++++++ 11 files changed, 198 insertions(+), 14 deletions(-) create mode 100644 ICSharpCode.NRefactory.Tests/TypeSystem/CyclicProjectDependency.cs rename ICSharpCode.NRefactory/TypeSystem/{Implementation => }/DefaultSolutionSnapshot.cs (67%) create mode 100644 ICSharpCode.NRefactory/TypeSystem/ProjectReference.cs diff --git a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs index a890c1ea41..2dfac4a1ad 100644 --- a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs +++ b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs @@ -31,6 +31,7 @@ namespace ICSharpCode.NRefactory.CSharp public class CSharpProjectContent : IProjectContent { string assemblyName; + string projectFileName; string location; Dictionary parsedFiles; List assemblyReferences; @@ -38,7 +39,6 @@ namespace ICSharpCode.NRefactory.CSharp public CSharpProjectContent() { - this.assemblyName = string.Empty; this.parsedFiles = new Dictionary(Platform.FileNameComparer); this.assemblyReferences = new List(); this.compilerSettings = new CompilerSettings(); @@ -48,6 +48,7 @@ namespace ICSharpCode.NRefactory.CSharp protected CSharpProjectContent(CSharpProjectContent pc) { this.assemblyName = pc.assemblyName; + this.projectFileName = pc.projectFileName; this.location = pc.location; this.parsedFiles = new Dictionary(pc.parsedFiles, Platform.FileNameComparer); this.assemblyReferences = new List(pc.assemblyReferences); @@ -62,6 +63,10 @@ namespace ICSharpCode.NRefactory.CSharp get { return assemblyReferences; } } + public string ProjectFileName { + get { return projectFileName; } + } + public string AssemblyName { get { return assemblyName; } } @@ -129,11 +134,18 @@ namespace ICSharpCode.NRefactory.CSharp pc.assemblyName = newAssemblyName; return pc; } + + public IProjectContent SetProjectFileName(string newProjectFileName) + { + CSharpProjectContent pc = Clone(); + pc.projectFileName = newProjectFileName; + return pc; + } - public IProjectContent SetLocation(string location) + public IProjectContent SetLocation(string newLocation) { CSharpProjectContent pc = Clone(); - pc.location = location; + pc.location = newLocation; return pc; } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs index c72d67312b..f8395cdabf 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs @@ -194,6 +194,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(object[]), typeof(string[]))); Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(dynamic[]), typeof(string[]))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(object[]), typeof(object[,]))); Assert.AreEqual(C.None, ExplicitConversion(typeof(object[]), typeof(int[]))); Assert.AreEqual(C.None, ExplicitConversion(typeof(short[]), typeof(int[]))); Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Array), typeof(int[]))); diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 7f1bbcdb46..66900628a5 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -234,6 +234,7 @@ + diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/CyclicProjectDependency.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/CyclicProjectDependency.cs new file mode 100644 index 0000000000..f734002859 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/CyclicProjectDependency.cs @@ -0,0 +1,64 @@ +// 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.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using NUnit.Framework; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + [TestFixture] + public class CyclicProjectDependency + { + IProjectContent pc1; + IProjectContent pc2; + ISolutionSnapshot solution; + + [SetUp] + public void Setup() + { + pc1 = new CSharpProjectContent() + .SetAssemblyName("PC1") + .SetProjectFileName("PC1.csproj") + .AddAssemblyReferences(new IAssemblyReference[] { CecilLoaderTests.Mscorlib, new ProjectReference("PC2.csproj") }); + + pc2 = new CSharpProjectContent() + .SetAssemblyName("PC2") + .SetProjectFileName("PC2.csproj") + .AddAssemblyReferences(new IAssemblyReference[] { CecilLoaderTests.Mscorlib, new ProjectReference("PC1.csproj") }); + + solution = new DefaultSolutionSnapshot(new[] { pc1, pc2 }); + } + + [Test] + public void CreateCompilation1() + { + ICompilation c = solution.GetCompilation(pc1); + Assert.AreEqual(new string[] { "PC1", "mscorlib", "PC2" }, c.Assemblies.Select(asm => asm.AssemblyName).ToArray()); + } + + [Test] + public void CreateCompilation2() + { + ICompilation c = solution.GetCompilation(pc2); + Assert.AreEqual(new string[] { "PC2", "mscorlib", "PC1" }, c.Assemblies.Select(asm => asm.AssemblyName).ToArray()); + } + } +} diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 50ec131c12..d648f746e0 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -136,6 +136,7 @@ + @@ -170,7 +171,6 @@ - @@ -222,6 +222,7 @@ + diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultSolutionSnapshot.cs b/ICSharpCode.NRefactory/TypeSystem/DefaultSolutionSnapshot.cs similarity index 67% rename from ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultSolutionSnapshot.cs rename to ICSharpCode.NRefactory/TypeSystem/DefaultSolutionSnapshot.cs index f32676c220..16d07a08dc 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultSolutionSnapshot.cs +++ b/ICSharpCode.NRefactory/TypeSystem/DefaultSolutionSnapshot.cs @@ -18,16 +18,47 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; -namespace ICSharpCode.NRefactory.TypeSystem.Implementation +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.TypeSystem { /// /// Default implementation of ISolutionSnapshot. /// public class DefaultSolutionSnapshot : ISolutionSnapshot { + readonly Dictionary projectDictionary = new Dictionary(Platform.FileNameComparer); ConcurrentDictionary dictionary = new ConcurrentDictionary(); + /// + /// Creates a new DefaultSolutionSnapshot with the specified projects. + /// + public DefaultSolutionSnapshot(IEnumerable projects) + { + foreach (var project in projects) { + if (project.ProjectFileName != null) + projectDictionary.Add(project.ProjectFileName, project); + } + } + + /// + /// Creates a new DefaultSolutionSnapshot that does not support s. + /// + public DefaultSolutionSnapshot() + { + } + + public IProjectContent GetProjectContent(string projectFileName) + { + IProjectContent pc; + if (projectDictionary.TryGetValue(projectFileName, out pc)) + return pc; + else + return null; + } + public ICompilation GetCompilation(IProjectContent project) { if (project == null) diff --git a/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs b/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs index 4a731c42ce..120701d04b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs @@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem /// public interface IProjectContent : IUnresolvedAssembly { + /// + /// Gets the path to the project file (e.g. .csproj). + /// + string ProjectFileName { get; } + /// /// Gets a parsed file by its file name. /// @@ -52,7 +57,8 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Creates a new that allows resolving within this project. /// /// - /// An ICompilation is immutable, it operates on a snapshot of this project. + /// This method does not support s. When dealing with a solution + /// containing multiple projects, consider using instead. /// ICompilation CreateCompilation(); @@ -61,7 +67,10 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// The parent solution snapshot to use for the compilation. /// - /// An ICompilation is immutable, it operates on a snapshot of this project. + /// This method is intended to be called by ISolutionSnapshot implementations. Other code should + /// call instead. + /// This method always creates a new compilation, even if the solution snapshot already contains + /// one for this project. /// ICompilation CreateCompilation(ISolutionSnapshot solutionSnapshot); @@ -71,7 +80,12 @@ namespace ICSharpCode.NRefactory.TypeSystem IProjectContent SetAssemblyName(string newAssemblyName); /// - /// Changes the location of this project content. + /// Changes the project file name of this project content. + /// + IProjectContent SetProjectFileName(string newProjectFileName); + + /// + /// Changes the path to the assembly location (the output path where the project compiles to). /// IProjectContent SetLocation(string newLocation); diff --git a/ICSharpCode.NRefactory/TypeSystem/ISolutionSnapshot.cs b/ICSharpCode.NRefactory/TypeSystem/ISolutionSnapshot.cs index 052dc1a798..888233d0e0 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ISolutionSnapshot.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ISolutionSnapshot.cs @@ -25,8 +25,18 @@ namespace ICSharpCode.NRefactory.TypeSystem /// public interface ISolutionSnapshot { + /// + /// Gets the project content with the specified file name. + /// Returns null if no such project exists in the solution. + /// + /// + /// This method is used by the class. + /// + IProjectContent GetProjectContent(string projectFileName); + /// /// Gets the compilation for the specified project. + /// The project must be a part of the solution (passed to the solution snapshot's constructor). /// ICompilation GetCompilation(IProjectContent project); } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/MinimalCorlib.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/MinimalCorlib.cs index a6c90a6e88..0c019b06ac 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/MinimalCorlib.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/MinimalCorlib.cs @@ -17,9 +17,6 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs index 08c5eb6d8b..cfff5fae10 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs @@ -18,8 +18,6 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -95,7 +93,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - [ObsoleteAttribute("Use compilation.Assemblies.Where(asm != compilation.MainAssembly) instead.")] public IList ReferencedAssemblies { get { if (referencedAssemblies == null) diff --git a/ICSharpCode.NRefactory/TypeSystem/ProjectReference.cs b/ICSharpCode.NRefactory/TypeSystem/ProjectReference.cs new file mode 100644 index 0000000000..928ec796de --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/ProjectReference.cs @@ -0,0 +1,56 @@ +// 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; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + /// + /// References another project content in the same solution. + /// Using the class requires that you + /// + [Serializable] + public class ProjectReference : IAssemblyReference + { + readonly string projectFileName; + + /// + /// Creates a new reference to the specified project (must be part of the same solution). + /// + /// Full path to the file name. Must be identical to of the target project; do not use a relative path. + public ProjectReference(string projectFileName) + { + this.projectFileName = projectFileName; + } + + public IAssembly Resolve(ITypeResolveContext context) + { + var solution = context.Compilation.SolutionSnapshot; + var pc = solution.GetProjectContent(projectFileName); + if (pc != null) + return pc.Resolve(context); + else + return null; + } + + public override string ToString() + { + return string.Format("[ProjectReference {0}]", projectFileName); + } + } +}