简体   繁体   中英

Sort and remove (unused) using statements Roslyn script/code?

Sort and remove (unused) using statements Roslyn script/code? I'm looking for some .NET/Roslyn (compiler as service) code that can run through a project and sort and remove unused using statements. I believe this is possible with Roslyn? Can anyone point me to code that could do this rewrite?

This is a feature in Visual Studio, but academically I think you would collect using statements from your SyntaxTree like this:

var usings = syntaxTree.Root.DescendentNodes().Where(node is UsingDirectiveSyntax);

...and compare that to the namespaces resolved by the symbol table like this:

private static IEnumerable<INamespaceSymbol> GetNamespaceSymbol(ISymbol symbol)
{
    if (symbol != null && symbol.ContainingNamespace != null)
        yield return symbol.ContainingNamespace;
}

var ns = semanticModel.SyntaxTree.Root.DescendentNodes().SelectMany(node =>
    GetNamespaceSymbol(semanticModel.GetSemanticInfo(node).Symbol)).Distinct();

Roslyn CTP September 2012 provides a GetUnusedImportDirectives() method, which is of great use here.

By modifying the OrganizeSolution sample project Matt referenced you can achieve both sorting and removing of (unused) using directives. An (outdated) version of this project can be found here: http://go.microsoft.com/fwlink/?LinkId=263977 . It is outdated because document.GetUpdatedDocument() does not exist anymore,

var document = newSolution.GetDocument(documentId);
var transformation = document.OrganizeImports();
var newDocument = transformation.GetUpdatedDocument();

can be simplified to

var document = newSolution.GetDocument(documentId);
var newDocument = document.OrganizeImports();

Adding newDocument = RemoveUnusedImportDirectives(newDocument); and providing the following method will do the trick.

private static IDocument RemoveUnusedImportDirectives(IDocument document)
{
    var root = document.GetSyntaxRoot();
    var semanticModel = document.GetSemanticModel();

    // An IDocument can refer to both a CSharp as well as a VisualBasic source file.
    // Therefore we need to distinguish those cases and provide appropriate casts.  
    // Since the question was tagged c# only the CSharp way is provided.
    switch (document.LanguageServices.Language)
    {
        case LanguageNames.CSharp:
            var oldUsings = ((CompilationUnitSyntax)root).Usings;
            var unusedUsings = ((SemanticModel)semanticModel).GetUnusedImportDirectives();
            var newUsings = Syntax.List(oldUsings.Where(item => !unusedUsings.Contains(item)));
            root = ((CompilationUnitSyntax)root).WithUsings(newUsings);
            document = document.UpdateSyntaxRoot(root);
            break;

        case LanguageNames.VisualBasic:
            // TODO
            break;
    }
    return document;
}

I use this the following extension method to sort usings

internal static SyntaxList<UsingDirectiveSyntax> Sort(this SyntaxList<UsingDirectiveSyntax> usingDirectives, bool placeSystemNamespaceFirst = false) =>
    SyntaxFactory.List(
        usingDirectives
        .OrderBy(x => x.StaticKeyword.IsKind(SyntaxKind.StaticKeyword) ? 1 : x.Alias == null ? 0 : 2)
        .ThenBy(x => x.Alias?.ToString())
        .ThenByDescending(x => placeSystemNamespaceFirst && x.Name.ToString().StartsWith(nameof(System) + "."))
        .ThenBy(x => x.Name.ToString()));

and

compilationUnit = compilationUnit.WithUsings(SortUsings(compilationUnit.Usings))

Check out the OrganizeSolution sample project that came with Roslyn. It does something similar to what you want. It does the sorting part. You'll have to also use the SemanticModel like Jeff shows to determine if there are no references to a particular namespace in the source.

For removing statements, check out the FAQ item 30 in the solution in the following directory, in the FAQ.cs file: (Note this is for the June 2012 CTP version of Roslyn).

%userprofile%\\Documents\\Microsoft Roslyn CTP - June 2012\\CSharp\\APISampleUnitTestsCS

There is also a FAQ which refers to this project:

http://www.codeplex.com/Download?ProjectName=dlr&DownloadId=386858

Here is the sample rewriter from the sample code, which removes assignment statements.

// Below SyntaxRewriter removes multiple assignement statements from under the 
// SyntaxNode being visited.
public class AssignmentStatementRemover : SyntaxRewriter
{
    public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
    {
        SyntaxNode updatedNode = base.VisitExpressionStatement(node);

        if (node.Expression.Kind == SyntaxKind.AssignExpression)
        {
            if (node.Parent.Kind == SyntaxKind.Block)
            {
                // There is a parent block so it is ok to remove the statement completely.
                updatedNode = null;
            }
            else
            {
                // The parent context is some statement like an if statement without a block.
                // Return an empty statement.
                updatedNode = Syntax.EmptyStatement()
                    .WithLeadingTrivia(updatedNode.GetLeadingTrivia())
                    .WithTrailingTrivia(updatedNode.GetTrailingTrivia());
            }
        }
     return updatedNode;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM