Skip to content

Commit

Permalink
Merge pull request #956 from AArnott/fix833
Browse files Browse the repository at this point in the history
Fix msgpack003-005 analyzer and add tests
  • Loading branch information
AArnott committed Jul 1, 2020
2 parents b953419 + a4d04c9 commit 8073e88
Show file tree
Hide file tree
Showing 16 changed files with 299 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<RootNamespace>MessagePackAnalyzer.Vsix</RootNamespace>
<AssemblyName>MessagePackAnalyzer.Vsix</AssemblyName>
<TargetVsixContainerName>MessagePackAnalyzer.vsix</TargetVsixContainerName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<GeneratePkgDefFile>false</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
Expand Down
27 changes: 12 additions & 15 deletions src/MessagePackAnalyzer/MessagePackAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,31 @@ public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.InterfaceDeclaration);
context.RegisterCompilationStartAction(ctxt =>
{
if (ReferenceSymbols.TryCreate(ctxt.Compilation, out ReferenceSymbols? typeReferences))
{
ctxt.RegisterSyntaxNodeAction(c => Analyze(c, typeReferences), SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.InterfaceDeclaration);
}
});
}

private static void Analyze(SyntaxNodeAnalysisContext context)
private static void Analyze(SyntaxNodeAnalysisContext context, ReferenceSymbols typeReferences)
{
SemanticModel model = context.SemanticModel;

var typeDeclaration = context.Node as TypeDeclarationSyntax;
if (typeDeclaration == null)
TypeDeclarationSyntax typeDeclaration = (TypeDeclarationSyntax)context.Node;
INamedTypeSymbol? declaredSymbol = context.SemanticModel.GetDeclaredSymbol(typeDeclaration);
if (declaredSymbol is null)
{
return;
}

INamedTypeSymbol declaredSymbol = model.GetDeclaredSymbol(typeDeclaration);
if (declaredSymbol == null)
{
return;
}

var typeReferences = new ReferenceSymbols(model.Compilation);

if (
((declaredSymbol.TypeKind == TypeKind.Interface) && declaredSymbol.GetAttributes().Any(x2 => Equals(x2.AttributeClass, typeReferences.UnionAttribute)))
|| ((declaredSymbol.TypeKind == TypeKind.Class) && declaredSymbol.GetAttributes().Any(x2 => Equals(x2.AttributeClass, typeReferences.MessagePackObjectAttribute)))
|| ((declaredSymbol.TypeKind == TypeKind.Struct) && declaredSymbol.GetAttributes().Any(x2 => Equals(x2.AttributeClass, typeReferences.MessagePackObjectAttribute))))
{
var reportContext = new DiagnosticsReportContext(context);
var collector = new TypeCollector(reportContext, model.Compilation);
var collector = new TypeCollector(reportContext, typeReferences);
collector.CollectCore(declaredSymbol);
reportContext.ReportAll();
}
Expand Down
1 change: 1 addition & 0 deletions src/MessagePackAnalyzer/MessagePackAnalyzer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" version="2.9.8" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.10.0" />
<PackageReference Include="Nullable" Version="1.2.1" />
</ItemGroup>
<ItemGroup>
<Compile Update="Strings.Designer.cs">
Expand Down
75 changes: 28 additions & 47 deletions src/MessagePackAnalyzer/MessagePackCodeFixProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
Expand All @@ -14,10 +13,6 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Text;

#nullable disable

namespace MessagePackAnalyzer
{
Expand All @@ -42,21 +37,26 @@ public sealed override FixAllProvider GetFixAllProvider()
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false) as CompilationUnitSyntax;
if (root is null)
{
return;
}

SemanticModel model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

var typeInfo = context.Diagnostics[0]?.Properties.GetValueOrDefault("type", null);
INamedTypeSymbol namedSymbol = (typeInfo != null)
INamedTypeSymbol? namedSymbol = (typeInfo != null)
? model.Compilation.GetTypeByMetadataName(typeInfo.Replace("global::", string.Empty))
: null;

if (namedSymbol == null)
if (namedSymbol is null)
{
SyntaxNode targetNode = root.FindNode(context.Span);
var property = targetNode as PropertyDeclarationSyntax;
var field = targetNode as FieldDeclarationSyntax;
var dec = targetNode as VariableDeclaratorSyntax;

ITypeSymbol targetType = null;
ITypeSymbol? targetType = null;
if (property == null && field == null)
{
var typeDeclare = targetNode as TypeDeclarationSyntax;
Expand Down Expand Up @@ -108,7 +108,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)

if (targetType.TypeKind == TypeKind.Array)
{
targetType = (targetType as IArrayTypeSymbol).ElementType;
targetType = ((IArrayTypeSymbol)targetType).ElementType;
}

namedSymbol = targetType as INamedTypeSymbol;
Expand All @@ -123,34 +123,22 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(action, context.Diagnostics.First()); // use single.
}

private static async Task<Document> AddKeyAttributeAsync(Document document, INamedTypeSymbol type, CancellationToken cancellationToken)
private static async Task<Solution> AddKeyAttributeAsync(Document document, INamedTypeSymbol type, CancellationToken cancellationToken)
{
if (type.DeclaringSyntaxReferences.Length != 0)
{
document = document.Project.GetDocument(type.DeclaringSyntaxReferences[0].SyntaxTree);
}

DocumentEditor editor = await DocumentEditor.CreateAsync(document).ConfigureAwait(false);
var solutionEditor = new SolutionEditor(document.Project.Solution);

ISymbol[] targets = type.GetAllMembers()
.Where(x => x.Kind == SymbolKind.Property || x.Kind == SymbolKind.Field)
.Where(x => x.GetAttributes().FindAttributeShortName(MessagePackAnalyzer.IgnoreShortName) == null && x.GetAttributes().FindAttributeShortName(MessagePackAnalyzer.IgnoreDataMemberShortName) == null)
.Where(x => !x.IsStatic)
.Where(x =>
{
var p = x as IPropertySymbol;
if (p == null)
return x switch
{
var f = x as IFieldSymbol;
if (f.IsImplicitlyDeclared)
{
return false;
}
return true;
}
return p.ExplicitInterfaceImplementations.Length == 0;
IPropertySymbol p => p.ExplicitInterfaceImplementations.Length == 0,
IFieldSymbol f => !f.IsImplicitlyDeclared,
_ => throw new NotSupportedException("Unsupported member type."),
};
})
.ToArray();

Expand All @@ -159,38 +147,31 @@ private static async Task<Document> AddKeyAttributeAsync(Document document, INam
.Where(x => x != null)
.Select(x => x.ConstructorArguments[0])
.Where(x => !x.IsNull)
.Where(x => x.Value.GetType() == typeof(int))
.Where(x => x.Value is int)
.Select(x => (int)x.Value)
.DefaultIfEmpty(-1) // if empty, start from zero.
.Max() + 1;

foreach (ISymbol item in targets)
foreach (ISymbol member in targets)
{
SyntaxNode node = await item.DeclaringSyntaxReferences[0].GetSyntaxAsync().ConfigureAwait(false);

AttributeData attr = item.GetAttributes().FindAttributeShortName(MessagePackAnalyzer.KeyAttributeShortName);
if (attr != null)
if (member.GetAttributes().FindAttributeShortName(MessagePackAnalyzer.KeyAttributeShortName) is null)
{
continue; // already tagged Index.
SyntaxNode node = await member.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var documentEditor = await solutionEditor.GetDocumentEditorAsync(document.Project.Solution.GetDocumentId(node.SyntaxTree), cancellationToken).ConfigureAwait(false);
var syntaxGenerator = SyntaxGenerator.GetGenerator(documentEditor.OriginalDocument);
documentEditor.AddAttribute(node, syntaxGenerator.Attribute("MessagePack.KeyAttribute", syntaxGenerator.LiteralExpression(startOrder++)));
}

AttributeListSyntax attribute = RoslynCodeFixExtensions.ParseAttributeList($"[Key({startOrder++})]")
.WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed);

editor.AddAttribute(node, attribute);
}

if (type.GetAttributes().FindAttributeShortName(MessagePackAnalyzer.MessagePackObjectAttributeShortName) == null)
{
SyntaxNode rootNode = await type.DeclaringSyntaxReferences[0].GetSyntaxAsync().ConfigureAwait(false);
editor.AddAttribute(rootNode, RoslynCodeFixExtensions.ParseAttributeList("[MessagePackObject]"));
SyntaxNode node = await type.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var documentEditor = await solutionEditor.GetDocumentEditorAsync(document.Project.Solution.GetDocumentId(node.SyntaxTree), cancellationToken).ConfigureAwait(false);
var syntaxGenerator = SyntaxGenerator.GetGenerator(documentEditor.OriginalDocument);
documentEditor.AddAttribute(node, syntaxGenerator.Attribute("MessagePack.MessagePackObject"));
}

Document newDocument = editor.GetChangedDocument();
var newRoot = editor.GetChangedRoot() as CompilationUnitSyntax;
newDocument = newDocument.WithSyntaxRoot(newRoot.WithUsing("MessagePack"));

return newDocument;
return solutionEditor.GetChangedSolution();
}
}
}
4 changes: 3 additions & 1 deletion src/MessagePackAnalyzer/MsgPack001SpecifyOptionsAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
Expand All @@ -10,6 +9,9 @@

namespace MessagePackAnalyzer
{
/// <summary>
/// An analyzer that guards against calling APIs that rely on static, mutable fields defining "default" options.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class MsgPack001SpecifyOptionsAnalyzer : DiagnosticAnalyzer
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace MessagePackAnalyzer
{
/// <summary>
/// An analyzer to guide callers to avoid use of mutable static fields for MessagePackSerializerOptions.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class MsgPack002UseConstantOptionsAnalyzer : DiagnosticAnalyzer
{
Expand Down
91 changes: 0 additions & 91 deletions src/MessagePackAnalyzer/NullabilityAttributes.cs

This file was deleted.

0 comments on commit 8073e88

Please sign in to comment.