Roslyn 去除多余using

编程入门 行业动态 更新时间:2024-10-10 01:23:56

Roslyn 去除<a href=https://www.elefans.com/category/jswz/34/1754162.html style=多余using"/>

Roslyn 去除多余using

原因

当你添加新代码的时候VS会自动帮你添加对应的using,但是它只会帮你加不会帮你减
由于运行时并不能使用UnityEditor命名空间里面的东西。你就算加了也会在打包的时候给你报错,除非使用宏包裹起来
因为我们打包都是在打包机上操作的。一般情况下自己本地是不会打包也就不会知道在代码里面有多余using
经常出现打包到一半因为有多余的using导致的打包失败。然后再通知程序修改上传重新打包

解决方案

VS右键有个删除using和对其排序功能。但是说实话并没有什么卵用。每次保存前都右键执行一次比上面的流程更繁琐
基于以上流程过于繁琐。并且浪费时间。有了以下方案。
在git的pre-commit钩子加上cs后缀文件的using检测。如果有多余的using直接删除

编译动态链接库

编译需要设置是否开启

var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: allowUnsafe);
var compilation = CSharpCompilation.Create(assemblyName, options: options);

添加动态库所引用的dll

portableExecutable = MetadataReference.CreateFromFile(dllPath);
compilation = compilation.AddReferences(portableExecutable);

添加动态库所包含的文件

var option = new CSharpParseOptions(LanguageVersion.CSharp8, DocumentationMode.Parse, SourceCodeKind.Regular, symbols);
var encoding = FileEncoding.GetType(path);
var content = File.ReadAllText(path, encoding);
var tree = CSharpSyntaxTree.ParseText(content, option);
compilation = compilation.AddSyntaxTrees(tree);

以上所有包含的信息在csproj里面都有记录。直接读取并设置即可

检测是否有多余using


从文中可以看到 CS8019 是多余using警告。我们只需要根据这个移除对应的节点即可

CSharpCompilation compilation = CompilationHelper.GetCompilation(assemblyName);
var tree = CompilationHelper.GetSyntaxTree(assemblyName, path);
var root = tree.GetCompilationUnitRoot();
SemanticModel model = compilation.GetSemanticModel(tree);
var diagnostics = model.GetDiagnostics();
foreach (Diagnostic diagnostic in diagnostics)
{if (diagnostic.Id == "CS8019"){if (root.FindNode(diagnostic.Location.SourceSpan) is UsingDirectiveSyntax node){// 需要过滤的节点}}
}

语法树修改

通过CSharpSyntaxRewriter对语法树节点的修改
我们需要的是重载 VisitUsingDirective 函数,只需要返回空即可删除对应节点

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;namespace CSharpAnalyzer
{public class UsingSyntaxRewriter : CSharpSyntaxRewriter{private Dictionary<UsingDirectiveSyntax, bool> filters = new Dictionary<UsingDirectiveSyntax, bool>();public bool IsUpdate(){return filters.Count > 0;}public void Add(UsingDirectiveSyntax node){if (node == null)return;filters[node] = true;}public override SyntaxNode VisitUsingDirective(UsingDirectiveSyntax node){if (filters.ContainsKey(node))return null;return base.VisitUsingDirective(node);}}}

完整代码

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;namespace CSharpAnalyzer
{public class CompilationHelper{static Dictionary<string, string[]> symbolDict = new Dictionary<string, string[]>();static Dictionary<string, CSharpCompilation> compilationDict = new Dictionary<string, CSharpCompilation>();public static void LoadCsproj(string csproj){if (!File.Exists(csproj)){throw new Exception($"{csproj} not found");}var assemblyName = Path.GetFileNameWithoutExtension(csproj);if (compilationDict.ContainsKey(assemblyName))return;var dir = Path.GetDirectoryName(csproj);XmlDocument document = new XmlDocument();document.LoadXml(File.ReadAllText(csproj));var root = document.FirstChild;var project = root.NextSibling;bool allowUnsafe = false;string[] symbols = null;List<string> dllList = new List<string>();List<string> fileList = new List<string>();foreach (XmlNode node in project.ChildNodes){switch (node.Name){case "PropertyGroup":foreach (XmlNode n in node.ChildNodes){if (n.Name == "DefineConstants"){symbols = n.InnerText.Split(';');}else if (n.Name == "AllowUnsafeBlocks"){bool.TryParse(n.InnerText, out allowUnsafe);}}break;case "ItemGroup":foreach (XmlNode n in node.ChildNodes){if (n.Name == "Compile"){fileList.Add(n.Attributes["Include"].Value);}else if (n.Name == "Reference"){dllList.Add(n.ChildNodes[0].InnerText);}else if (n.Name == "ProjectReference"){var name = Path.GetFileNameWithoutExtension(n.Attributes["Include"].Value);var dll = Path.Combine(@"Library\ScriptAssemblies", name + ".dll");dllList.Add(dll);}}break;}}var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: allowUnsafe);var compilation = CSharpCompilation.Create(assemblyName, options: options);foreach (var dll in dllList){var dllPath = dll;if (!dll.Contains(":"))dllPath = Path.Combine(dir, dll);var portableExecutable = PortableExecutableHelper.Get(dllPath);if (portableExecutable != null){compilation = compilation.AddReferences(portableExecutable);}}foreach (var file in fileList){var path = Path.Combine(dir, file).Replace("\\", "/");var tree = GetSyntaxTree(assemblyName, path);compilation = compilation.AddSyntaxTrees(tree);}symbolDict[assemblyName] = symbols;compilationDict[assemblyName] = compilation;}public static CSharpCompilation GetCompilation(string assemblyName){compilationDict.TryGetValue(assemblyName, out var compilation); return compilation;}public static SyntaxTree GetSyntaxTree(string assemblyName, string path){symbolDict.TryGetValue(assemblyName, out var symbol);return SyntaxTreeHelper.GetSyntaxTree(path, symbol);}}
}
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.IO;namespace CSharpAnalyzer
{internal class PortableExecutableHelper{static Dictionary<string, PortableExecutableReference> dict = new Dictionary<string, PortableExecutableReference>();internal static PortableExecutableReference Get(string path){if (dict.TryGetValue(path, out var dll)){return dll;}if (File.Exists(path)){dll = MetadataReference.CreateFromFile(path);if (dll != null){dict[path] = dll;return dll;}}return null;}}
}
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Generic;
using System.IO;namespace CSharpAnalyzer
{public class SyntaxTreeHelper{static Dictionary<string, SyntaxTree> syntaxTreeDict = new Dictionary<string, SyntaxTree>();public static SyntaxTree GetSyntaxTree(string path, string[] symbols, bool reload = false){SyntaxTree result = null;if (!reload){if (syntaxTreeDict.TryGetValue(path, out result)){return result;}}if (!File.Exists(path))return result;CSharpParseOptions option = null;if (symbols != null && symbols.Length > 0){option = new CSharpParseOptions(LanguageVersion.CSharp8, DocumentationMode.Parse, SourceCodeKind.Regular, symbols);}var encoding = FileEncoding.GetType(path);var content = File.ReadAllText(path, encoding);result = CSharpSyntaxTree.ParseText(content, option);syntaxTreeDict[path] = result;return result;}}
}
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.IO;namespace CSharpAnalyzer
{public class UnnecessaryImportsAnalyzer{public static bool Run(string path, string assemblyName){if (!File.Exists(path))return false;CSharpCompilation compilation = CompilationHelper.GetCompilation(assemblyName);if (compilation == null)return false;var tree = CompilationHelper.GetSyntaxTree(assemblyName, path.Replace(@"\","/"));var root = tree.GetCompilationUnitRoot();SemanticModel model = compilation.GetSemanticModel(tree);var diagnostics = model.GetDiagnostics();if (diagnostics.IsEmpty){var encoding = FileEncoding.GetType(path);if (encoding != System.Text.Encoding.UTF8){var context = File.ReadAllText(path, encoding);File.WriteAllText(path, context, System.Text.Encoding.UTF8);return true;}return false;}{var usingSyntaxRewriter = new UsingSyntaxRewriter();foreach (Diagnostic diagnostic in diagnostics){if(diagnostic.Severity == DiagnosticSeverity.Error) {Console.WriteLine($"{path} {diagnostic.GetMessage()}");return false;}if (diagnostic.Id == "CS8019"){if (root.FindNode(diagnostic.Location.SourceSpan) is UsingDirectiveSyntax node){usingSyntaxRewriter.Add(node);}}}if (usingSyntaxRewriter.IsUpdate()){var newRoot = usingSyntaxRewriter.Visit(root);var context = newRoot.GetText().ToString();File.WriteAllText(path, context, System.Text.Encoding.UTF8);return true;}else{var encoding = FileEncoding.GetType(path);if (encoding != System.Text.Encoding.UTF8){var context = File.ReadAllText(path, encoding);File.WriteAllText(path, context, System.Text.Encoding.UTF8);return true;}}}return false;}}}
// 
using System;
using System.IO;
using System.Text;namespace CSharpAnalyzer
{public class FileEncoding{/// <summary> /// 给定文件的路径,读取文件的二进制数据,判断文件的编码类型 /// </summary> /// <param name=“FILE_NAME“>文件路径</param> /// <returns>文件的编码类型</returns> public static System.Text.Encoding GetType(string FILE_NAME){FileStream fs = new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read);Encoding r = GetType(fs);fs.Close();return r;}/// <summary> /// 通过给定的文件流,判断文件的编码类型 /// </summary> /// <param name=“fs“>文件流</param> /// <returns>文件的编码类型</returns> public static System.Text.Encoding GetType(FileStream fs){Encoding reVal = Encoding.Default;BinaryReader r = new BinaryReader(fs, System.Text.Encoding.Default);int i;int.TryParse(fs.Length.ToString(), out i);byte[] ss = r.ReadBytes(i);if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF)){reVal = Encoding.UTF8;}else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00){reVal = Encoding.BigEndianUnicode;}else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41){reVal = Encoding.Unicode;}r.Close();return reVal;}/// <summary> /// 判断是否是不带 BOM 的 UTF8 格式 /// </summary> /// <param name=“data“></param> /// <returns></returns> private static bool IsUTF8Bytes(byte[] data){int charByteCounter = 1; //计算当前正分析的字符应还有的字节数 byte curByte; //当前分析的字节. for (int i = 0; i < data.Length; i++){curByte = data[i];if (charByteCounter == 1){if (curByte >= 0x80){//判断当前 while (((curByte <<= 1) & 0x80) != 0){charByteCounter++;}//标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X if (charByteCounter == 1 || charByteCounter > 6){return false;}}}else{//若是UTF-8 此时第一位必须为1 if ((curByte & 0xC0) != 0x80){return false;}charByteCounter--;}}if (charByteCounter > 1){throw new Exception("非预期的byte格式");}return true;}}
}

更多推荐

Roslyn 去除多余using

本文发布于:2023-12-06 22:02:26,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1669086.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:多余   Roslyn

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!