Skip to content

Commit

Permalink
Merge pull request #25875 from Neme12/allowUnsafe
Browse files Browse the repository at this point in the history
Code fix to add unsafe option to C# project
  • Loading branch information
jasonmalinowski authored Jan 16, 2019
2 parents 68ab9f3 + 0edab52 commit b8b7d7e
Show file tree
Hide file tree
Showing 59 changed files with 854 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.UpdateProjectToAllowUnsafe;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UpdateProjectToAllowUnsafe
{
[Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)]
public class UpdateProjectToAllowUnsafeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new CSharpUpdateProjectToAllowUnsafeCodeFixProvider());

private async Task TestAllowUnsafeEnabledIfDisabledAsync(string initialMarkup)
{
var parameters = new TestParameters();
using (var workspace = CreateWorkspaceFromOptions(initialMarkup, parameters))
{
var (_, action) = await GetCodeActionsAsync(workspace, parameters);
var operations = await VerifyActionAndGetOperationsAsync(action, default);

var (oldSolution, newSolution) = ApplyOperationsAndGetSolution(workspace, operations);
Assert.True(((CSharpCompilationOptions)newSolution.Projects.Single().CompilationOptions).AllowUnsafe);
}

// no action offered if unsafe was already enabled
await TestMissingAsync(initialMarkup, new TestParameters(compilationOptions:
new CSharpCompilationOptions(outputKind: default, allowUnsafe: true)));
}

[Fact]
public async Task OnUnsafeClass()
{
await TestAllowUnsafeEnabledIfDisabledAsync(
@"
unsafe class [|C|] // The compiler reports this on the name, not the 'unsafe' keyword.
{
}");
}

[Fact]
public async Task OnUnsafeMethod()
{
await TestAllowUnsafeEnabledIfDisabledAsync(
@"
class C
{
unsafe void [|M|]()
{
}
}");
}

[Fact]
public async Task OnUnsafeLocalFunction()
{
await TestAllowUnsafeEnabledIfDisabledAsync(
@"
class C
{
void M()
{
unsafe void [|F|]()
{
}
}
}");
}

[Fact]
public async Task OnUnsafeBlock()
{
await TestAllowUnsafeEnabledIfDisabledAsync(
@"
class C
{
void M()
{
[|unsafe|]
{
}
}
}");
}

[Fact]
public async Task NotInsideUnsafeBlock()
{
await TestMissingAsync(
@"
class C
{
void M()
{
unsafe
{
[|int * p;|]
}
}
}");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
Expand All @@ -11,7 +12,7 @@
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.Async
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UpgradeProject
{
[Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public partial class UpgradeProjectTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
Expand All @@ -31,9 +32,7 @@ private async Task TestLanguageVersionUpgradedAsync(
var (_, action) = await GetCodeActionsAsync(workspace, parameters);
var operations = await VerifyActionAndGetOperationsAsync(action, default);

var appliedChanges = ApplyOperationsAndGetSolution(workspace, operations);
var oldSolution = appliedChanges.Item1;
var newSolution = appliedChanges.Item2;
var (oldSolution, newSolution) = ApplyOperationsAndGetSolution(workspace, operations);
Assert.All(newSolution.Projects.Where(p => p.Language == LanguageNames.CSharp),
p => Assert.Equal(expected, ((CSharpParseOptions)p.ParseOptions).SpecifiedLanguageVersion));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public partial class TestWorkspace
private const string AliasAttributeName = "Alias";
private const string ProjectNameAttribute = "Name";
private const string CheckOverflowAttributeName = "CheckOverflow";
private const string AllowUnsafeAttributeName = "AllowUnsafe";
private const string OutputKindName = "OutputKind";

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ private static CompilationOptions CreateCompilationOptions(TestWorkspace workspa
var strongNameProvider = default(StrongNameProvider);
var delaySign = default(bool?);
var checkOverflow = false;
var allowUnsafe = false;
var outputKind = OutputKind.DynamicallyLinkedLibrary;

if (compilationOptionsElement != null)
Expand All @@ -514,6 +515,12 @@ private static CompilationOptions CreateCompilationOptions(TestWorkspace workspa
checkOverflow = (bool)checkOverflowAttribute;
}

var allowUnsafeAttribute = compilationOptionsElement.Attribute(AllowUnsafeAttributeName);
if (allowUnsafeAttribute != null)
{
allowUnsafe = (bool)allowUnsafeAttribute;
}

var reportDiagnosticAttribute = compilationOptionsElement.Attribute(ReportDiagnosticAttributeName);
if (reportDiagnosticAttribute != null)
{
Expand Down Expand Up @@ -560,7 +567,7 @@ private static CompilationOptions CreateCompilationOptions(TestWorkspace workspa

// VB needs Compilation.ParseOptions set (we do the same at the VS layer)
return language == LanguageNames.CSharp
? (CompilationOptions)new CSharpCompilationOptions(OutputKind.WindowsRuntimeMetadata)
? (CompilationOptions)new CSharpCompilationOptions(OutputKind.WindowsRuntimeMetadata, allowUnsafe: allowUnsafe)
: new VisualBasicCompilationOptions(OutputKind.WindowsRuntimeMetadata).WithGlobalImports(globalImports).WithRootNamespace(rootNamespace)
.WithParseOptions((VisualBasicParseOptions)parseOptions ?? VisualBasicParseOptions.Default);
}
Expand Down Expand Up @@ -588,6 +595,11 @@ private static CompilationOptions CreateCompilationOptions(TestWorkspace workspa
.WithDelaySign(delaySign)
.WithOverflowChecks(checkOverflow);

if (language == LanguageNames.CSharp)
{
compilationOptions = ((CSharpCompilationOptions)compilationOptions).WithAllowUnsafe(allowUnsafe);
}

if (language == LanguageNames.VisualBasic)
{
// VB needs Compilation.ParseOptions set (we do the same at the VS layer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ private static XAttribute CreateDocumentationModeAttribute(ParseOptions parseOpt

private static XElement CreateCompilationOptionsElement(CompilationOptions options)
{
XElement element = null;
if (options is Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions vbOptions)
var element = new XElement(CompilationOptionsElementName);

if (options is CodeAnalysis.CSharp.CSharpCompilationOptions csOptions)
{
element.SetAttributeValue(AllowUnsafeAttributeName, csOptions.AllowUnsafe);
}
else if (options is CodeAnalysis.VisualBasic.VisualBasicCompilationOptions vbOptions)
{
element = new XElement(CompilationOptionsElementName,
vbOptions.GlobalImports.AsEnumerable().Select(i => new XElement(GlobalImportElementName, i.Name)));
element.Add(vbOptions.GlobalImports.AsEnumerable().Select(i => new XElement(GlobalImportElementName, i.Name)));

if (vbOptions.RootNamespace != null)
{
Expand All @@ -90,7 +94,6 @@ private static XElement CreateCompilationOptionsElement(CompilationOptions optio

if (options.CheckOverflow)
{
element = element ?? new XElement(CompilationOptionsElementName);
element.SetAttributeValue(CheckOverflowAttributeName, true);
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Features/CSharp/Portable/CSharpFeaturesResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@
<data name="if_statement_can_be_simplified" xml:space="preserve">
<value>'if' statement can be simplified</value>
</data>
<data name="Allow_unsafe_code_in_this_project" xml:space="preserve">
<value>Allow unsafe code in this project</value>
</data>
<data name="Add_Obsolete" xml:space="preserve">
<value>Add [Obsolete]</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.UpgradeProject;

namespace Microsoft.CodeAnalysis.CSharp.UpdateProjectToAllowUnsafe
{
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
internal class CSharpUpdateProjectToAllowUnsafeCodeFixProvider : CodeFixProvider
{
private const string CS0227 = nameof(CS0227); // error CS0227: Unsafe code may only appear if compiling with /unsafe

public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(CS0227);

public override FixAllProvider GetFixAllProvider()
{
// We're OK with users having to explicitly opt in each project. Unsafe code is itself an edge case and we don't
// need to make it easier to convert to it on a larger scale. It's also unlikely that anyone will need this.
return null;
}

public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(new ProjectOptionsChangeAction(CSharpFeaturesResources.Allow_unsafe_code_in_this_project,
_ => Task.FromResult(AllowUnsafeOnProject(context.Document.Project))), context.Diagnostics);
return Task.CompletedTask;
}

private static Solution AllowUnsafeOnProject(Project project)
{
var compilationOptions = (CSharpCompilationOptions)project.CompilationOptions;
return project.Solution.WithProjectCompilationOptions(project.Id, compilationOptions.WithAllowUnsafe(true));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<target state="new">Add/remove braces for single-line control statements</target>
<note />
</trans-unit>
<trans-unit id="Allow_unsafe_code_in_this_project">
<source>Allow unsafe code in this project</source>
<target state="new">Allow unsafe code in this project</target>
<note />
</trans-unit>
<trans-unit id="Apply_expression_block_body_preferences">
<source>Apply expression/block body preferences</source>
<target state="new">Apply expression/block body preferences</target>
Expand Down
Loading

0 comments on commit b8b7d7e

Please sign in to comment.