├── .gitignore ├── LICENSE ├── MacroSharp.ExampleMacros ├── MacroSharp.ExampleMacros.csproj ├── NotifyPropertyChanged.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── MacroSharp.Examples ├── App.config ├── MacroSharp.Examples.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── TestNPCMacro.cs └── packages.config ├── MacroSharp.sln ├── MacroSharp ├── AssemblyLoader.cs ├── Attributes │ ├── MethodMacroAttribute.cs │ └── TypeMacroAttribute.cs ├── IMacroTransform.cs ├── MacroCSharpSyntaxRewriter.cs ├── MacroCompiler.cs ├── MacroPluginAttribute.cs ├── MacroRegistry.cs ├── MacroSharp.csproj ├── MacroSymbolVisitor.cs ├── MacroSyntaxFactory.cs ├── MacroSyntaxFactoryFromGenerated.cs ├── MacroSyntaxFactoryFromRoslyn.cs ├── MacroTransform.cs ├── Properties │ └── AssemblyInfo.cs ├── SyntaxFactoryExtensions.cs ├── TransformContext.cs ├── WellKnownTypes.cs └── packages.config ├── README.md └── RoslynCommon.targets /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | # TODO: Comment the next line if you want to checkin your web deploy settings 137 | # but database connection strings (with potential passwords) will be unencrypted 138 | *.pubxml 139 | *.publishproj 140 | 141 | # NuGet Packages 142 | *.nupkg 143 | # The packages folder can be ignored because of Package Restore 144 | **/packages/* 145 | # except build/, which is used as an MSBuild target. 146 | !**/packages/build/ 147 | # Uncomment if necessary however generally it will be regenerated when needed 148 | #!**/packages/repositories.config 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Visual Studio cache files 158 | # files ending in .cache can be ignored 159 | *.[Cc]ache 160 | # but keep track of directories ending in .cache 161 | !*.[Cc]ache/ 162 | 163 | # Others 164 | ClientBin/ 165 | [Ss]tyle[Cc]op.* 166 | ~$* 167 | *~ 168 | *.dbmdl 169 | *.dbproj.schemaview 170 | *.pfx 171 | *.publishsettings 172 | node_modules/ 173 | orleans.codegen.cs 174 | 175 | # RIA/Silverlight projects 176 | Generated_Code/ 177 | 178 | # Backup & report files from converting an old project file 179 | # to a newer Visual Studio version. Backup files are not needed, 180 | # because we have git ;-) 181 | _UpgradeReport_Files/ 182 | Backup*/ 183 | UpgradeLog*.XML 184 | UpgradeLog*.htm 185 | 186 | # SQL Server files 187 | *.mdf 188 | *.ldf 189 | 190 | # Business Intelligence projects 191 | *.rdl.data 192 | *.bim.layout 193 | *.bim_*.settings 194 | 195 | # Microsoft Fakes 196 | FakesAssemblies/ 197 | 198 | # Node.js Tools for Visual Studio 199 | .ntvs_analysis.dat 200 | 201 | # Visual Studio 6 build log 202 | *.plg 203 | 204 | # Visual Studio 6 workspace options file 205 | *.opt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 russpowers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MacroSharp.ExampleMacros/MacroSharp.ExampleMacros.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {1B6FBD6E-83DE-463E-B4F5-B1AC1956FA88} 9 | Library 10 | Properties 11 | MacroSharp.ExampleMacros 12 | MacroSharp.ExampleMacros 13 | v4.5 14 | 512 15 | $(RoslynBinaryPath) 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | ..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll 40 | True 41 | 42 | 43 | 44 | ..\packages\System.Reflection.Metadata.1.1.0-alpha-00012\lib\dotnet\System.Reflection.Metadata.dll 45 | True 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {42778190-193e-400f-891c-aca9d975e0aa} 61 | MacroSharp 62 | 63 | 64 | 65 | 66 | 67 | 68 | 75 | -------------------------------------------------------------------------------- /MacroSharp.ExampleMacros/NotifyPropertyChanged.cs: -------------------------------------------------------------------------------- 1 | using MacroSharp; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | using F = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 10 | using M = MacroSharp.MacroSyntaxFactory; 11 | using Microsoft.CodeAnalysis.CSharp; 12 | 13 | namespace MacroSharp.ExampleMacros 14 | { 15 | /// 16 | /// This is the attribute that will be used on a class 17 | /// 18 | public class NotifyPropertyChangedAttribute : TypeMacroAttribute 19 | { 20 | } 21 | 22 | /// 23 | /// This was hacked together very quickly as an demo, please don't use it as 24 | /// as an example of good Roslyn syntax... I will improve it later 25 | /// 26 | class NotifyPropertyChanged : MacroTransform 27 | { 28 | private class MyRewriter : MacroCSharpSyntaxRewriter 29 | { 30 | public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node) 31 | { 32 | if (node.AccessorList.Accessors.Count == 2 && 33 | node.AccessorList.Accessors[0].Body == null && 34 | node.AccessorList.Accessors[1].Body == null) 35 | { 36 | 37 | var propertyName = node.Identifier.Text; 38 | var privateVarName = "_" + Char.ToLower(propertyName[0]) + propertyName.Substring(1); 39 | var privateVar = F.ParseName(privateVarName); 40 | 41 | AddBeforeCurrentNode( 42 | F.FieldDeclaration( 43 | F.VariableDeclaration(node.Type) 44 | .AddVariables(F.VariableDeclarator(privateVarName)))); 45 | 46 | var getter = F.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, 47 | F.Block(F.ReturnStatement(privateVar))); 48 | 49 | var setter = F.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, 50 | F.Block( 51 | F.ExpressionStatement(F.ParseExpression(privateVarName + " = value")), 52 | F.IfStatement( 53 | F.ParseExpression("PropertyChanged != null"), 54 | F.ExpressionStatement( 55 | F.InvocationExpression( 56 | F.ParseName("PropertyChanged"), 57 | F.ParseArgumentList("(this, new System.ComponentModel.PropertyChangedEventArgs(\"" + propertyName + "\"))")))))); 58 | 59 | return F.PropertyDeclaration(node.Type, propertyName) 60 | .AddAccessorListAccessors(getter, setter) 61 | .AddModifiers(F.Token(SyntaxKind.PublicKeyword)); 62 | } 63 | else 64 | return base.VisitPropertyDeclaration(node); 65 | } 66 | 67 | public override SyntaxNode VisitEventFieldDeclaration(EventFieldDeclarationSyntax node) 68 | { 69 | if (node.Declaration.Variables.Any(x => x.Identifier.Text == "PropertyChanged")) 70 | { 71 | // This is just a sample of how to create a warning... normally you wouldn't have one here 72 | Context.Diagnostics.Add(Diagnostic.Create(diagDesc, node.GetLocation())); 73 | foundPCEH = true; 74 | } 75 | 76 | return base.VisitEventFieldDeclaration(node); 77 | } 78 | 79 | public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) 80 | { 81 | var transformed = (ClassDeclarationSyntax)base.VisitClassDeclaration(node); 82 | if (!foundPCEH) 83 | return transformed 84 | .AddMembers( 85 | F.EventFieldDeclaration( 86 | F.VariableDeclaration(pceh) 87 | .AddVariables(F.VariableDeclarator("PropertyChanged"))) 88 | .WithModifiers(SyntaxTokenList.Create(F.Token(SyntaxKind.PublicKeyword)))); 89 | else 90 | return transformed; 91 | } 92 | 93 | TypeSyntax pceh = F.ParseTypeName("System.ComponentModel.PropertyChangedEventHandler"); 94 | private bool foundPCEH; 95 | private DiagnosticDescriptor diagDesc = 96 | new DiagnosticDescriptor("INPC001", "PropertyChanged already created (just testing, everything's ok)", "PropertyChanged already created (just testing, everything's ok)", 97 | "Macro", DiagnosticSeverity.Warning, true); 98 | } 99 | 100 | public override SyntaxNode Transform(TransformContext context) 101 | { 102 | // Uncomment the below line to launch a debugger 103 | //System.Diagnostics.Debugger.Launch(); 104 | var t = new MyRewriter().Run(context); 105 | return t; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /MacroSharp.ExampleMacros/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MacroSharp.Examples")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MacroSharp.Examples")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("1b6fbd6e-83de-463e-b4f5-b1ac1956fa88")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MacroSharp.ExampleMacros/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /MacroSharp.Examples/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MacroSharp.Examples/MacroSharp.Examples.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {ACB9D7A1-BA0F-4C30-87FC-B367BA640687} 9 | Exe 10 | Properties 11 | MacroSharp.Examples 12 | MacroSharp.Examples 13 | v4.5 14 | 512 15 | $(RoslynBinaryPath) 16 | csc2.exe 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | ..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll 41 | True 42 | 43 | 44 | 45 | ..\packages\System.Reflection.Metadata.1.1.0-alpha-00012\lib\dotnet\System.Reflection.Metadata.dll 46 | True 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | {1b6fbd6e-83de-463e-b4f5-b1ac1956fa88} 67 | MacroSharp.ExampleMacros 68 | 69 | 70 | {42778190-193e-400f-891c-aca9d975e0aa} 71 | MacroSharp 72 | 73 | 74 | 75 | 82 | -------------------------------------------------------------------------------- /MacroSharp.Examples/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | // This needs to appear once in an assembly to have it run the macro plugin 8 | [assembly:MacroSharp.MacroPlugin] 9 | 10 | namespace MacroSharp.Examples 11 | { 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | var testNPC = new TestNPCMacro(); 17 | testNPC.PropertyChanged += TestNPC_PropertyChanged; 18 | testNPC.X = 5; 19 | testNPC.Name = "Roslyn"; 20 | testNPC.NameNoNPC = "This should not print"; 21 | testNPC.X = 10; 22 | Console.ReadLine(); 23 | } 24 | 25 | private static void TestNPC_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 26 | { 27 | Console.WriteLine("You changed " + e.PropertyName); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MacroSharp.Examples/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MacroSharp.Examples")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MacroSharp.Examples")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("acb9d7a1-ba0f-4c30-87fc-b367ba640687")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MacroSharp.Examples/TestNPCMacro.cs: -------------------------------------------------------------------------------- 1 | using MacroSharp.ExampleMacros; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MacroSharp.Examples 10 | { 11 | // Invoke the macro using its attribute 12 | [NotifyPropertyChanged] 13 | class TestNPCMacro 14 | { 15 | public int X { get; set; } 16 | public string Name { get; set; } 17 | private string _nameNoNPC; 18 | public string NameNoNPC 19 | { 20 | get 21 | { 22 | 23 | 24 | 25 | return _nameNoNPC; 26 | } 27 | set 28 | { 29 | _nameNoNPC = value; 30 | } 31 | } 32 | 33 | // This is optional, the macro will create it if necessary 34 | // We create it here because the example creates a warning if it is found 35 | public event PropertyChangedEventHandler PropertyChanged; 36 | } 37 | } -------------------------------------------------------------------------------- /MacroSharp.Examples/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /MacroSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.22823.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MacroSharp", "MacroSharp\MacroSharp.csproj", "{42778190-193E-400F-891C-ACA9D975E0AA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MacroSharp.ExampleMacros", "MacroSharp.ExampleMacros\MacroSharp.ExampleMacros.csproj", "{1B6FBD6E-83DE-463E-B4F5-B1AC1956FA88}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MacroSharp.Examples", "MacroSharp.Examples\MacroSharp.Examples.csproj", "{ACB9D7A1-BA0F-4C30-87FC-B367BA640687}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {42778190-193E-400F-891C-ACA9D975E0AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {42778190-193E-400F-891C-ACA9D975E0AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {42778190-193E-400F-891C-ACA9D975E0AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {42778190-193E-400F-891C-ACA9D975E0AA}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {1B6FBD6E-83DE-463E-B4F5-B1AC1956FA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {1B6FBD6E-83DE-463E-B4F5-B1AC1956FA88}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {1B6FBD6E-83DE-463E-B4F5-B1AC1956FA88}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {1B6FBD6E-83DE-463E-B4F5-B1AC1956FA88}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {ACB9D7A1-BA0F-4C30-87FC-B367BA640687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {ACB9D7A1-BA0F-4C30-87FC-B367BA640687}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {ACB9D7A1-BA0F-4C30-87FC-B367BA640687}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {ACB9D7A1-BA0F-4C30-87FC-B367BA640687}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /MacroSharp/AssemblyLoader.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Reflection; 10 | using Roslyn.Utilities; 11 | using System.Threading.Tasks; 12 | using System.Threading; 13 | using Microsoft.CodeAnalysis; 14 | using System.Reflection.PortableExecutable; 15 | using System.Reflection.Metadata; 16 | 17 | namespace MacroSharp 18 | { 19 | /// 20 | /// A combination of Roslyn's AbstractAnalyzerAssemblyLoader and ShadowCopyAnalyzerAssemblyLoader 21 | /// that supports assembly reloading. 22 | /// 23 | class AssemblyLoader : IDisposable 24 | { 25 | private struct AssemblyWithLastModified 26 | { 27 | public AssemblyWithLastModified(Assembly assembly, DateTime modified) 28 | { 29 | Assembly = assembly; 30 | LastModified = modified; 31 | } 32 | 33 | public readonly Assembly Assembly; 34 | public readonly DateTime LastModified; 35 | } 36 | 37 | private readonly Dictionary _pathsToAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); 38 | private readonly Dictionary _namesToAssemblies = new Dictionary(); 39 | private readonly List _dependencyPaths = new List(); 40 | private readonly object _guard = new object(); 41 | 42 | private bool _hookedAssemblyResolve; 43 | 44 | /// 45 | /// Implemented by derived types to handle the actual loading of an assembly from 46 | /// a file on disk, and any bookkeeping specific to the derived type. 47 | /// 48 | public void AddDependencyLocation(string fullPath) 49 | { 50 | if (fullPath == null) 51 | { 52 | throw new ArgumentNullException(nameof(fullPath)); 53 | } 54 | 55 | lock (_guard) 56 | { 57 | if (!_dependencyPaths.Contains(fullPath, StringComparer.OrdinalIgnoreCase)) 58 | { 59 | _dependencyPaths.Add(fullPath); 60 | } 61 | } 62 | } 63 | 64 | public Assembly LoadFromPath(string fullPath) 65 | { 66 | if (fullPath == null) 67 | { 68 | throw new ArgumentNullException(nameof(fullPath)); 69 | } 70 | 71 | Debug.Assert(Path.IsPathRooted(fullPath)); 72 | 73 | lock (_guard) 74 | { 75 | AssemblyWithLastModified assemblyLM; 76 | if (_pathsToAssemblies.TryGetValue(fullPath, out assemblyLM)) 77 | { 78 | if (assemblyLM.LastModified == GetLastModified(fullPath)) 79 | return assemblyLM.Assembly; 80 | } 81 | 82 | assemblyLM = LoadInternal(fullPath); 83 | 84 | if (!_hookedAssemblyResolve) 85 | { 86 | _hookedAssemblyResolve = true; 87 | 88 | AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 89 | } 90 | 91 | return assemblyLM.Assembly; 92 | } 93 | } 94 | 95 | private AssemblyWithLastModified LoadInternal(string fullPath) 96 | { 97 | Assembly assembly = LoadCore(fullPath); 98 | string assemblyName = assembly.FullName; 99 | DateTime lastModified = GetLastModified(fullPath); 100 | AssemblyWithLastModified assemblyWT = new AssemblyWithLastModified(assembly, lastModified); 101 | 102 | _pathsToAssemblies[fullPath] = assemblyWT; 103 | _namesToAssemblies[assemblyName] = assemblyWT; 104 | 105 | return assemblyWT; 106 | } 107 | 108 | private DateTime GetLastModified(string fullPath) => File.GetLastWriteTime(fullPath); 109 | 110 | /// 111 | /// Handler for . Delegates to 112 | /// and prevents exceptions from leaking out. 113 | /// 114 | private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 115 | { 116 | try 117 | { 118 | return AssemblyResolveInternal(args); 119 | } 120 | catch 121 | { 122 | return null; 123 | } 124 | } 125 | 126 | private Assembly AssemblyResolveInternal(ResolveEventArgs args) 127 | { 128 | string requestedNameWithPolicyApplied = AppDomain.CurrentDomain.ApplyPolicy(args.Name); 129 | 130 | lock (_guard) 131 | { 132 | AssemblyWithLastModified assemblyLM; 133 | if (_namesToAssemblies.TryGetValue(requestedNameWithPolicyApplied, out assemblyLM)) 134 | { 135 | return assemblyLM.Assembly; 136 | } 137 | 138 | AssemblyIdentity requestedAssemblyIdentity; 139 | if (!AssemblyIdentity.TryParseDisplayName(requestedNameWithPolicyApplied, out requestedAssemblyIdentity)) 140 | { 141 | return null; 142 | } 143 | 144 | foreach (string candidatePath in _dependencyPaths) 145 | { 146 | if (AssemblyAlreadyLoaded(candidatePath) || 147 | !FileMatchesAssemblyName(candidatePath, requestedAssemblyIdentity.Name)) 148 | { 149 | continue; 150 | } 151 | 152 | AssemblyIdentity candidateIdentity = TryGetAssemblyIdentity(candidatePath); 153 | 154 | if (requestedAssemblyIdentity.Equals(candidateIdentity)) 155 | { 156 | return LoadInternal(candidatePath).Assembly; 157 | } 158 | } 159 | 160 | return null; 161 | } 162 | } 163 | 164 | private bool AssemblyAlreadyLoaded(string path) 165 | { 166 | return _pathsToAssemblies.ContainsKey(path); 167 | } 168 | 169 | private bool FileMatchesAssemblyName(string path, string assemblySimpleName) 170 | { 171 | return Path.GetFileNameWithoutExtension(path).Equals(assemblySimpleName, StringComparison.OrdinalIgnoreCase); 172 | } 173 | 174 | private static AssemblyIdentity TryGetAssemblyIdentity(string filePath) 175 | { 176 | try 177 | { 178 | if (!File.Exists(filePath)) 179 | { 180 | return null; 181 | } 182 | 183 | using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) 184 | using (var peReader = new PEReader(stream)) 185 | { 186 | var metadataReader = peReader.GetMetadataReader(); 187 | 188 | AssemblyDefinition assemblyDefinition = metadataReader.GetAssemblyDefinition(); 189 | 190 | string name = metadataReader.GetString(assemblyDefinition.Name); 191 | Version version = assemblyDefinition.Version; 192 | 193 | StringHandle cultureHandle = assemblyDefinition.Culture; 194 | string cultureName = (!cultureHandle.IsNil) ? metadataReader.GetString(cultureHandle) : null; 195 | AssemblyFlags flags = assemblyDefinition.Flags; 196 | 197 | bool hasPublicKey = (flags & AssemblyFlags.PublicKey) != 0; 198 | BlobHandle publicKeyHandle = assemblyDefinition.PublicKey; 199 | ImmutableArray publicKeyOrToken = !publicKeyHandle.IsNil 200 | ? metadataReader.GetBlobBytes(publicKeyHandle).AsImmutableOrNull() 201 | : default(ImmutableArray); 202 | return new AssemblyIdentity(name, version, cultureName, publicKeyOrToken, hasPublicKey); 203 | } 204 | } 205 | catch { } 206 | 207 | return null; 208 | } 209 | 210 | // Below is a slightly modified copy of ShadowCopyAnalyzerAssemblyLoader. 211 | // See the remarks section above for a detailed discussion. 212 | 213 | /// 214 | /// The base directory for shadow copies. Each instance of 215 | /// gets its own 216 | /// subdirectory under this directory. This is also the starting point 217 | /// for scavenge operations. 218 | /// 219 | private readonly string _baseDirectory; 220 | 221 | /// 222 | /// The directory where this instance of 223 | /// will shadow-copy assemblies. 224 | /// 225 | private string _shadowCopyDirectory; 226 | private Mutex _shadowCopyDirectoryMutex; 227 | 228 | /// 229 | /// Used to generate unique names for per-assembly directories. 230 | /// 231 | private int _assemblyDirectoryId; 232 | 233 | public AssemblyLoader(string baseDirectory = null) 234 | { 235 | if (baseDirectory != null) 236 | { 237 | _baseDirectory = baseDirectory; 238 | } 239 | else 240 | { 241 | _baseDirectory = Path.Combine(Path.GetTempPath(), "MacroSharp", "AnalyzerShadowCopies"); 242 | } 243 | 244 | Directory.CreateDirectory(_baseDirectory); 245 | DeleteLeftoverDirectories(); 246 | } 247 | 248 | public void Dispose() 249 | { 250 | DeleteLeftoverDirectories(); 251 | } 252 | 253 | private void DeleteLeftoverDirectories() 254 | { 255 | foreach (var subDirectory in Directory.EnumerateDirectories(_baseDirectory)) 256 | { 257 | string name = Path.GetFileName(subDirectory).ToLowerInvariant(); 258 | Mutex mutex = null; 259 | try 260 | { 261 | // We only want to try deleting the directory if no one else is currently 262 | // using it. That is, if there is no corresponding mutex. 263 | if (!Mutex.TryOpenExisting(name, out mutex)) 264 | { 265 | ClearReadOnlyFlagOnFiles(subDirectory); 266 | Directory.Delete(subDirectory, recursive: true); 267 | } 268 | } 269 | catch 270 | { 271 | // If something goes wrong we will leave it to the next run to clean up. 272 | // Just swallow the exception and move on. 273 | } 274 | finally 275 | { 276 | if (mutex != null) 277 | { 278 | mutex.Dispose(); 279 | } 280 | } 281 | } 282 | } 283 | 284 | private Assembly LoadCore(string fullPath) 285 | { 286 | if (_shadowCopyDirectory == null) 287 | { 288 | _shadowCopyDirectory = CreateUniqueDirectoryForProcess(); 289 | } 290 | 291 | string assemblyDirectory = CreateUniqueDirectoryForAssembly(); 292 | string shadowCopyPath = CopyFileAndResources(fullPath, assemblyDirectory); 293 | 294 | return Assembly.LoadFile(shadowCopyPath); 295 | } 296 | 297 | private string CopyFileAndResources(string fullPath, string assemblyDirectory) 298 | { 299 | string fileNameWithExtension = Path.GetFileName(fullPath); 300 | string shadowCopyPath = Path.Combine(assemblyDirectory, fileNameWithExtension); 301 | 302 | CopyFile(fullPath, shadowCopyPath); 303 | 304 | string originalDirectory = Path.GetDirectoryName(fullPath); 305 | string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileNameWithExtension); 306 | string resourcesNameWithoutExtension = fileNameWithoutExtension + ".resources"; 307 | string resourcesNameWithExtension = resourcesNameWithoutExtension + ".dll"; 308 | 309 | foreach (var directory in Directory.EnumerateDirectories(originalDirectory)) 310 | { 311 | string directoryName = Path.GetFileName(directory); 312 | 313 | string resourcesPath = Path.Combine(directory, resourcesNameWithExtension); 314 | if (File.Exists(resourcesPath)) 315 | { 316 | string resourcesShadowCopyPath = Path.Combine(assemblyDirectory, directoryName, resourcesNameWithExtension); 317 | CopyFile(resourcesPath, resourcesShadowCopyPath); 318 | } 319 | 320 | resourcesPath = Path.Combine(directory, resourcesNameWithoutExtension, resourcesNameWithExtension); 321 | if (File.Exists(resourcesPath)) 322 | { 323 | string resourcesShadowCopyPath = Path.Combine(assemblyDirectory, directoryName, resourcesNameWithoutExtension, resourcesNameWithExtension); 324 | CopyFile(resourcesPath, resourcesShadowCopyPath); 325 | } 326 | } 327 | 328 | return shadowCopyPath; 329 | } 330 | 331 | private void CopyFile(string originalPath, string shadowCopyPath) 332 | { 333 | var directory = Path.GetDirectoryName(shadowCopyPath); 334 | Directory.CreateDirectory(directory); 335 | 336 | File.Copy(originalPath, shadowCopyPath); 337 | 338 | ClearReadOnlyFlagOnFile(new FileInfo(shadowCopyPath)); 339 | } 340 | 341 | private void ClearReadOnlyFlagOnFiles(string directoryPath) 342 | { 343 | DirectoryInfo directory = new DirectoryInfo(directoryPath); 344 | 345 | foreach (var file in directory.EnumerateFiles(searchPattern: "*", searchOption: SearchOption.AllDirectories)) 346 | { 347 | ClearReadOnlyFlagOnFile(file); 348 | } 349 | } 350 | 351 | private void ClearReadOnlyFlagOnFile(FileInfo fileInfo) 352 | { 353 | try 354 | { 355 | if (fileInfo.IsReadOnly) 356 | { 357 | fileInfo.IsReadOnly = false; 358 | } 359 | } 360 | catch 361 | { 362 | // There are many reasons this could fail. Ignore it and keep going. 363 | } 364 | } 365 | 366 | private string CreateUniqueDirectoryForAssembly() 367 | { 368 | int directoryId = _assemblyDirectoryId++; 369 | 370 | string directory = Path.Combine(_shadowCopyDirectory, directoryId.ToString()); 371 | 372 | Directory.CreateDirectory(directory); 373 | return directory; 374 | } 375 | 376 | private string CreateUniqueDirectoryForProcess() 377 | { 378 | string guid = Guid.NewGuid().ToString("N").ToLowerInvariant(); 379 | string directory = Path.Combine(_baseDirectory, guid); 380 | 381 | _shadowCopyDirectoryMutex = new Mutex(initiallyOwned: false, name: guid); 382 | 383 | Directory.CreateDirectory(directory); 384 | 385 | return directory; 386 | } 387 | } 388 | 389 | static class RoslynCompatibilityExtensions 390 | { 391 | public static ImmutableArray AsImmutableOrNull(this T[] items) 392 | { 393 | if (items == null) 394 | { 395 | return default(ImmutableArray); 396 | } 397 | 398 | return ImmutableArray.Create(items); 399 | } 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /MacroSharp/Attributes/MethodMacroAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MacroSharp 8 | { 9 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] 10 | public class MethodMacroAttribute : Attribute 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MacroSharp/Attributes/TypeMacroAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MacroSharp 8 | { 9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true, Inherited = true)] 10 | public class TypeMacroAttribute : Attribute 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MacroSharp/IMacroTransform.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MacroSharp 9 | { 10 | public interface IMacroTransform 11 | { 12 | SyntaxNode Transform(TransformContext context); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MacroSharp/MacroCSharpSyntaxRewriter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MacroSharp 10 | { 11 | public class MacroCSharpSyntaxRewriter : CSharpSyntaxRewriter 12 | { 13 | private const int MaxListRecursion = 20; 14 | 15 | protected TransformContext Context => _context; 16 | 17 | public virtual SyntaxNode Run(TransformContext context) 18 | { 19 | _context = context; 20 | return Visit(context.SyntaxNode); 21 | } 22 | 23 | private struct ListData 24 | { 25 | public List PrependedNodes; 26 | public List AppendedNodes; 27 | } 28 | 29 | public override SyntaxList VisitList(SyntaxList list) 30 | { 31 | _listDataIndex += 1; 32 | List alternate = null; 33 | for (int i = 0, n = list.Count; i < n; i++) 34 | { 35 | var item = list[i]; 36 | var visited = this.VisitListElement(item); 37 | var prepended = _listData[_listDataIndex].PrependedNodes; 38 | var appended = _listData[_listDataIndex].AppendedNodes; 39 | 40 | if (item != visited || 41 | (prepended != null && prepended.Count > 0) || 42 | (appended != null && appended.Count > 0)) 43 | { 44 | if (alternate == null) 45 | { 46 | alternate = new List(n); 47 | for (int j = 0; j < i; ++j) 48 | alternate.Add(list[j]); 49 | } 50 | 51 | if (prepended != null && prepended.Count > 0) 52 | { 53 | alternate.AddRange(prepended); 54 | prepended.Clear(); 55 | } 56 | 57 | if (visited != null && !visited.IsKind(SyntaxKind.None)) 58 | alternate.Add(visited); 59 | 60 | if (appended != null && appended.Count > 0) 61 | { 62 | alternate.AddRange(appended); 63 | appended.Clear(); 64 | } 65 | } 66 | else if (alternate != null && visited != null && !visited.IsKind(SyntaxKind.None)) 67 | alternate.Add(visited); 68 | } 69 | 70 | _listDataIndex -= 1; 71 | 72 | if (alternate != null) 73 | return SyntaxFactory.List(alternate); 74 | 75 | return list; 76 | } 77 | 78 | public void AddAfterCurrentNode(SyntaxNode node) 79 | { 80 | if (_listData[_listDataIndex].AppendedNodes == null) 81 | _listData[_listDataIndex].AppendedNodes = new List(); 82 | 83 | _listData[_listDataIndex].AppendedNodes.Add(node); 84 | } 85 | 86 | public void AddBeforeCurrentNode(SyntaxNode node) 87 | { 88 | if (_listData[_listDataIndex].PrependedNodes == null) 89 | _listData[_listDataIndex].PrependedNodes = new List(); 90 | 91 | _listData[_listDataIndex].PrependedNodes.Add(node); 92 | } 93 | 94 | private TransformContext _context; 95 | private int _listDataIndex = -1; 96 | private ListData[] _listData = new ListData[MaxListRecursion]; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /MacroSharp/MacroCompiler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.Plugins; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MacroSharp 10 | { 11 | class MacroCompiler : ICompilerPlugin 12 | { 13 | private AssemblyLoader _assemblyLoader; 14 | 15 | public MacroCompiler() 16 | { 17 | _assemblyLoader = new AssemblyLoader(); 18 | } 19 | 20 | public void BeforeCompile(BeforeCompileContext context) 21 | { 22 | var wellKnownTypes = new WellKnownTypes(context.Compilation); 23 | var registry = new MacroRegistry(context.Compilation, _assemblyLoader, wellKnownTypes); 24 | var symbolVisitor = new MacroSymbolVisitor(context.Compilation, wellKnownTypes, registry); 25 | 26 | foreach (var s in context.Compilation.SourceModule.GlobalNamespace.GetMembers()) 27 | s.Accept(symbolVisitor); 28 | 29 | context.Compilation = symbolVisitor.Compilation; 30 | foreach (var diagnostic in symbolVisitor.Diagnostics) 31 | context.Diagnostics.Add(diagnostic); 32 | } 33 | 34 | public void AfterCompile(AfterCompileContext context) 35 | { 36 | } 37 | 38 | public void Dispose() 39 | { 40 | _assemblyLoader.Dispose(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MacroSharp/MacroPluginAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.Plugins; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MacroSharp 9 | { 10 | public class MacroPluginAttribute : Attribute, ICompilerPluginAttribute 11 | { 12 | public ICompilerPlugin Create() 13 | { 14 | return new MacroCompiler(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MacroSharp/MacroRegistry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MacroSharp 11 | { 12 | class MacroRegistry 13 | { 14 | private class RegisteredAssembly 15 | { 16 | public readonly Assembly Assembly; 17 | public readonly Dictionary Macros; 18 | 19 | public RegisteredAssembly(Assembly assembly) 20 | { 21 | Assembly = assembly; 22 | Macros = new Dictionary(); 23 | } 24 | } 25 | 26 | private readonly Compilation _compilation; 27 | private readonly AssemblyLoader _assemblyLoader; 28 | private readonly WellKnownTypes _wellKnownTypes; 29 | private readonly Dictionary _registeredAssemblies; 30 | 31 | public MacroRegistry(Compilation compilation, AssemblyLoader assemblyLoader, WellKnownTypes wellKnownTypes) 32 | { 33 | _compilation = compilation; 34 | _assemblyLoader = assemblyLoader; 35 | _wellKnownTypes = wellKnownTypes; 36 | _registeredAssemblies = new Dictionary(); 37 | } 38 | 39 | public IMacroTransform GetMacroTransformFor(AttributeData attribute) 40 | { 41 | var containingAssembly = attribute.AttributeClass.ContainingAssembly; 42 | RegisteredAssembly registeredAssembly; 43 | 44 | if (!_registeredAssemblies.TryGetValue(containingAssembly, out registeredAssembly)) 45 | registeredAssembly = RegisterAssembly(containingAssembly); 46 | 47 | return registeredAssembly.Macros[attribute.AttributeClass.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)]; 48 | } 49 | 50 | private RegisteredAssembly RegisterAssembly(IAssemblySymbol assemblySymbol) 51 | { 52 | var macroAssemblyPath = _compilation.GetMetadataReference(assemblySymbol).Display; 53 | var macroAssembly = _assemblyLoader.LoadFromPath(macroAssemblyPath); 54 | var registeredAssembly = new RegisteredAssembly(macroAssembly); 55 | 56 | foreach (var type in macroAssembly.DefinedTypes) 57 | { 58 | var methodAttribute = GetFirstArgumentOfGenericAncestor(typeof(MacroTransform<>), type); 59 | if (methodAttribute != null) 60 | { 61 | registeredAssembly.Macros.Add("global::" + methodAttribute.FullName, (IMacroTransform)Activator.CreateInstance(type)); 62 | } 63 | } 64 | 65 | _registeredAssemblies.Add(assemblySymbol, registeredAssembly); 66 | return registeredAssembly; 67 | } 68 | 69 | static Type GetFirstArgumentOfGenericAncestor(Type generic, Type toCheck) 70 | { 71 | while (toCheck != null && toCheck != typeof(object)) 72 | { 73 | var toCheckInfo = toCheck.GetTypeInfo(); 74 | var cur = toCheckInfo.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 75 | if (generic == cur) 76 | { 77 | return toCheck.GenericTypeArguments[0]; 78 | } 79 | toCheck = toCheckInfo.BaseType; 80 | } 81 | return null; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /MacroSharp/MacroSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {42778190-193E-400F-891C-ACA9D975E0AA} 9 | Library 10 | Properties 11 | MacroSharp 12 | MacroSharp 13 | v4.5 14 | 512 15 | $(RoslynBinaryPath) 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | -------------------------------------------------------------------------------- /MacroSharp/MacroSymbolVisitor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MacroSharp 9 | { 10 | class MacroSymbolVisitor : SymbolVisitor 11 | { 12 | public struct SyntaxPair 13 | { 14 | public SyntaxPair(SyntaxNode oldNode, SyntaxNode newNode) 15 | { 16 | OldNode = oldNode; 17 | NewNode = newNode; 18 | } 19 | public SyntaxNode OldNode { get; } 20 | public SyntaxNode NewNode { get; } 21 | } 22 | 23 | private Compilation _compilation; 24 | private readonly WellKnownTypes _wellKnownTypes; 25 | private readonly MacroRegistry _registry; 26 | private readonly TransformContext _transformContext; 27 | private readonly List _diagnostics; 28 | 29 | public MacroSymbolVisitor(Compilation compilation, WellKnownTypes wellKnownTypes, MacroRegistry registry) 30 | { 31 | _compilation = compilation; 32 | _wellKnownTypes = wellKnownTypes; 33 | _registry = registry; 34 | _transformContext = new TransformContext(); 35 | _diagnostics = new List(); 36 | } 37 | 38 | public Dictionary> UpdatedNodes = new Dictionary>(); 39 | 40 | public override object VisitNamespace(INamespaceSymbol symbol) 41 | { 42 | foreach (var s in symbol.GetMembers()) 43 | s.Accept(this); 44 | 45 | return null; 46 | } 47 | 48 | public override object VisitNamedType(INamedTypeSymbol symbol) 49 | { 50 | if (TransformSymbolWithMacroAttribute(symbol, _wellKnownTypes.TypeMacroAttribute)) 51 | return null; 52 | 53 | foreach (var s in symbol.GetMembers()) 54 | s.Accept(this); 55 | 56 | return null; 57 | } 58 | 59 | public override object VisitMethod(IMethodSymbol method) 60 | { 61 | TransformSymbolWithMacroAttribute(method, _wellKnownTypes.MethodMacroAttribute); 62 | return null; 63 | } 64 | 65 | private bool TransformSymbolWithMacroAttribute(ISymbol symbol, INamedTypeSymbol macroAttribute) 66 | { 67 | var attributes = symbol.GetAttributes(); 68 | 69 | if (attributes.Length == 0) 70 | return false; 71 | 72 | var attributeData = attributes.SingleOrDefault(x => x.AttributeClass.BaseType == macroAttribute); 73 | 74 | if (attributeData == null) 75 | return false; 76 | 77 | var transform = _registry.GetMacroTransformFor(attributeData); 78 | 79 | // If there are multiple references, this must be a partial class, we can't transform that 80 | var syntaxReference = symbol.DeclaringSyntaxReferences.Single(); 81 | 82 | 83 | // This should be called before bind, which should not trigger a parse 84 | var originalNode = syntaxReference.GetSyntax(); 85 | 86 | _transformContext.Diagnostics.Clear(); 87 | _transformContext.Compilation = _compilation; 88 | _transformContext.AttributeData = attributeData; 89 | _transformContext.SyntaxNode = originalNode; 90 | _transformContext.Symbol = symbol; 91 | 92 | var transformed = transform.Transform(_transformContext); 93 | 94 | _diagnostics.AddRange(_transformContext.Diagnostics); 95 | 96 | if (transformed != null && transformed != originalNode) 97 | { 98 | var originalTree = originalNode.SyntaxTree; 99 | var originalRoot = originalTree.GetRoot(); 100 | var transformedRoot = originalRoot.ReplaceNode(originalNode, transformed); 101 | var transformedTree = originalTree.WithRootAndOptions(transformedRoot, originalTree.Options); 102 | _compilation = _compilation.ReplaceSyntaxTree(originalTree, transformedTree); 103 | 104 | // We will need to re-compute the semantic model since the compilation has been modified 105 | _transformContext.ResetSemanticModel(); 106 | 107 | return true; 108 | } 109 | else 110 | return false; 111 | } 112 | 113 | public Compilation Compilation => _compilation; 114 | public List Diagnostics => _diagnostics; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /MacroSharp/MacroSyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | using Microsoft.CodeAnalysis.Text; 9 | using Roslyn.Utilities; 10 | using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax; 11 | using Microsoft.CodeAnalysis; 12 | using Microsoft.CodeAnalysis.CSharp; 13 | using F = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 14 | 15 | namespace MacroSharp 16 | { 17 | /// 18 | /// A class containing factory methods for constructing syntax nodes, tokens and trivia. 19 | /// 20 | public static partial class MacroSyntaxFactory 21 | { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MacroSharp/MacroSyntaxFactoryFromRoslyn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | using Microsoft.CodeAnalysis.Text; 9 | using Roslyn.Utilities; 10 | using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax; 11 | using Microsoft.CodeAnalysis; 12 | using Microsoft.CodeAnalysis.CSharp; 13 | using F = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 14 | 15 | namespace MacroSharp 16 | { 17 | /// 18 | /// A class containing factory methods for constructing syntax nodes, tokens and trivia. 19 | /// 20 | public static partial class MacroSyntaxFactory 21 | { 22 | /// 23 | /// A trivia with kind EndOfLineTrivia containing both the carriage return and line feed characters. 24 | /// 25 | public static readonly SyntaxTrivia CarriageReturnLineFeed = F.CarriageReturnLineFeed; 26 | 27 | /// 28 | /// A trivia with kind EndOfLineTrivia containing a single line feed character. 29 | /// 30 | public static readonly SyntaxTrivia LineFeed = F.LineFeed; 31 | 32 | /// 33 | /// A trivia with kind EndOfLineTrivia containing a single carriage return character. 34 | /// 35 | public static readonly SyntaxTrivia CarriageReturn = F.CarriageReturn; 36 | 37 | /// 38 | /// A trivia with kind WhitespaceTrivia containing a single space character. 39 | /// 40 | public static readonly SyntaxTrivia Space = F.Space; 41 | 42 | /// 43 | /// A trivia with kind WhitespaceTrivia containing a single tab character. 44 | /// 45 | public static readonly SyntaxTrivia Tab = F.Tab; 46 | 47 | /// 48 | /// An elastic trivia with kind EndOfLineTrivia containing both the carriage return and line feed characters. 49 | /// Elastic trivia are used to denote trivia that was not produced by parsing source text, and are usually not 50 | /// preserved during formatting. 51 | /// 52 | public static readonly SyntaxTrivia ElasticCarriageReturnLineFeed = F.ElasticCarriageReturnLineFeed; 53 | 54 | /// 55 | /// An elastic trivia with kind EndOfLineTrivia containing a single line feed character. Elastic trivia are used 56 | /// to denote trivia that was not produced by parsing source text, and are usually not preserved during 57 | /// formatting. 58 | /// 59 | public static readonly SyntaxTrivia ElasticLineFeed = F.ElasticLineFeed; 60 | 61 | /// 62 | /// An elastic trivia with kind EndOfLineTrivia containing a single carriage return character. Elastic trivia 63 | /// are used to denote trivia that was not produced by parsing source text, and are usually not preserved during 64 | /// formatting. 65 | /// 66 | public static readonly SyntaxTrivia ElasticCarriageReturn = F.ElasticCarriageReturn; 67 | 68 | /// 69 | /// An elastic trivia with kind WhitespaceTrivia containing a single space character. Elastic trivia are used to 70 | /// denote trivia that was not produced by parsing source text, and are usually not preserved during formatting. 71 | /// 72 | public static readonly SyntaxTrivia ElasticSpace = F.ElasticSpace; 73 | 74 | /// 75 | /// An elastic trivia with kind WhitespaceTrivia containing a single tab character. Elastic trivia are used to 76 | /// denote trivia that was not produced by parsing source text, and are usually not preserved during formatting. 77 | /// 78 | public static readonly SyntaxTrivia ElasticTab = F.ElasticTab; 79 | 80 | /// 81 | /// An elastic trivia with kind WhitespaceTrivia containing no characters. Elastic marker trivia are included 82 | /// automatically by factory methods when trivia is not specified. Syntax formatting will replace elastic 83 | /// markers with appropriate trivia. 84 | /// 85 | public static readonly SyntaxTrivia ElasticMarker = F.ElasticMarker; 86 | 87 | /// 88 | /// Creates a trivia with kind EndOfLineTrivia containing the specified text. 89 | /// 90 | /// The text of the end of line. Any text can be specified here, however only carriage return and 91 | /// line feed characters are recognized by the parser as end of line. 92 | public static SyntaxTrivia EndOfLine(string text) => F.EndOfLine(text); 93 | 94 | /// 95 | /// Creates a trivia with kind EndOfLineTrivia containing the specified text. Elastic trivia are used to 96 | /// denote trivia that was not produced by parsing source text, and are usually not preserved during formatting. 97 | /// 98 | /// The text of the end of line. Any text can be specified here, however only carriage return and 99 | /// line feed characters are recognized by the parser as end of line. 100 | public static SyntaxTrivia ElasticEndOfLine(string text) => F.ElasticEndOfLine(text); 101 | 102 | /// 103 | /// Creates a trivia with kind WhitespaceTrivia containing the specified text. 104 | /// 105 | /// The text of the whitespace. Any text can be specified here, however only specific 106 | /// whitespace characters are recognized by the parser. 107 | public static SyntaxTrivia Whitespace(string text) => F.Whitespace(text); 108 | 109 | /// 110 | /// Creates a trivia with kind WhitespaceTrivia containing the specified text. Elastic trivia are used to 111 | /// denote trivia that was not produced by parsing source text, and are usually not preserved during formatting. 112 | /// 113 | /// The text of the whitespace. Any text can be specified here, however only specific 114 | /// whitespace characters are recognized by the parser. 115 | public static SyntaxTrivia ElasticWhitespace(string text) => F.ElasticWhitespace(text); 116 | 117 | /// 118 | /// Creates a trivia with kind either SingleLineCommentTrivia or MultiLineCommentTrivia containing the specified 119 | /// text. 120 | /// 121 | /// The entire text of the comment including the leading '//' token for single line comments 122 | /// or stop or start tokens for multiline comments. 123 | public static SyntaxTrivia Comment(string text) => F.Comment(text); 124 | 125 | /// 126 | /// Creates a trivia with kind DisabledTextTrivia. Disabled text corresponds to any text between directives that 127 | /// is not considered active. 128 | /// 129 | public static SyntaxTrivia DisabledText(string text) => F.DisabledText(text); 130 | 131 | /// 132 | /// Creates a trivia with kind PreprocessingMessageTrivia. 133 | /// 134 | public static SyntaxTrivia PreprocessingMessage(string text) => F.PreprocessingMessage(text); 135 | 136 | /// 137 | /// Trivia nodes represents parts of the program text that are not parts of the 138 | /// syntactic grammar, such as spaces, newlines, comments, preprocessors 139 | /// directives, and disabled code. 140 | /// 141 | /// 142 | /// A representing the specific kind of SyntaxTrivia. One of 143 | /// WhitespaceTrivia, EndOfLineTrivia, CommentTrivia, 144 | /// DocumentationCommentExteriorTrivia, DisabledTextTrivia. 145 | /// 146 | /// 147 | /// The actual text of this token. 148 | /// 149 | public static SyntaxTrivia SyntaxTrivia(SyntaxKind kind, string text) => F.SyntaxTrivia(kind, text); 150 | 151 | /// 152 | /// Creates a token corresponding to a syntax kind. This method can be used for token syntax kinds whose text 153 | /// can be inferred by the kind alone. 154 | /// 155 | /// A syntax kind value for a token. These have the suffix Token or Keyword. 156 | /// 157 | public static SyntaxToken Token(SyntaxKind kind) => F.Token(kind); 158 | 159 | /// 160 | /// Creates a token corresponding to syntax kind. This method can be used for token syntax kinds whose text can 161 | /// be inferred by the kind alone. 162 | /// 163 | /// A list of trivia immediately preceding the token. 164 | /// A syntax kind value for a token. These have the suffix Token or Keyword. 165 | /// A list of trivia immediately following the token. 166 | public static SyntaxToken Token(SyntaxTriviaList leading, SyntaxKind kind, SyntaxTriviaList trailing) 167 | { 168 | return F.Token(leading, kind, trailing); 169 | } 170 | 171 | /// 172 | /// Creates a token corresponding to syntax kind. This method gives control over token Text and ValueText. 173 | /// 174 | /// For example, consider the text '<see cref="operator &#43;"/>'. To create a token for the value of 175 | /// the operator symbol (&#43;), one would call 176 | /// Token(default(SyntaxTriviaList), SyntaxKind.PlusToken, "&#43;", "+", default(SyntaxTriviaList)). 177 | /// 178 | /// A list of trivia immediately preceding the token. 179 | /// A syntax kind value for a token. These have the suffix Token or Keyword. 180 | /// The text from which this this token was created (e.g. lexed). 181 | /// How C# should interpret the text of this token. 182 | /// A list of trivia immediately following the token. 183 | public static SyntaxToken Token(SyntaxTriviaList leading, SyntaxKind kind, string text, string valueText, SyntaxTriviaList trailing) 184 | { 185 | return F.Token(leading, kind, text, valueText, trailing); 186 | } 187 | 188 | /// 189 | /// Creates a missing token corresponding to syntax kind. A missing token is produced by the parser when an 190 | /// expected token is not found. A missing token has no text and normally has associated diagnostics. 191 | /// 192 | /// A syntax kind value for a token. These have the suffix Token or Keyword. 193 | public static SyntaxToken MissingToken(SyntaxKind kind) => F.MissingToken(kind); 194 | 195 | /// 196 | /// Creates a missing token corresponding to syntax kind. A missing token is produced by the parser when an 197 | /// expected token is not found. A missing token has no text and normally has associated diagnostics. 198 | /// 199 | /// A list of trivia immediately preceding the token. 200 | /// A syntax kind value for a token. These have the suffix Token or Keyword. 201 | /// A list of trivia immediately following the token. 202 | public static SyntaxToken MissingToken(SyntaxTriviaList leading, SyntaxKind kind, SyntaxTriviaList trailing) 203 | { 204 | return F.MissingToken(leading, kind, trailing); 205 | } 206 | 207 | /// 208 | /// Creates a token with kind IdentifierToken containing the specified text. 209 | /// The raw text of the identifier name, including any escapes or leading '@' 210 | /// character. 211 | /// 212 | public static SyntaxToken Identifier(string text) => F.Identifier(text); 213 | 214 | /// 215 | /// Creates a token with kind IdentifierToken containing the specified text. 216 | /// 217 | /// A list of trivia immediately preceding the token. 218 | /// The raw text of the identifier name, including any escapes or leading '@' 219 | /// character. 220 | /// A list of trivia immediately following the token. 221 | public static SyntaxToken Identifier(SyntaxTriviaList leading, string text, SyntaxTriviaList trailing) 222 | { 223 | return F.Identifier(leading, text, trailing); 224 | } 225 | 226 | /// 227 | /// Creates a verbatim token with kind IdentifierToken containing the specified text. 228 | /// 229 | /// A list of trivia immediately preceding the token. 230 | /// The raw text of the identifier name, including any escapes or leading '@' 231 | /// character as it is in source. 232 | /// The canonical value of the token's text. 233 | /// A list of trivia immediately following the token. 234 | public static SyntaxToken VerbatimIdentifier(SyntaxTriviaList leading, string text, string valueText, SyntaxTriviaList trailing) 235 | { 236 | return F.VerbatimIdentifier(leading, text, valueText, trailing); 237 | } 238 | 239 | /// 240 | /// Creates a token with kind IdentifierToken containing the specified text. 241 | /// 242 | /// A list of trivia immediately preceding the token. 243 | /// An alternative SyntaxKind that can be inferred for this token in special 244 | /// contexts. These are usually keywords. 245 | /// The raw text of the identifier name, including any escapes or leading '@' 246 | /// character. 247 | /// The text of the identifier name without escapes or leading '@' character. 248 | /// A list of trivia immediately following the token. 249 | /// 250 | public static SyntaxToken Identifier(SyntaxTriviaList leading, SyntaxKind contextualKind, string text, string valueText, SyntaxTriviaList trailing) 251 | { 252 | return F.Identifier(leading, contextualKind, text, valueText, trailing); 253 | } 254 | 255 | /// 256 | /// Creates a token with kind NumericLiteralToken from a 4-byte signed integer value. 257 | /// 258 | /// The 4-byte signed integer value to be represented by the returned token. 259 | public static SyntaxToken Literal(int value) => F.Literal(value); 260 | 261 | /// 262 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 4-byte signed integer value. 263 | /// 264 | /// The raw text of the literal. 265 | /// The 4-byte signed integer value to be represented by the returned token. 266 | public static SyntaxToken Literal(string text, int value) => F.Literal(text, value); 267 | 268 | /// 269 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 4-byte signed integer value. 270 | /// 271 | /// A list of trivia immediately preceding the token. 272 | /// The raw text of the literal. 273 | /// The 4-byte signed integer value to be represented by the returned token. 274 | /// A list of trivia immediately following the token. 275 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, int value, SyntaxTriviaList trailing) 276 | { 277 | return F.Literal(leading, text, value, trailing); 278 | } 279 | 280 | /// 281 | /// Creates a token with kind NumericLiteralToken from a 4-byte unsigned integer value. 282 | /// 283 | /// The 4-byte unsigned integer value to be represented by the returned token. 284 | public static SyntaxToken Literal(uint value) => F.Literal(value); 285 | 286 | /// 287 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 4-byte unsigned integer value. 288 | /// 289 | /// The raw text of the literal. 290 | /// The 4-byte unsigned integer value to be represented by the returned token. 291 | public static SyntaxToken Literal(string text, uint value) => F.Literal(text, value); 292 | 293 | /// 294 | /// Creates a token with kind NumericLiteraToken from the text and corresponding 4-byte unsigned integer value. 295 | /// 296 | /// A list of trivia immediately preceding the token. 297 | /// The raw text of the literal. 298 | /// The 4-byte unsigned integer value to be represented by the returned token. 299 | /// A list of trivia immediately following the token. 300 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, uint value, SyntaxTriviaList trailing) 301 | { 302 | return F.Literal(leading, text, value, trailing); 303 | } 304 | 305 | /// 306 | /// Creates a token with kind NumericLiteralToken from an 8-byte signed integer value. 307 | /// 308 | /// The 8-byte signed integer value to be represented by the returned token. 309 | public static SyntaxToken Literal(long value) => F.Literal(value); 310 | 311 | /// 312 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 8-byte signed integer value. 313 | /// 314 | /// The raw text of the literal. 315 | /// The 8-byte signed integer value to be represented by the returned token. 316 | public static SyntaxToken Literal(string text, long value) => F.Literal(text, value); 317 | 318 | /// 319 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 8-byte signed integer value. 320 | /// 321 | /// A list of trivia immediately preceding the token. 322 | /// The raw text of the literal. 323 | /// The 8-byte signed integer value to be represented by the returned token. 324 | /// A list of trivia immediately following the token. 325 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, long value, SyntaxTriviaList trailing) 326 | { 327 | return F.Literal(leading, text, value, trailing); 328 | } 329 | 330 | /// 331 | /// Creates a token with kind NumericLiteralToken from an 8-byte unsigned integer value. 332 | /// 333 | /// The 8-byte unsigned integer value to be represented by the returned token. 334 | public static SyntaxToken Literal(ulong value) => F.Literal(value); 335 | 336 | /// 337 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 8-byte unsigned integer value. 338 | /// 339 | /// The raw text of the literal. 340 | /// The 8-byte unsigned integer value to be represented by the returned token. 341 | public static SyntaxToken Literal(string text, ulong value) => F.Literal(text, value); 342 | 343 | /// 344 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 8-byte unsigned integer value. 345 | /// 346 | /// A list of trivia immediately preceding the token. 347 | /// The raw text of the literal. 348 | /// The 8-byte unsigned integer value to be represented by the returned token. 349 | /// A list of trivia immediately following the token. 350 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, ulong value, SyntaxTriviaList trailing) 351 | { 352 | return F.Literal(leading, text, value, trailing); 353 | } 354 | 355 | /// 356 | /// Creates a token with kind NumericLiteralToken from a 4-byte floating point value. 357 | /// 358 | /// The 4-byte floating point value to be represented by the returned token. 359 | public static SyntaxToken Literal(float value) => F.Literal(value); 360 | 361 | /// 362 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 4-byte floating point value. 363 | /// 364 | /// The raw text of the literal. 365 | /// The 4-byte floating point value to be represented by the returned token. 366 | public static SyntaxToken Literal(string text, float value) => F.Literal(text, value); 367 | 368 | /// 369 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 4-byte floating point value. 370 | /// 371 | /// A list of trivia immediately preceding the token. 372 | /// The raw text of the literal. 373 | /// The 4-byte floating point value to be represented by the returned token. 374 | /// A list of trivia immediately following the token. 375 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, float value, SyntaxTriviaList trailing) 376 | { 377 | return F.Literal(leading, text, value, trailing); 378 | } 379 | 380 | /// 381 | /// Creates a token with kind NumericLiteralToken from an 8-byte floating point value. 382 | /// 383 | /// The 8-byte floating point value to be represented by the returned token. 384 | public static SyntaxToken Literal(double value) => F.Literal(value); 385 | 386 | /// 387 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 8-byte floating point value. 388 | /// 389 | /// The raw text of the literal. 390 | /// The 8-byte floating point value to be represented by the returned token. 391 | public static SyntaxToken Literal(string text, double value) => F.Literal(text, value); 392 | 393 | /// 394 | /// Creates a token with kind NumericLiteralToken from the text and corresponding 8-byte floating point value. 395 | /// 396 | /// A list of trivia immediately preceding the token. 397 | /// The raw text of the literal. 398 | /// The 8-byte floating point value to be represented by the returned token. 399 | /// A list of trivia immediately following the token. 400 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, double value, SyntaxTriviaList trailing) 401 | { 402 | return F.Literal(leading, text, value, trailing); 403 | } 404 | 405 | /// 406 | /// Creates a token with kind NumericLiteralToken from a decimal value. 407 | /// 408 | /// The decimal value to be represented by the returned token. 409 | public static SyntaxToken Literal(decimal value) => F.Literal(value); 410 | 411 | /// 412 | /// Creates a token with kind NumericLiteralToken from the text and corresponding decimal value. 413 | /// 414 | /// The raw text of the literal. 415 | /// The decimal value to be represented by the returned token. 416 | public static SyntaxToken Literal(string text, decimal value) => F.Literal(text, value); 417 | 418 | /// 419 | /// Creates a token with kind NumericLiteralToken from the text and corresponding decimal value. 420 | /// 421 | /// A list of trivia immediately preceding the token. 422 | /// The raw text of the literal. 423 | /// The decimal value to be represented by the returned token. 424 | /// A list of trivia immediately following the token. 425 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, decimal value, SyntaxTriviaList trailing) 426 | { 427 | return F.Literal(leading, text, value, trailing); 428 | } 429 | 430 | /// 431 | /// Creates a token with kind StringLiteralToken from a string value. 432 | /// 433 | /// The string value to be represented by the returned token. 434 | public static SyntaxToken Literal(string value) => F.Literal(value); 435 | 436 | /// 437 | /// Creates a token with kind StringLiteralToken from the text and corresponding string value. 438 | /// 439 | /// The raw text of the literal, including quotes and escape sequences. 440 | /// The string value to be represented by the returned token. 441 | public static SyntaxToken Literal(string text, string value) => F.Literal(text, value); 442 | 443 | /// 444 | /// Creates a token with kind StringLiteralToken from the text and corresponding string value. 445 | /// 446 | /// A list of trivia immediately preceding the token. 447 | /// The raw text of the literal, including quotes and escape sequences. 448 | /// The string value to be represented by the returned token. 449 | /// A list of trivia immediately following the token. 450 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, string value, SyntaxTriviaList trailing) 451 | { 452 | return F.Literal(leading, text, value, trailing); 453 | } 454 | 455 | /// 456 | /// Creates a token with kind CharacterLiteralToken from a character value. 457 | /// 458 | /// The character value to be represented by the returned token. 459 | public static SyntaxToken Literal(char value) => F.Literal(value); 460 | 461 | /// 462 | /// Creates a token with kind CharacterLiteralToken from the text and corresponding character value. 463 | /// 464 | /// The raw text of the literal, including quotes and escape sequences. 465 | /// The character value to be represented by the returned token. 466 | public static SyntaxToken Literal(string text, char value) => F.Literal(text, value); 467 | 468 | /// 469 | /// Creates a token with kind CharacterLiteralToken from the text and corresponding character value. 470 | /// 471 | /// A list of trivia immediately preceding the token. 472 | /// The raw text of the literal, including quotes and escape sequences. 473 | /// The character value to be represented by the returned token. 474 | /// A list of trivia immediately following the token. 475 | public static SyntaxToken Literal(SyntaxTriviaList leading, string text, char value, SyntaxTriviaList trailing) 476 | { 477 | return F.Literal(leading, text, value, trailing); 478 | } 479 | 480 | /// 481 | /// Creates a token with kind BadToken. 482 | /// 483 | /// A list of trivia immediately preceding the token. 484 | /// The raw text of the bad token. 485 | /// A list of trivia immediately following the token. 486 | public static SyntaxToken BadToken(SyntaxTriviaList leading, string text, SyntaxTriviaList trailing) 487 | { 488 | return F.BadToken(leading, text, trailing); 489 | } 490 | 491 | /// 492 | /// Creates a token with kind XmlTextLiteralToken. 493 | /// 494 | /// A list of trivia immediately preceding the token. 495 | /// The raw text of the literal. 496 | /// The xml text value. 497 | /// A list of trivia immediately following the token. 498 | public static SyntaxToken XmlTextLiteral(SyntaxTriviaList leading, string text, string value, SyntaxTriviaList trailing) 499 | { 500 | return F.XmlTextLiteral(leading, text, value, trailing); 501 | } 502 | 503 | /// 504 | /// Creates a token with kind XmlTextLiteralNewLineToken. 505 | /// 506 | /// A list of trivia immediately preceding the token. 507 | /// The raw text of the literal. 508 | /// The xml text new line value. 509 | /// A list of trivia immediately following the token. 510 | public static SyntaxToken XmlTextNewLine(SyntaxTriviaList leading, string text, string value, SyntaxTriviaList trailing) 511 | { 512 | return F.XmlTextNewLine(leading, text, value, trailing); 513 | } 514 | 515 | /// 516 | /// Creates a token with kind XmlEntityLiteralToken. 517 | /// 518 | /// A list of trivia immediately preceding the token. 519 | /// The raw text of the literal. 520 | /// The xml entity value. 521 | /// A list of trivia immediately following the token. 522 | public static SyntaxToken XmlEntity(SyntaxTriviaList leading, string text, string value, SyntaxTriviaList trailing) 523 | { 524 | return F.XmlEntity(leading, text, value, trailing); 525 | } 526 | 527 | /// 528 | /// Creates a trivia with kind DocumentationCommentExteriorTrivia. 529 | /// 530 | /// The raw text of the literal. 531 | public static SyntaxTrivia DocumentationCommentExterior(string text) 532 | { 533 | return F.DocumentationCommentExterior(text); 534 | } 535 | 536 | /// 537 | /// Creates an empty list of syntax nodes. 538 | /// 539 | /// The specific type of the element nodes. 540 | public static SyntaxList List() where TNode : SyntaxNode 541 | { 542 | return F.List(); 543 | } 544 | 545 | /// 546 | /// Creates a singleton list of syntax nodes. 547 | /// 548 | /// The specific type of the element nodes. 549 | /// The single element node. 550 | /// 551 | public static SyntaxList SingletonList(TNode node) where TNode : SyntaxNode 552 | { 553 | return F.SingletonList(node); 554 | } 555 | 556 | 557 | /// 558 | /// Creates a list of syntax nodes. 559 | /// 560 | /// The specific type of the element nodes. 561 | /// A sequence of element nodes. 562 | public static SyntaxList List(IEnumerable nodes) where TNode : SyntaxNode 563 | { 564 | return F.List(nodes); 565 | } 566 | 567 | /// 568 | /// Creates an empty list of tokens. 569 | /// 570 | public static SyntaxTokenList TokenList() 571 | { 572 | return F.TokenList(); 573 | } 574 | 575 | /// 576 | /// Creates a singleton list of tokens. 577 | /// 578 | /// The single token. 579 | public static SyntaxTokenList TokenList(SyntaxToken token) 580 | { 581 | return F.TokenList(token); 582 | } 583 | 584 | /// 585 | /// Creates a list of tokens. 586 | /// 587 | /// An array of tokens. 588 | public static SyntaxTokenList TokenList(params SyntaxToken[] tokens) 589 | { 590 | return F.TokenList(tokens); 591 | } 592 | 593 | /// 594 | /// Creates a list of tokens. 595 | /// 596 | /// 597 | /// 598 | public static SyntaxTokenList TokenList(IEnumerable tokens) 599 | { 600 | return F.TokenList(tokens); 601 | } 602 | 603 | /// 604 | /// Creates a trivia from a StructuredTriviaSyntax node. 605 | /// 606 | public static SyntaxTrivia Trivia(StructuredTriviaSyntax node) 607 | { 608 | return F.Trivia(node); 609 | } 610 | 611 | /// 612 | /// Creates an empty list of trivia. 613 | /// 614 | public static SyntaxTriviaList TriviaList() 615 | { 616 | return F.TriviaList(); 617 | } 618 | 619 | /// 620 | /// Creates a singleton list of trivia. 621 | /// 622 | /// A single trivia. 623 | public static SyntaxTriviaList TriviaList(SyntaxTrivia trivia) 624 | { 625 | return F.TriviaList(trivia); 626 | } 627 | 628 | /// 629 | /// Creates a list of trivia. 630 | /// 631 | /// An array of trivia. 632 | public static SyntaxTriviaList TriviaList(params SyntaxTrivia[] trivias) 633 | { 634 | return F.TriviaList(trivias); 635 | } 636 | 637 | /// 638 | /// Creates a list of trivia. 639 | /// 640 | /// A sequence of trivia. 641 | public static SyntaxTriviaList TriviaList(IEnumerable trivias) 642 | { 643 | return F.TriviaList(trivias); 644 | } 645 | 646 | /// 647 | /// Creates an empty separated list. 648 | /// 649 | /// The specific type of the element nodes. 650 | public static SeparatedSyntaxList SeparatedList() where TNode : SyntaxNode 651 | { 652 | return F.SeparatedList(); 653 | } 654 | 655 | /// 656 | /// Creates a singleton separated list. 657 | /// 658 | /// The specific type of the element nodes. 659 | /// A single node. 660 | public static SeparatedSyntaxList SingletonSeparatedList(TNode node) where TNode : SyntaxNode 661 | { 662 | return F.SingletonSeparatedList(node); 663 | } 664 | 665 | /// 666 | /// Creates a separated list of nodes from a sequence of nodes, synthesizing comma separators in between. 667 | /// 668 | /// The specific type of the element nodes. 669 | /// A sequence of syntax nodes. 670 | public static SeparatedSyntaxList SeparatedList(IEnumerable nodes) where TNode : SyntaxNode 671 | { 672 | return F.SeparatedList(nodes); 673 | } 674 | 675 | /// 676 | /// Creates a separated list of nodes from a sequence of nodes and a sequence of separator tokens. 677 | /// 678 | /// The specific type of the element nodes. 679 | /// A sequence of syntax nodes. 680 | /// A sequence of token to be interleaved between the nodes. The number of tokens must 681 | /// be one less than the number of nodes. 682 | public static SeparatedSyntaxList SeparatedList(IEnumerable nodes, IEnumerable separators) where TNode : SyntaxNode 683 | { 684 | return F.SeparatedList(nodes, separators); 685 | } 686 | 687 | /// 688 | /// Creates a separated list from a sequence of nodes and tokens, starting with a node and alternating between additional nodes and separator tokens. 689 | /// 690 | /// The specific type of the element nodes. 691 | /// A sequence of nodes or tokens, alternating between nodes and separator tokens. 692 | public static SeparatedSyntaxList SeparatedList(IEnumerable nodesAndTokens) where TNode : SyntaxNode 693 | { 694 | return F.SeparatedList(nodesAndTokens); 695 | } 696 | 697 | /// 698 | /// Creates a separated list from a , where the list elements start with a node and then alternate between 699 | /// additional nodes and separator tokens. 700 | /// 701 | /// The specific type of the element nodes. 702 | /// The list of nodes and tokens. 703 | public static SeparatedSyntaxList SeparatedList(SyntaxNodeOrTokenList nodesAndTokens) where TNode : SyntaxNode 704 | { 705 | return F.SeparatedList(nodesAndTokens); 706 | } 707 | 708 | /// 709 | /// Create a from a sequence of . 710 | /// 711 | /// The sequence of nodes and tokens 712 | public static SyntaxNodeOrTokenList NodeOrTokenList(IEnumerable nodesAndTokens) 713 | { 714 | return F.NodeOrTokenList(nodesAndTokens); 715 | } 716 | 717 | /// 718 | /// Create a from one or more . 719 | /// 720 | /// The nodes and tokens 721 | public static SyntaxNodeOrTokenList NodeOrTokenList(params SyntaxNodeOrToken[] nodesAndTokens) 722 | { 723 | return F.NodeOrTokenList(nodesAndTokens); 724 | } 725 | 726 | /// 727 | /// Creates an IdentifierNameSyntax node. 728 | /// 729 | /// The identifier name. 730 | public static IdentifierNameSyntax IdentifierName(string name) 731 | { 732 | return F.IdentifierName(name); 733 | } 734 | 735 | // direct access to parsing for common grammar areas 736 | 737 | /// 738 | /// Create a new syntax tree from a syntax node. 739 | /// 740 | public static SyntaxTree SyntaxTree(SyntaxNode root, ParseOptions options = null, string path = "", Encoding encoding = null) 741 | { 742 | return F.SyntaxTree(root, options, path, encoding); 743 | } 744 | 745 | /// 746 | /// Produces a syntax tree by parsing the source text. 747 | /// 748 | public static SyntaxTree ParseSyntaxTree( 749 | string text, 750 | ParseOptions options = null, 751 | string path = "", 752 | Encoding encoding = null, 753 | CancellationToken cancellationToken = default(CancellationToken)) 754 | { 755 | return F.ParseSyntaxTree(text, options, path, encoding, cancellationToken); 756 | } 757 | 758 | /// 759 | /// Produces a syntax tree by parsing the source text. 760 | /// 761 | public static SyntaxTree ParseSyntaxTree( 762 | SourceText text, 763 | ParseOptions options = null, 764 | string path = "", 765 | CancellationToken cancellationToken = default(CancellationToken)) 766 | { 767 | return F.ParseSyntaxTree(text, options, path, cancellationToken); 768 | } 769 | 770 | /// 771 | /// Parse a list of trivia rules for leading trivia. 772 | /// 773 | public static SyntaxTriviaList ParseLeadingTrivia(string text, int offset = 0) 774 | { 775 | return F.ParseLeadingTrivia(text, offset); 776 | } 777 | 778 | /// 779 | /// Parse a list of trivia using the parsing rules for trailing trivia. 780 | /// 781 | public static SyntaxTriviaList ParseTrailingTrivia(string text, int offset = 0) 782 | { 783 | return F.ParseTrailingTrivia(text, offset); 784 | } 785 | 786 | /// 787 | /// Parse a C# language token. 788 | /// 789 | /// The text of the token including leading and trailing trivia. 790 | /// Optional offset into text. 791 | public static SyntaxToken ParseToken(string text, int offset = 0) 792 | { 793 | return F.ParseToken(text, offset); 794 | } 795 | 796 | /// 797 | /// Parse a sequence of C# language tokens. 798 | /// 799 | /// The text of all the tokens. 800 | /// An integer to use as the starting position of the first token. 801 | /// Optional offset into text. 802 | /// Parse options. 803 | public static IEnumerable ParseTokens(string text, int offset = 0, int initialTokenPosition = 0, CSharpParseOptions options = null) 804 | { 805 | return F.ParseTokens(text, offset, initialTokenPosition, options); 806 | } 807 | 808 | /// 809 | /// Parse a NameSyntax node using the grammar rule for names. 810 | /// 811 | public static NameSyntax ParseName(string text, int offset = 0, bool consumeFullText = true) 812 | { 813 | return F.ParseName(text, offset, consumeFullText); 814 | } 815 | 816 | /// 817 | /// Parse a TypeNameSyntax node using the grammar rule for type names. 818 | /// 819 | public static TypeSyntax ParseTypeName(string text, int offset = 0, bool consumeFullText = true) 820 | { 821 | return F.ParseTypeName(text, offset, consumeFullText); 822 | } 823 | 824 | /// 825 | /// Parse an ExpressionSyntax node using the lowest precedence grammar rule for expressions. 826 | /// 827 | /// The text of the expression. 828 | /// Optional offset into text. 829 | /// The optional parse options to use. If no options are specified default options are 830 | /// used. 831 | /// True if extra tokens in the input should be treated as an error 832 | public static ExpressionSyntax ParseExpression(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 833 | { 834 | return F.ParseExpression(text, offset, options, consumeFullText); 835 | } 836 | 837 | /// 838 | /// Parse a StatementSyntaxNode using grammar rule for statements. 839 | /// 840 | /// The text of the statement. 841 | /// Optional offset into text. 842 | /// The optional parse options to use. If no options are specified default options are 843 | /// used. 844 | /// True if extra tokens in the input should be treated as an error 845 | public static StatementSyntax ParseStatement(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 846 | { 847 | return F.ParseStatement(text, offset, options, consumeFullText); 848 | } 849 | 850 | /// 851 | /// Parse a CompilationUnitSyntax using the grammar rule for an entire compilation unit (file). To produce a 852 | /// SyntaxTree instance, use CSharpSyntaxTree.ParseText instead. 853 | /// 854 | /// The text of the compilation unit. 855 | /// Optional offset into text. 856 | /// The optional parse options to use. If no options are specified default options are 857 | /// used. 858 | public static CompilationUnitSyntax ParseCompilationUnit(string text, int offset = 0, CSharpParseOptions options = null) 859 | { 860 | return F.ParseCompilationUnit(text, offset, options); 861 | } 862 | 863 | /// 864 | /// Parse a ParameterListSyntax node. 865 | /// 866 | /// The text of the parenthesized parameter list. 867 | /// Optional offset into text. 868 | /// The optional parse options to use. If no options are specified default options are 869 | /// used. 870 | /// True if extra tokens in the input should be treated as an error 871 | public static ParameterListSyntax ParseParameterList(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 872 | { 873 | return F.ParseParameterList(text, offset, options, consumeFullText); 874 | } 875 | 876 | /// 877 | /// Parse a BracketedParameterListSyntax node. 878 | /// 879 | /// The text of the bracketed parameter list. 880 | /// Optional offset into text. 881 | /// The optional parse options to use. If no options are specified default options are 882 | /// used. 883 | /// True if extra tokens in the input should be treated as an error 884 | public static BracketedParameterListSyntax ParseBracketedParameterList(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 885 | { 886 | return F.ParseBracketedParameterList(text, offset, options, consumeFullText); 887 | } 888 | 889 | /// 890 | /// Parse an ArgumentListSyntax node. 891 | /// 892 | /// The text of the parenthesized argument list. 893 | /// Optional offset into text. 894 | /// The optional parse options to use. If no options are specified default options are 895 | /// used. 896 | /// True if extra tokens in the input should be treated as an error 897 | public static ArgumentListSyntax ParseArgumentList(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 898 | { 899 | return F.ParseArgumentList(text, offset, options, consumeFullText); 900 | } 901 | 902 | /// 903 | /// Parse a BracketedArgumentListSyntax node. 904 | /// 905 | /// The text of the bracketed argument list. 906 | /// Optional offset into text. 907 | /// The optional parse options to use. If no options are specified default options are 908 | /// used. 909 | /// True if extra tokens in the input should be treated as an error 910 | public static BracketedArgumentListSyntax ParseBracketedArgumentList(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 911 | { 912 | return F.ParseBracketedArgumentList(text, offset, options, consumeFullText); 913 | } 914 | 915 | /// 916 | /// Parse an AttributeArgumentListSyntax node. 917 | /// 918 | /// The text of the attribute argument list. 919 | /// Optional offset into text. 920 | /// The optional parse options to use. If no options are specified default options are 921 | /// used. 922 | /// True if extra tokens in the input should be treated as an error 923 | public static AttributeArgumentListSyntax ParseAttributeArgumentList(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true) 924 | { 925 | return F.ParseAttributeArgumentList(text, offset, options, consumeFullText); 926 | } 927 | 928 | /// 929 | /// Determines if two trees are the same, disregarding trivia differences. 930 | /// 931 | /// The original tree. 932 | /// The new tree. 933 | /// 934 | /// If true then the trees are equivalent if the contained nodes and tokens declaring 935 | /// metadata visible symbolic information are equivalent, ignoring any differences of nodes inside method bodies 936 | /// or initializer expressions, otherwise all nodes and tokens must be equivalent. 937 | /// 938 | public static bool AreEquivalent(SyntaxTree oldTree, SyntaxTree newTree, bool topLevel) 939 | { 940 | return F.AreEquivalent(oldTree, newTree, topLevel); 941 | } 942 | 943 | /// 944 | /// Determines if two syntax nodes are the same, disregarding trivia differences. 945 | /// 946 | /// The old node. 947 | /// The new node. 948 | /// 949 | /// If true then the nodes are equivalent if the contained nodes and tokens declaring 950 | /// metadata visible symbolic information are equivalent, ignoring any differences of nodes inside method bodies 951 | /// or initializer expressions, otherwise all nodes and tokens must be equivalent. 952 | /// 953 | public static bool AreEquivalent(SyntaxNode oldNode, SyntaxNode newNode, bool topLevel) 954 | { 955 | return F.AreEquivalent(oldNode, newNode, topLevel); 956 | } 957 | 958 | /// 959 | /// Determines if two syntax nodes are the same, disregarding trivia differences. 960 | /// 961 | /// The old node. 962 | /// The new node. 963 | /// 964 | /// If specified called for every child syntax node (not token) that is visited during the comparison. 965 | /// It it returns true the child is recursively visited, otherwise the child and its subtree is disregarded. 966 | /// 967 | public static bool AreEquivalent(SyntaxNode oldNode, SyntaxNode newNode, Func ignoreChildNode = null) 968 | { 969 | return F.AreEquivalent(oldNode, newNode, ignoreChildNode); 970 | } 971 | 972 | /// 973 | /// Determines if two syntax tokens are the same, disregarding trivia differences. 974 | /// 975 | /// The old token. 976 | /// The new token. 977 | public static bool AreEquivalent(SyntaxToken oldToken, SyntaxToken newToken) 978 | { 979 | return F.AreEquivalent(oldToken, newToken); 980 | } 981 | 982 | /// 983 | /// Determines if two lists of tokens are the same, disregarding trivia differences. 984 | /// 985 | /// The old token list. 986 | /// The new token list. 987 | public static bool AreEquivalent(SyntaxTokenList oldList, SyntaxTokenList newList) 988 | { 989 | return F.AreEquivalent(oldList, newList); 990 | } 991 | 992 | /// 993 | /// Determines if two lists of syntax nodes are the same, disregarding trivia differences. 994 | /// 995 | /// The old list. 996 | /// The new list. 997 | /// 998 | /// If true then the nodes are equivalent if the contained nodes and tokens declaring 999 | /// metadata visible symbolic information are equivalent, ignoring any differences of nodes inside method bodies 1000 | /// or initializer expressions, otherwise all nodes and tokens must be equivalent. 1001 | /// 1002 | public static bool AreEquivalent(SyntaxList oldList, SyntaxList newList, bool topLevel) 1003 | where TNode : CSharpSyntaxNode 1004 | { 1005 | return F.AreEquivalent(oldList, newList, topLevel); 1006 | } 1007 | 1008 | /// 1009 | /// Determines if two lists of syntax nodes are the same, disregarding trivia differences. 1010 | /// 1011 | /// The old list. 1012 | /// The new list. 1013 | /// 1014 | /// If specified called for every child syntax node (not token) that is visited during the comparison. 1015 | /// It it returns true the child is recursively visited, otherwise the child and its subtree is disregarded. 1016 | /// 1017 | public static bool AreEquivalent(SyntaxList oldList, SyntaxList newList, Func ignoreChildNode = null) 1018 | where TNode : SyntaxNode 1019 | { 1020 | return F.AreEquivalent(oldList, newList, ignoreChildNode); 1021 | } 1022 | 1023 | /// 1024 | /// Determines if two lists of syntax nodes are the same, disregarding trivia differences. 1025 | /// 1026 | /// The old list. 1027 | /// The new list. 1028 | /// 1029 | /// If true then the nodes are equivalent if the contained nodes and tokens declaring 1030 | /// metadata visible symbolic information are equivalent, ignoring any differences of nodes inside method bodies 1031 | /// or initializer expressions, otherwise all nodes and tokens must be equivalent. 1032 | /// 1033 | public static bool AreEquivalent(SeparatedSyntaxList oldList, SeparatedSyntaxList newList, bool topLevel) 1034 | where TNode : SyntaxNode 1035 | { 1036 | return F.AreEquivalent(oldList, newList, topLevel); 1037 | } 1038 | 1039 | /// 1040 | /// Determines if two lists of syntax nodes are the same, disregarding trivia differences. 1041 | /// 1042 | /// The old list. 1043 | /// The new list. 1044 | /// 1045 | /// If specified called for every child syntax node (not token) that is visited during the comparison. 1046 | /// It it returns true the child is recursively visited, otherwise the child and its subtree is disregarded. 1047 | /// 1048 | public static bool AreEquivalent(SeparatedSyntaxList oldList, SeparatedSyntaxList newList, Func ignoreChildNode = null) 1049 | where TNode : SyntaxNode 1050 | { 1051 | return F.AreEquivalent(oldList, newList, ignoreChildNode); 1052 | } 1053 | 1054 | /// 1055 | /// Gets the containing expression that is actually a language expression and not just typed 1056 | /// as an ExpressionSyntax for convenience. For example, NameSyntax nodes on the right side 1057 | /// of qualified names and member access expressions are not language expressions, yet the 1058 | /// containing qualified names or member access expressions are indeed expressions. 1059 | /// 1060 | public static ExpressionSyntax GetStandaloneExpression(ExpressionSyntax expression) 1061 | { 1062 | return F.GetStandaloneExpression(expression); 1063 | } 1064 | 1065 | /// 1066 | /// Converts a generic name expression into one without the generic arguments. 1067 | /// 1068 | /// 1069 | /// 1070 | public static ExpressionSyntax GetNonGenericExpression(ExpressionSyntax expression) 1071 | { 1072 | return F.GetNonGenericExpression(expression); 1073 | } 1074 | 1075 | /// 1076 | /// Determines whether the given text is considered a syntactically complete submission. 1077 | /// 1078 | public static bool IsCompleteSubmission(SyntaxTree tree) 1079 | { 1080 | return F.IsCompleteSubmission(tree); 1081 | } 1082 | 1083 | /// Creates a new CaseSwitchLabelSyntax instance. 1084 | public static CaseSwitchLabelSyntax CaseSwitchLabel(ExpressionSyntax value) 1085 | { 1086 | return F.CaseSwitchLabel(value); 1087 | } 1088 | 1089 | /// Creates a new DefaultSwitchLabelSyntax instance. 1090 | public static DefaultSwitchLabelSyntax DefaultSwitchLabel() 1091 | { 1092 | return F.DefaultSwitchLabel(); 1093 | } 1094 | 1095 | /// Creates a new BlockSyntax instance. 1096 | public static BlockSyntax Block(params StatementSyntax[] statements) 1097 | { 1098 | return F.Block(statements); 1099 | } 1100 | 1101 | /// Creates a new BlockSyntax instance. 1102 | public static BlockSyntax Block(IEnumerable statements) 1103 | { 1104 | return F.Block(statements); 1105 | } 1106 | 1107 | public static PropertyDeclarationSyntax PropertyDeclaration( 1108 | SyntaxList attributeLists, 1109 | SyntaxTokenList modifiers, 1110 | TypeSyntax type, 1111 | ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, 1112 | SyntaxToken identifier, 1113 | AccessorListSyntax accessorList) 1114 | { 1115 | return F.PropertyDeclaration(attributeLists, modifiers, type, explicitInterfaceSpecifier, identifier, accessorList); 1116 | } 1117 | 1118 | public static MethodDeclarationSyntax MethodDeclaration( 1119 | SyntaxList attributeLists, 1120 | SyntaxTokenList modifiers, 1121 | TypeSyntax returnType, 1122 | ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, 1123 | SyntaxToken identifier, 1124 | TypeParameterListSyntax typeParameterList, 1125 | ParameterListSyntax parameterList, 1126 | SyntaxList constraintClauses, 1127 | BlockSyntax body, 1128 | SyntaxToken semicolonToken) 1129 | { 1130 | return F.MethodDeclaration(attributeLists, modifiers, returnType, explicitInterfaceSpecifier, 1131 | identifier, typeParameterList, parameterList, constraintClauses, body, semicolonToken); 1132 | } 1133 | 1134 | public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( 1135 | SyntaxList attributeLists, 1136 | SyntaxTokenList modifiers, 1137 | SyntaxToken implicitOrExplicitKeyword, 1138 | SyntaxToken operatorKeyword, 1139 | TypeSyntax type, 1140 | ParameterListSyntax parameterList, 1141 | BlockSyntax body, 1142 | SyntaxToken semicolonToken) 1143 | { 1144 | return F.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, 1145 | operatorKeyword, type, parameterList, body, semicolonToken); 1146 | } 1147 | 1148 | public static OperatorDeclarationSyntax OperatorDeclaration( 1149 | SyntaxList attributeLists, 1150 | SyntaxTokenList modifiers, 1151 | TypeSyntax returnType, 1152 | SyntaxToken operatorKeyword, 1153 | SyntaxToken operatorToken, 1154 | ParameterListSyntax parameterList, 1155 | BlockSyntax body, 1156 | SyntaxToken semicolonToken) 1157 | { 1158 | return F.OperatorDeclaration(attributeLists, modifiers, returnType, operatorKeyword, 1159 | operatorToken, parameterList, body, semicolonToken); 1160 | } 1161 | 1162 | public static IndexerDeclarationSyntax IndexerDeclaration( 1163 | SyntaxList attributeLists, 1164 | SyntaxTokenList modifiers, 1165 | TypeSyntax type, 1166 | ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, 1167 | BracketedParameterListSyntax parameterList, 1168 | AccessorListSyntax accessorList) 1169 | { 1170 | return F.IndexerDeclaration(attributeLists, modifiers, type, explicitInterfaceSpecifier, 1171 | parameterList, accessorList); 1172 | } 1173 | 1174 | /// Creates a new UsingDirectiveSyntax instance. 1175 | public static UsingDirectiveSyntax UsingDirective(NameEqualsSyntax alias, NameSyntax name) 1176 | { 1177 | return F.UsingDirective(alias, name); 1178 | } 1179 | 1180 | /* AnonymousMethodExpressionSyntax.cs */ 1181 | 1182 | public static AnonymousMethodExpressionSyntax AnonymousMethodExpression() 1183 | { 1184 | return F.AnonymousMethodExpression(); 1185 | } 1186 | 1187 | /* TypeDeclarationSyntax.cs */ 1188 | 1189 | public static TypeDeclarationSyntax TypeDeclaration(SyntaxKind kind, SyntaxToken identifier) 1190 | { 1191 | return F.TypeDeclaration(kind, identifier); 1192 | } 1193 | 1194 | public static TypeDeclarationSyntax TypeDeclaration(SyntaxKind kind, string identifier) 1195 | { 1196 | return F.TypeDeclaration(kind, identifier); 1197 | } 1198 | 1199 | public static TypeDeclarationSyntax TypeDeclaration(SyntaxKind kind, SyntaxList attributes, SyntaxTokenList modifiers, SyntaxToken keyword, SyntaxToken identifier, TypeParameterListSyntax typeParameterList, BaseListSyntax baseList, SyntaxList constraintClauses, SyntaxToken openBraceToken, SyntaxList members, SyntaxToken closeBraceToken, SyntaxToken semicolonToken) 1200 | { 1201 | return F.TypeDeclaration(kind, attributes, modifiers, keyword, identifier, typeParameterList, baseList, constraintClauses, openBraceToken, members, closeBraceToken, semicolonToken); 1202 | } 1203 | } 1204 | } 1205 | -------------------------------------------------------------------------------- /MacroSharp/MacroTransform.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MacroSharp 9 | { 10 | public abstract class MacroTransform : IMacroTransform 11 | { 12 | public abstract SyntaxNode Transform(TransformContext context); 13 | } 14 | } -------------------------------------------------------------------------------- /MacroSharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MacroSharp")] 9 | [assembly: AssemblyDescription("Macros for C#")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Russ Powers")] 12 | [assembly: AssemblyProduct("MacroSharp")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("42778190-193e-400f-891c-aca9d975e0aa")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MacroSharp/SyntaxFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using F = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 9 | 10 | namespace MacroSharp 11 | { 12 | public static class SyntaxFactoryExtensions 13 | { 14 | public static TNode WithLineTriviaFrom(this TNode node, SyntaxNode originalNode) 15 | where TNode : SyntaxNode 16 | { 17 | var lineSpan = originalNode.GetLocation().GetLineSpan(); 18 | 19 | return node 20 | .WithLeadingTrivia(F.Trivia(F.LineDirectiveTrivia(F.Literal(lineSpan.Span.Start.Line), true)), F.CarriageReturn) 21 | .WithTrailingTrivia(F.CarriageReturn); 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MacroSharp/TransformContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MacroSharp 9 | { 10 | public class TransformContext 11 | { 12 | public TransformContext() 13 | { 14 | Diagnostics = new List(); 15 | } 16 | 17 | internal void ResetSemanticModel() 18 | { 19 | _lazySemanticModel = null; 20 | } 21 | 22 | public Compilation Compilation { get; internal set; } 23 | public AttributeData AttributeData { get; internal set; } 24 | public SyntaxNode SyntaxNode { get; internal set; } 25 | public ISymbol Symbol { get; internal set; } 26 | public IList Diagnostics { get; private set; } 27 | private SemanticModel _lazySemanticModel; 28 | public SemanticModel SemanticModel 29 | { 30 | get 31 | { 32 | if (_lazySemanticModel == null) 33 | _lazySemanticModel = Compilation.GetSemanticModel(SyntaxNode.SyntaxTree); 34 | 35 | return _lazySemanticModel; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MacroSharp/WellKnownTypes.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MacroSharp 9 | { 10 | class WellKnownTypes 11 | { 12 | private const string TypeName_MethodMacroAttribute = "MacroSharp.MethodMacroAttribute"; 13 | private const string TypeName_TypeMacroAttribute = "MacroSharp.TypeMacroAttribute"; 14 | 15 | public readonly INamedTypeSymbol MethodMacroAttribute; 16 | public readonly INamedTypeSymbol TypeMacroAttribute; 17 | 18 | public readonly bool AllTypesFound; 19 | 20 | public WellKnownTypes(Compilation compilation) 21 | { 22 | MethodMacroAttribute = compilation.GetTypeByMetadataName(TypeName_MethodMacroAttribute); 23 | TypeMacroAttribute = compilation.GetTypeByMetadataName(TypeName_TypeMacroAttribute); 24 | 25 | AllTypesFound = 26 | MethodMacroAttribute != null && 27 | TypeMacroAttribute != null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MacroSharp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MacroSharp 2 | This library provides attribute-based macros for C#. Macro attributes can be applied to methods and type declarations, allowing you to manipulate syntax trees at compile time. This project uses my fork of Roslyn to provide compiler plugin support. 3 | 4 | # Basic Example 5 | I have created `NotifyPropertyChanged`, a simple macro that automatically implements INotifyPropertyChanged for a class and emits PropertyChanged events for auto properties. It also will create a PropertyChanged handler if one does not exist. 6 | 7 | The macro source is in MacroSharp.ExampleMacros [here](https://github.com/russpowers/MacroSharp/blob/master/MacroSharp.ExampleMacros/NotifyPropertyChanged.cs). See the Getting Started section below to download the full working example. 8 | 9 | The usage of `NotifyPropertyChanged` looks like this: 10 | 11 | // Invoke the macro using its attribute 12 | [NotifyPropertyChanged] 13 | public class MyMacroTest 14 | { 15 | public int X { get; set; } 16 | } 17 | 18 | And the generated code looks like this: 19 | 20 | [NotifyPropertyChanged] 21 | public class MyMacroTest 22 | { 23 | private int _x; 24 | public int X 25 | { 26 | get { return _x; } 27 | set 28 | { 29 | _x = value; 30 | if (PropertyChanged != null) 31 | PropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("X")); 32 | } 33 | } 34 | 35 | public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 36 | } 37 | 38 | # Getting started 39 | 1. Be sure you have VS2015 (any version) installed 40 | 2. Clone my fork of Roslyn: https://github.com/russpowers/roslyn 41 | 3. Install the nuget dependencies by opening a command prompt in the cloned Roslyn folder and typing: `nuget.exe restore Roslyn.sln` 42 | 4. Open src/Roslyn.sln in VS2015 and build 43 | 5. Clone this into a separate folder 44 | 6. Edit RoslynCommon.targets and change the `RoslynBinaryPath` to point to the binary folder of the Roslyn you just built 45 | 7. Open MacroSharp.sln in VS 2015 46 | 8. Build all 47 | 9. Switch the startup project to MacroSharp.Examples and Run. 48 | 49 | # To use macros in a new project 50 | - Follow the above steps 1-8 51 | - Create a new macro project `A` (where the macros will be stored) 52 | - Create a new project that will use the macro project `B` (console, class library, whatever) 53 | - Add a reference to MacroSharp.dll in both `A` and `B` projects 54 | - Add a reference to `A` in `B` 55 | - Edit `B`'s .csproj file by adding `*ROSLYN PATH*\Binaries\Debug or Release` and `csc2.exe` to the first ``. Example: `D:\roslyn\Binaries\Debug` `csc2.exe` 56 | - In one of `B`'s .cs files, add an assembly attribute `[assembly:MacroSharp.MacroPlugin]` 57 | 58 | # Notes 59 | 60 | The first build with macros will take a couple of seconds because it has to startup an external version of Roslyn to compile. Subsequent compiles are normal speed. 61 | 62 | There is only one example right now, NotifyPropertyChanged. It automatically implements INotifyPropertyChanged for a class and emits PropertyChanged events for auto properties. 63 | 64 | This does not integrate with Intellisense (yet), so you may see warnings/errors in Visual Studio that do not reflect your macros. Also, debugging a file with macros in it can be a bit wonky, especially if you heavily modify the syntax, so expect some issues there. -------------------------------------------------------------------------------- /RoslynCommon.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | d:\roslyn\Binaries\Debug 5 | 6 | --------------------------------------------------------------------------------