├── .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