public CSharpProject(Solution solution, string title, string fileName)
        {
            // Normalize the file name
            fileName = Path.GetFullPath(fileName);

            this.Solution = solution;
            this.Title = title;
            this.FileName = fileName;

            // Use MSBuild to open the .csproj
            var msbuildProject = new Microsoft.Build.Evaluation.Project(fileName);
            // Figure out some compiler settings
            this.AssemblyName = msbuildProject.GetPropertyValue("AssemblyName");
            this.CompilerSettings.AllowUnsafeBlocks = GetBoolProperty(msbuildProject, "AllowUnsafeBlocks") ?? false;
            this.CompilerSettings.CheckForOverflow = GetBoolProperty(msbuildProject, "CheckForOverflowUnderflow") ?? false;
            string defineConstants = msbuildProject.GetPropertyValue("DefineConstants");
            foreach (string symbol in defineConstants.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                this.CompilerSettings.ConditionalSymbols.Add(symbol.Trim());

            // Initialize the unresolved type system
            IProjectContent pc = new CSharpProjectContent();
            pc = pc.SetAssemblyName(this.AssemblyName);
            pc = pc.SetProjectFileName(fileName);
            pc = pc.SetCompilerSettings(this.CompilerSettings);
            // Parse the C# code files
            foreach (var item in msbuildProject.GetItems("Compile")) {
                var file = new CSharpFile(this, Path.Combine(msbuildProject.DirectoryPath, item.EvaluatedInclude));
                Files.Add(file);
            }
            // Add parsed files to the type system
            pc = pc.AddOrUpdateFiles(Files.Select(f => f.UnresolvedTypeSystemForFile));

            // Add referenced assemblies:
            foreach (string assemblyFile in ResolveAssemblyReferences(msbuildProject)) {
                IUnresolvedAssembly assembly = solution.LoadAssembly(assemblyFile);
                pc = pc.AddAssemblyReferences(new [] { assembly });
            }

            // Add project references:
            foreach (var item in msbuildProject.GetItems("ProjectReference")) {
                string referencedFileName = Path.Combine(msbuildProject.DirectoryPath, item.EvaluatedInclude);
                // Normalize the path; this is required to match the name with the referenced project's file name
                referencedFileName = Path.GetFullPath(referencedFileName);
                pc = pc.AddAssemblyReferences(new[] { new ProjectReference(referencedFileName) });
            }
            this.ProjectContent = pc;
        }
示例#2
0
        public static void Main(string[] args)
        {
            if (args.Length == 0) {
                Console.WriteLine("Please specify the path to a .sln file on the command line");

                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
                return;
            }

            Solution solution = new Solution(args[0]);
            foreach (var file in solution.AllFiles) {
                var astResolver = new CSharpAstResolver(file.Project.Compilation, file.SyntaxTree, file.UnresolvedTypeSystemForFile);
                foreach (var invocation in file.SyntaxTree.Descendants.OfType<InvocationExpression>()) {
                    // Retrieve semantics for the invocation
                    var rr = astResolver.Resolve(invocation) as InvocationResolveResult;
                    if (rr == null) {
                        // Not an invocation resolve result - e.g. could be a UnknownMemberResolveResult instead
                        continue;
                    }
                    if (rr.Member.FullName != "System.String.IndexOf") {
                        // Invocation isn't a string.IndexOf call
                        continue;
                    }
                    if (rr.Member.Parameters.First().Type.FullName != "System.String") {
                        // Ignore the overload that accepts a char, as that doesn't take a StringComparison.
                        // (looking for a char always performs the expected ordinal comparison)
                        continue;
                    }
                    if (rr.Member.Parameters.Last().Type.FullName == "System.StringComparison") {
                        // Already using the overload that specifies a StringComparison
                        continue;
                    }
                    Console.WriteLine(invocation.GetRegion() + ": " + invocation.GetText());
                    file.IndexOfInvocations.Add(invocation);
                }
            }
            Console.WriteLine("Found {0} places to refactor in {1} files.",
                              solution.AllFiles.Sum(f => f.IndexOfInvocations.Count),
                              solution.AllFiles.Count(f => f.IndexOfInvocations.Count > 0));
            Console.Write("Apply refactorings? ");
            string answer = Console.ReadLine();
            if ("yes".Equals(answer, StringComparison.OrdinalIgnoreCase) || "y".Equals(answer, StringComparison.OrdinalIgnoreCase)) {
                foreach (var file in solution.AllFiles) {
                    if (file.IndexOfInvocations.Count == 0)
                        continue;
                    // DocumentScript expects the the AST to stay unmodified (so that it fits
                    // to the document state at the time of the DocumentScript constructor call),
                    // so we call Freeze() to prevent accidental modifications (e.g. forgetting a Clone() call).
                    file.SyntaxTree.Freeze();
                    // AST resolver used to find context for System.StringComparison generation
                    var compilation = file.Project.Compilation;
                    var astResolver = new CSharpAstResolver(compilation, file.SyntaxTree, file.UnresolvedTypeSystemForFile);

                    // Create a document containing the file content:
                    var document = new StringBuilderDocument(file.OriginalText);
                    var formattingOptions = FormattingOptionsFactory.CreateAllman();
                    var options = new TextEditorOptions();
                    using (var script = new DocumentScript(document, formattingOptions, options)) {
                        foreach (InvocationExpression expr in file.IndexOfInvocations) {
                            // Generate a reference to System.StringComparison in this context:
                            var astBuilder = new TypeSystemAstBuilder(astResolver.GetResolverStateBefore(expr));
                            IType stringComparison = compilation.FindType(typeof(StringComparison));
                            AstType stringComparisonAst = astBuilder.ConvertType(stringComparison);

                            // Alternative 1: clone a portion of the AST and modify it
                            var copy = (InvocationExpression)expr.Clone();
                            copy.Arguments.Add(stringComparisonAst.Member("Ordinal"));
                            script.Replace(expr, copy);

            //							// Alternative 2: perform direct text insertion
            //							int offset = script.GetCurrentOffset(expr.RParToken.StartLocation);
            //							script.InsertText(offset, ", " + stringComparisonAst.GetText() +  ".Ordinal");
                        }
                    }
                    File.WriteAllText(Path.ChangeExtension(file.FileName, ".output.cs"), document.Text);
                }
            }
        }