From b3976eb8d72d43a5d3ad857085bb1b4e9afe7d1a Mon Sep 17 00:00:00 2001 From: Paul Ritter Date: Mon, 8 Feb 2021 18:05:27 +0100 Subject: [PATCH] Analyzer Bongaloo 2: The Return (#1512) Co-authored-by: Paul --- MSBuild/Robust.Analyzers.targets | 5 + MSBuild/Robust.Engine.targets | 1 + Robust.Analyzers/ExplicitInterfaceAnalyzer.cs | 92 +++++++++++ Robust.Analyzers/Robust.Analyzers.csproj | 13 ++ Robust.Analyzers/SerializableAnalyzer.cs | 150 ++++++++++++++++++ .../Input/KeyBindingRegistration.cs | 2 +- .../Container/ContainerManagerComponent.cs | 2 +- ...RequiresExplicitImplementationAttribute.cs | 7 + .../RequiresSerializableAttribute.cs | 7 + Robust.Shared/Audio/AudioParams.cs | 2 +- Robust.Shared/GameObjects/ComponentState.cs | 2 + .../Renderable/SharedSpriteComponent.cs | 2 +- .../SharedUserInterfaceComponent.cs | 2 +- .../Interfaces/Serialization/IExposeData.cs | 2 + Robust.Shared/Map/PhysShapeGrid.cs | 3 +- Robust.Shared/Physics/PhysShapeAabb.cs | 3 +- Robust.Shared/Physics/PhysShapeCircle.cs | 3 +- Robust.Shared/Physics/PhysShapeRect.cs | 3 +- RobustToolbox.sln | 11 ++ 19 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 MSBuild/Robust.Analyzers.targets create mode 100644 Robust.Analyzers/ExplicitInterfaceAnalyzer.cs create mode 100644 Robust.Analyzers/Robust.Analyzers.csproj create mode 100644 Robust.Analyzers/SerializableAnalyzer.cs create mode 100644 Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs create mode 100644 Robust.Shared/Analyzers/RequiresSerializableAttribute.cs diff --git a/MSBuild/Robust.Analyzers.targets b/MSBuild/Robust.Analyzers.targets new file mode 100644 index 00000000000..3304dc624c7 --- /dev/null +++ b/MSBuild/Robust.Analyzers.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/MSBuild/Robust.Engine.targets b/MSBuild/Robust.Engine.targets index a64fc74c71d..0c1f7a6e039 100644 --- a/MSBuild/Robust.Engine.targets +++ b/MSBuild/Robust.Engine.targets @@ -1,2 +1,3 @@ + diff --git a/Robust.Analyzers/ExplicitInterfaceAnalyzer.cs b/Robust.Analyzers/ExplicitInterfaceAnalyzer.cs new file mode 100644 index 00000000000..d1f5923c958 --- /dev/null +++ b/Robust.Analyzers/ExplicitInterfaceAnalyzer.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Document = Microsoft.CodeAnalysis.Document; + +namespace Robust.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ExplicitInterfaceAnalyzer : DiagnosticAnalyzer + { + public readonly SyntaxKind[] ExcludedModifiers = + { + SyntaxKind.VirtualKeyword, + SyntaxKind.AbstractKeyword, + SyntaxKind.OverrideKeyword + }; + + public const string DiagnosticId = "RA0000"; + + private const string Title = "No explicit interface specified"; + private const string MessageFormat = "No explicit interface specified"; + private const string Description = "Make sure to specify the interface in your method-declaration."; + private const string Category = "Usage"; + + [SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); + + private const string RequiresExplicitImplementationAttributeMetadataName = + "Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute"; + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration); + } + + private void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + ISymbol symbol; + Location location; + switch (context.Node) + { + //we already have a explicit interface specified, no need to check further + case MethodDeclarationSyntax methodDecl when methodDecl.ExplicitInterfaceSpecifier != null || methodDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())): + return; + case PropertyDeclarationSyntax propertyDecl when propertyDecl.ExplicitInterfaceSpecifier != null || propertyDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())): + return; + + case MethodDeclarationSyntax methodDecl: + symbol = context.SemanticModel.GetDeclaredSymbol(methodDecl); + location = methodDecl.Identifier.GetLocation(); + break; + case PropertyDeclarationSyntax propertyDecl: + symbol = context.SemanticModel.GetDeclaredSymbol(propertyDecl); + location = propertyDecl.Identifier.GetLocation(); + break; + + default: + return; + } + + var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName); + + var isInterfaceMember = symbol?.ContainingType.AllInterfaces.Any( + i => + i.GetMembers().Any(m => SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(m))) + && i.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol)) + ) ?? false; + + if (isInterfaceMember) + { + //we do not have an explicit interface specified. bad! + var diagnostic = Diagnostic.Create( + Rule, + location); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/Robust.Analyzers/Robust.Analyzers.csproj b/Robust.Analyzers/Robust.Analyzers.csproj new file mode 100644 index 00000000000..f4946ce38a9 --- /dev/null +++ b/Robust.Analyzers/Robust.Analyzers.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + + + + + + + + + diff --git a/Robust.Analyzers/SerializableAnalyzer.cs b/Robust.Analyzers/SerializableAnalyzer.cs new file mode 100644 index 00000000000..1114e1990ee --- /dev/null +++ b/Robust.Analyzers/SerializableAnalyzer.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Robust.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class SerializableAnalyzer : DiagnosticAnalyzer + { + // Metadata of the analyzer + public const string DiagnosticId = "RA0001"; + + // You could use LocalizedString but it's a little more complicated for this sample + private const string Title = "Class not marked as (Net)Serializable"; + private const string MessageFormat = "Class not marked as (Net)Serializable"; + private const string Description = "The class should be marked as (Net)Serializable."; + private const string Category = "Usage"; + + private const string RequiresSerializableAttributeMetadataName = "Robust.Shared.Analyzers.RequiresSerializableAttribute"; + private const string SerializableAttributeMetadataName = "System.SerializableAttribute"; + private const string NetSerializableAttributeMetadataName = "Robust.Shared.Serialization.NetSerializableAttribute"; + + [SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); + } + + private bool Marked(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol) + { + if (namedTypeSymbol == null) return false; + if (namedTypeSymbol.GetAttributes() + .Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))) return true; + return Marked(namedTypeSymbol.BaseType, attrSymbol); + } + + private void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresSerializableAttributeMetadataName); + var classDecl = (ClassDeclarationSyntax) context.Node; + var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl); + if (classSymbol == null) return; + + if (Marked(classSymbol, attrSymbol)) + { + var attributes = classSymbol.GetAttributes(); + var serAttr = context.Compilation.GetTypeByMetadataName(SerializableAttributeMetadataName); + var netSerAttr = context.Compilation.GetTypeByMetadataName(NetSerializableAttributeMetadataName); + + var hasSerAttr = attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, serAttr)); + var hasNetSerAttr = + attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, netSerAttr)); + + if (!hasSerAttr || !hasNetSerAttr) + { + var requiredAttributes = new List(); + if(!hasSerAttr) requiredAttributes.Add(SerializableAttributeMetadataName); + if(!hasNetSerAttr) requiredAttributes.Add(NetSerializableAttributeMetadataName); + + context.ReportDiagnostic( + Diagnostic.Create( + Rule, + classDecl.Identifier.GetLocation(), + ImmutableDictionary.CreateRange(new Dictionary() + { + { + "requiredAttributes", string.Join(",", requiredAttributes) + } + }))); + } + } + } + } + + [ExportCodeFixProvider(LanguageNames.CSharp)] + public class SerializableCodeFixProvider : CodeFixProvider + { + private const string Title = "Annotate class as (Net)Serializable."; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken); + + foreach (var diagnostic in context.Diagnostics) + { + var span = diagnostic.Location.SourceSpan; + var classDecl = root.FindToken(span.Start).Parent.AncestorsAndSelf().OfType().First(); + + if(!diagnostic.Properties.TryGetValue("requiredAttributes", out var requiredAttributes)) return; + + context.RegisterCodeFix( + CodeAction.Create( + Title, + c => FixAsync(context.Document, classDecl, requiredAttributes, c), + Title), + diagnostic); + } + } + + private async Task FixAsync(Document document, ClassDeclarationSyntax classDecl, + string requiredAttributes, CancellationToken cancellationToken) + { + var attributes = new List(); + var namespaces = new List(); + foreach (var attribute in requiredAttributes.Split(',')) + { + var tempSplit = attribute.Split('.'); + namespaces.Add(string.Join(".",tempSplit.Take(tempSplit.Length-1))); + var @class = tempSplit.Last(); + @class = @class.Substring(0, @class.Length - 9); //cut out "Attribute" at the end + attributes.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName(@class))); + } + + var newClassDecl = + classDecl.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributes))); + + var root = (CompilationUnitSyntax) await document.GetSyntaxRootAsync(cancellationToken); + root = root.ReplaceNode(classDecl, newClassDecl); + + foreach (var ns in namespaces) + { + if(root.Usings.Any(u => u.Name.ToString() == ns)) continue; + root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns))); + } + return document.WithSyntaxRoot(root); + } + + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId); + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + } +} diff --git a/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs b/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs index 700f3c093d3..8a6580485ce 100644 --- a/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs +++ b/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs @@ -18,7 +18,7 @@ public struct KeyBindingRegistration : IExposeData public bool CanRepeat; public bool AllowSubCombs; - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref Function, "function", default); serializer.DataField(ref Type, "type", KeyBindingType.State); diff --git a/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs b/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs index 0cc5bf4b7bc..4cde65bade5 100644 --- a/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs +++ b/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs @@ -289,7 +289,7 @@ public ContainerPrototypeData(List entities, string type) Type = type; } - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref Entities, "entities", new List()); serializer.DataField(ref Type, "type", null); diff --git a/Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs b/Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs new file mode 100644 index 00000000000..4db6c504590 --- /dev/null +++ b/Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Robust.Shared.Analyzers +{ + [AttributeUsage(AttributeTargets.Interface)] + public class RequiresExplicitImplementationAttribute : Attribute { } +} diff --git a/Robust.Shared/Analyzers/RequiresSerializableAttribute.cs b/Robust.Shared/Analyzers/RequiresSerializableAttribute.cs new file mode 100644 index 00000000000..b705dcb3677 --- /dev/null +++ b/Robust.Shared/Analyzers/RequiresSerializableAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Robust.Shared.Analyzers +{ + [AttributeUsage(AttributeTargets.Class)] + public class RequiresSerializableAttribute : Attribute { } +} diff --git a/Robust.Shared/Audio/AudioParams.cs b/Robust.Shared/Audio/AudioParams.cs index e126f34110d..1ebcc8bcf2a 100644 --- a/Robust.Shared/Audio/AudioParams.cs +++ b/Robust.Shared/Audio/AudioParams.cs @@ -55,7 +55,7 @@ public struct AudioParams : IExposeData /// public static readonly AudioParams Default = new(0, 1, "Master", 62.5f, 1, AudioMixTarget.Stereo, false, 0f); - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { Volume = serializer.ReadDataField("volume", 0f); PitchScale = serializer.ReadDataField("pitchscale", 1f); diff --git a/Robust.Shared/GameObjects/ComponentState.cs b/Robust.Shared/GameObjects/ComponentState.cs index 0c285f5e9ca..6dacd8870ab 100644 --- a/Robust.Shared/GameObjects/ComponentState.cs +++ b/Robust.Shared/GameObjects/ComponentState.cs @@ -1,8 +1,10 @@ using Robust.Shared.Serialization; using System; +using Robust.Shared.Analyzers; namespace Robust.Shared.GameObjects { + [RequiresSerializable] [Serializable, NetSerializable] public class ComponentState { diff --git a/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs b/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs index 3bd9072c76d..a06b45d43fa 100644 --- a/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs +++ b/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs @@ -80,7 +80,7 @@ public static PrototypeLayerData New() }; } - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref Shader, "shader", null); serializer.DataField(ref TexturePath, "texture", null); diff --git a/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs b/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs index 4ccd8d3c028..ba79de7ef88 100644 --- a/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs +++ b/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs @@ -14,7 +14,7 @@ protected sealed class PrototypeData : IExposeData public object UiKey { get; private set; } = default!; public string ClientType { get; private set; } = default!; - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { UiKey = serializer.ReadStringEnumKey("key"); ClientType = serializer.ReadDataField("type"); diff --git a/Robust.Shared/Interfaces/Serialization/IExposeData.cs b/Robust.Shared/Interfaces/Serialization/IExposeData.cs index 516bd9fd929..8d14b0d36d3 100644 --- a/Robust.Shared/Interfaces/Serialization/IExposeData.cs +++ b/Robust.Shared/Interfaces/Serialization/IExposeData.cs @@ -1,3 +1,4 @@ +using Robust.Shared.Analyzers; using Robust.Shared.Serialization; namespace Robust.Shared.Interfaces.Serialization @@ -5,6 +6,7 @@ namespace Robust.Shared.Interfaces.Serialization /// /// Interface for the "expose data" system, which is basically our method of handling data serialization. /// + [RequiresExplicitImplementation] public interface IExposeData { /// diff --git a/Robust.Shared/Map/PhysShapeGrid.cs b/Robust.Shared/Map/PhysShapeGrid.cs index 8e5e49168fc..f7033cd0957 100644 --- a/Robust.Shared/Map/PhysShapeGrid.cs +++ b/Robust.Shared/Map/PhysShapeGrid.cs @@ -1,5 +1,6 @@ using System; using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Serialization; using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Physics; @@ -83,7 +84,7 @@ public PhysShapeGrid(IMapGrid mapGrid) } /// - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref _gridId, "grid", GridId.Invalid); diff --git a/Robust.Shared/Physics/PhysShapeAabb.cs b/Robust.Shared/Physics/PhysShapeAabb.cs index b5e7db959b3..80cb1a76ece 100644 --- a/Robust.Shared/Physics/PhysShapeAabb.cs +++ b/Robust.Shared/Physics/PhysShapeAabb.cs @@ -1,4 +1,5 @@ using System; +using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -80,7 +81,7 @@ public Box2 CalculateLocalBounds(Angle rotation) } /// - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags()); serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags()); diff --git a/Robust.Shared/Physics/PhysShapeCircle.cs b/Robust.Shared/Physics/PhysShapeCircle.cs index 014b7ffc211..0af57d3ac95 100644 --- a/Robust.Shared/Physics/PhysShapeCircle.cs +++ b/Robust.Shared/Physics/PhysShapeCircle.cs @@ -1,4 +1,5 @@ using System; +using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -61,7 +62,7 @@ public float Radius } /// - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags()); serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags()); diff --git a/Robust.Shared/Physics/PhysShapeRect.cs b/Robust.Shared/Physics/PhysShapeRect.cs index f7bdaea06f8..a90ec3ec2ff 100644 --- a/Robust.Shared/Physics/PhysShapeRect.cs +++ b/Robust.Shared/Physics/PhysShapeRect.cs @@ -1,4 +1,5 @@ using System; +using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -64,7 +65,7 @@ public void DebugDraw(DebugDrawingHandle handle, in Matrix3 modelMatrix, in Box2 handle.SetTransform(Matrix3.Identity); } - public void ExposeData(ObjectSerializer serializer) + void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags()); serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags()); diff --git a/RobustToolbox.sln b/RobustToolbox.sln index 701cbe2b7c2..3c91a0a421e 100644 --- a/RobustToolbox.sln +++ b/RobustToolbox.sln @@ -32,6 +32,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ManagedHttpListener", "ManagedHttpListener", "{15D28C35-25F6-4EA8-8D53-29DA7C8A24A7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.HttpListener", "ManagedHttpListener\src\System.Net.HttpListener.csproj", "{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.Injectors", "Robust.Client.Injectors\Robust.Client.Injectors.csproj", "{EEF2C805-5E03-41EA-A916-49C1DD15EF41}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.NameGenerator", "Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj", "{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}" @@ -44,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.IL.Cecil", "XamlX\src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Analyzers", "Robust.Analyzers\Robust.Analyzers.csproj", "{3173712A-9E75-4685-B657-9AF9B7D54EFB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -188,6 +191,14 @@ Global {B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.Build.0 = Release|Any CPU {B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.ActiveCfg = Release|Any CPU {B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.Build.0 = Release|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|x64.Build.0 = Debug|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|Any CPU.Build.0 = Release|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|x64.ActiveCfg = Release|Any CPU + {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE