| @@ -1,6 +1,6 @@ | |||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio 15 | # Visual Studio 15 | ||||
| VisualStudioVersion = 15.0.26730.12 | |||||
| VisualStudioVersion = 15.0.27130.0 | |||||
| MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | ||||
| EndProject | EndProject | ||||
| @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\D | |||||
| EndProject | EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" | ||||
| EndProject | EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Analyzers", "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj", "{BBA8E7FB-C834-40DC-822F-B112CB7F0140}" | |||||
| EndProject | |||||
| Global | Global | ||||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
| @@ -130,6 +132,18 @@ Global | |||||
| {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU | ||||
| {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU | ||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x64.Build.0 = Debug|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x86.Build.0 = Debug|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x64.ActiveCfg = Release|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x64.Build.0 = Release|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x86.ActiveCfg = Release|Any CPU | |||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x86.Build.0 = Release|Any CPU | |||||
| EndGlobalSection | EndGlobalSection | ||||
| GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
| HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
| @@ -141,6 +155,7 @@ Global | |||||
| {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | ||||
| {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} | {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} | ||||
| {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | ||||
| {BBA8E7FB-C834-40DC-822F-B112CB7F0140} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | |||||
| EndGlobalSection | EndGlobalSection | ||||
| GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} | SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} | ||||
| @@ -0,0 +1,30 @@ | |||||
| <Project Sdk="Microsoft.NET.Sdk"> | |||||
| <PropertyGroup> | |||||
| <VersionPrefix>1.0.2</VersionPrefix> | |||||
| <Authors>RogueException</Authors> | |||||
| <PackageTags>discord;discordapp</PackageTags> | |||||
| <PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl> | |||||
| <PackageLicenseUrl>http://opensource.org/licenses/MIT</PackageLicenseUrl> | |||||
| <RepositoryType>git</RepositoryType> | |||||
| <RepositoryUrl>git://github.com/RogueException/Discord.Net</RepositoryUrl> | |||||
| <AssemblyName>Discord.Net.Analyzers</AssemblyName> | |||||
| <RootNamespace>Discord.Analyzers</RootNamespace> | |||||
| <Description>A Discord.Net extension adding support for design-time analysis of the API usage.</Description> | |||||
| <TargetFramework>netstandard1.3</TargetFramework> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' Or '$(TargetFramework)' == 'net45' "> | |||||
| <DefineConstants>$(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET</DefineConstants> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' "> | |||||
| <DefineConstants>$(DefineConstants);FORMATSTR;UNIXTIME;MSTRYBUFFER;UDPDISPOSE</DefineConstants> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | |||||
| <NoWarn>$(NoWarn);CS1573;CS1591</NoWarn> | |||||
| <WarningsAsErrors>true</WarningsAsErrors> | |||||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <PackageReference Include="Microsoft.CodeAnalysis" Version="2.6.0" /> | |||||
| <PackageReference Include="Discord.Net.Commands" Version="1.0.2" /> | |||||
| </ItemGroup> | |||||
| </Project> | |||||
| @@ -0,0 +1,78 @@ | |||||
| using System; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| using Microsoft.CodeAnalysis; | |||||
| using Microsoft.CodeAnalysis.CSharp; | |||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | |||||
| using Microsoft.CodeAnalysis.Diagnostics; | |||||
| using Discord.Commands; | |||||
| namespace Discord.Analyzers | |||||
| { | |||||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | |||||
| public sealed class GuildAccessAnalyzer : DiagnosticAnalyzer | |||||
| { | |||||
| private const string DiagnosticId = "DNET0001"; | |||||
| private const string Title = "Limit command to Guild contexts."; | |||||
| private const string MessageFormat = "Command method '{0}' is accessing 'Context.Guild' but is not restricted to Guild contexts."; | |||||
| private const string Description = "Accessing 'Context.Guild' in a command without limiting the command to run only in guilds."; | |||||
| private const string Category = "Design"; | |||||
| private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); | |||||
| public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); | |||||
| public override void Initialize(AnalysisContext context) | |||||
| { | |||||
| context.RegisterSyntaxNodeAction(AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression); | |||||
| } | |||||
| private static void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context) | |||||
| { | |||||
| // Bail out if the containing class doesn't derive from 'ModuleBase<T>' | |||||
| var classNode = context.Node.FirstAncestorOrSelf<TypeDeclarationSyntax>(); | |||||
| var classSymbol = context.SemanticModel.GetSymbolInfo(classNode).Symbol as INamedTypeSymbol; | |||||
| if (!DerivesFromModuleBase(classSymbol)) | |||||
| return; | |||||
| // Bail out if the containing method isn't marked with '[Command]' | |||||
| var methodNode = context.Node.FirstAncestorOrSelf<MethodDeclarationSyntax>(); | |||||
| var methodSymbol = context.SemanticModel.GetSymbolInfo(methodNode).Symbol; | |||||
| var methodAttributes = methodSymbol.GetAttributes(); | |||||
| if (!methodAttributes.Any(a => a.AttributeClass.Name == nameof(CommandAttribute))) | |||||
| return; | |||||
| // Are you geting a property named 'Guild'? | |||||
| var memberAccessSymbol = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol; | |||||
| if (memberAccessSymbol.AssociatedSymbol.Name == "Guild") //I guess? | |||||
| { | |||||
| // Is the '[RequireContext]' attribute not applied to either the method or the class, or doesn't contain 'ContextType.Guild'? | |||||
| var ctxAttribute = methodAttributes.SingleOrDefault(_attributeDataPredicate) | |||||
| ?? classSymbol.GetAttributes().SingleOrDefault(_attributeDataPredicate); | |||||
| if (ctxAttribute == null || ctxAttribute.ConstructorArguments.Any(arg => !arg.Value.Equals(ContextType.Guild))) | |||||
| { | |||||
| // Report the diagnostic | |||||
| var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation(), methodSymbol.Name); | |||||
| context.ReportDiagnostic(diagnostic); | |||||
| } | |||||
| } | |||||
| } | |||||
| private static readonly Func<AttributeData, bool> _attributeDataPredicate = a => a.AttributeClass.Name == nameof(RequireContextAttribute); | |||||
| private static readonly string _moduleBaseName = typeof(ModuleBase<>).Name; | |||||
| private static bool DerivesFromModuleBase(INamedTypeSymbol symbol) | |||||
| { | |||||
| var bType = symbol.BaseType; | |||||
| while (bType != null) | |||||
| { | |||||
| if (bType.MetadataName == _moduleBaseName) | |||||
| return true; | |||||
| bType = bType.BaseType; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||