提交 31073d01 编写于 作者: J Julien Couvreur 提交者: GitHub

Add "#error version" to C# and udpate UpgradeProject to support 7.1 (#18045)

上级 8a078fac
......@@ -2851,6 +2851,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Compiler version: &apos;{0}&apos;. Language version: {1}..
/// </summary>
internal static string ERR_CompilerAndLanguageVersion {
get {
return ResourceManager.GetString("ERR_CompilerAndLanguageVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An expression tree lambda may not contain a COM call with ref omitted on arguments.
/// </summary>
......
......@@ -5041,4 +5041,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_VoidAssignment" xml:space="preserve">
<value>A value of type 'void' may not be assigned.</value>
</data>
</root>
\ No newline at end of file
<data name="ERR_CompilerAndLanguageVersion" xml:space="preserve">
<value>Compiler version: '{0}'. Language version: {1}.</value>
</data>
</root>
......@@ -1471,5 +1471,6 @@ internal enum ErrorCode
ERR_InvalidPreprocessingSymbol = 8301,
ERR_FeatureNotAvailableInVersion7_1 = 8302,
ERR_LanguageVersionCannotHaveLeadingZeroes = 8303,
ERR_CompilerAndLanguageVersion = 8304,
}
}
\ No newline at end of file
}
......@@ -6,6 +6,7 @@
namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax
{
using System.Reflection;
using Microsoft.CodeAnalysis.Syntax.InternalSyntax;
internal class DirectiveParser : SyntaxParser
......@@ -308,7 +309,29 @@ private DirectiveTriviaSyntax ParseErrorOrWarningDirective(SyntaxToken hash, Syn
//could be negative if part of the error text comes from the trailing trivia of the keyword token
int triviaOffset = eod.GetLeadingTriviaWidth() - triviaWidth;
eod = this.AddError(eod, triviaOffset, triviaWidth, isError ? ErrorCode.ERR_ErrorDirective : ErrorCode.WRN_WarningDirective, triviaBuilder.ToString());
string errorText = triviaBuilder.ToString();
eod = this.AddError(eod, triviaOffset, triviaWidth, isError ? ErrorCode.ERR_ErrorDirective : ErrorCode.WRN_WarningDirective, errorText);
if (isError)
{
if (errorText.Equals("version", StringComparison.Ordinal))
{
Assembly assembly = typeof(CSharpCompiler).GetTypeInfo().Assembly;
string version = CommonCompiler.GetAssemblyFileVersion(assembly);
eod = this.AddError(eod, triviaOffset, triviaWidth, ErrorCode.ERR_CompilerAndLanguageVersion, version,
this.Options.SpecifiedLanguageVersion.ToDisplayString());
}
else
{
const string versionMarker = "version:";
if (errorText.StartsWith(versionMarker, StringComparison.Ordinal) &&
LanguageVersionFacts.TryParse(errorText.Substring(versionMarker.Length), out var languageVersion))
{
ErrorCode error = this.Options.LanguageVersion.GetErrorCode();
eod = this.AddError(eod, triviaOffset, triviaWidth, error, "version", new CSharpRequiredLanguageVersion(languageVersion));
}
}
}
}
if (isError)
......
......@@ -1307,6 +1307,7 @@ public void LanguageVersionAdded_Canary()
// - update the "UpgradeProject" codefixer
// - update the IDE drop-down for selecting Language Version
// - don't fix the canary test until you update all the tests that include it
// - update the command-line documentation (CommandLine.md)
Assert.Equal(LanguageVersion.CSharp7_1, LanguageVersion.Latest.MapSpecifiedToEffectiveVersion());
Assert.Equal(LanguageVersion.CSharp7, LanguageVersion.Default.MapSpecifiedToEffectiveVersion());
}
......
......@@ -2,10 +2,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -58,7 +61,7 @@ internal struct MemberInfo
#endregion
public class PreprocessorTests
public class PreprocessorTests : TestBase
{
public PreprocessorTests()
{
......@@ -182,7 +185,30 @@ private void VerifyDirectivesSpecial(CSharpSyntaxNode node, params DirectiveInfo
{
Assert.Equal(exp.Text, ((DefineDirectiveTriviaSyntax)dt).Name.ValueText); // Text
}
break;
case SyntaxKind.ErrorDirectiveTrivia:
if (null != exp.Text)
{
Assert.Equal(exp.Text, ((ErrorDirectiveTriviaSyntax)dt).EndOfDirectiveToken.ToFullString());
}
break;
case SyntaxKind.LoadDirectiveTrivia:
if (null != exp.Text)
{
Assert.Equal(exp.Text, ((LoadDirectiveTriviaSyntax)dt).File.ValueText);
}
break;
case SyntaxKind.UndefDirectiveTrivia:
if (null != exp.Text)
{
Assert.Equal(exp.Text, ((UndefDirectiveTriviaSyntax)dt).Name.ValueText);
}
break;
case SyntaxKind.ReferenceDirectiveTrivia:
if (null != exp.Text)
{
Assert.Equal(exp.Text, ((ReferenceDirectiveTriviaSyntax)dt).File.ValueText);
}
break;
case SyntaxKind.LineDirectiveTrivia:
var ld = dt as LineDirectiveTriviaSyntax;
......@@ -217,7 +243,12 @@ private void VerifyDirectivesSpecial(CSharpSyntaxNode node, params DirectiveInfo
Assert.NotEqual(SyntaxKind.None, ld.File.Kind());
Assert.Equal(exp.Text, ld.File.Value);
}
break;
default:
if (null != exp.Text)
{
Assert.True(false, String.Format("You are expecting some text in the directive, but this method doesn't know how to verify it for `{0}`.", exp.Kind));
}
break;
} // switch
}
......@@ -2560,7 +2591,7 @@ public void TestNegUndefWithBadTokensAfterName()
var node = Parse(text);
TestRoundTripping(node, text, false);
VerifyErrorCode(node, (int)ErrorCode.ERR_EndOfPPLineExpected);
VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.UndefDirectiveTrivia, Status = NodeStatus.IsActive, Text = "FOO(" });
VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.UndefDirectiveTrivia, Status = NodeStatus.IsActive, Text = "FOO" });
}
[Fact]
......@@ -2584,7 +2615,7 @@ public void TestNegUndefWithBadNumericalName()
var node = Parse(text);
TestRoundTripping(node, text, false);
VerifyErrorCode(node, (int)ErrorCode.ERR_IdentifierExpected);
VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.UndefDirectiveTrivia, Status = NodeStatus.IsActive, Text = "1234" });
VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.UndefDirectiveTrivia, Status = NodeStatus.IsActive, Text = "" });
}
[Fact]
......@@ -2962,6 +2993,71 @@ private void CheckDiagnosticStringFileName(string compilationFileName, string li
Assert.Equal(expectedErrorStringFileName, actualErrorStringFileName);
}
[Fact]
public void TestErrorWithVersion()
{
var text = "#error version";
var node = Parse(text, SourceCodeKind.Regular);
TestRoundTripping(node, text, disallowErrors: false);
VerifyDirectivesSpecial(node, new DirectiveInfo
{
Kind = SyntaxKind.ErrorDirectiveTrivia,
Status = NodeStatus.IsActive,
Text = "version"
});
node.GetDiagnostics().Verify(
// (1,8): error CS1029: #error: 'version'
// #error version
Diagnostic(ErrorCode.ERR_ErrorDirective, "version").WithArguments("version").WithLocation(1, 8),
// (1,8): error CS8304: Compiler version: '42.42.42.42424 (<developer build>)'. Language version: 4.
// #error version
Diagnostic(ErrorCode.ERR_CompilerAndLanguageVersion, "version").WithArguments(GetExpectedVersion(), "4").WithLocation(1, 8)
);
}
[Fact]
public void TestErrorWithVersionNumber()
{
var text = "#error version:7.1";
var node = Parse(text, SourceCodeKind.Regular);
TestRoundTripping(node, text, disallowErrors: false);
VerifyDirectivesSpecial(node, new DirectiveInfo
{
Kind = SyntaxKind.ErrorDirectiveTrivia,
Status = NodeStatus.IsActive,
Text = "version:7.1"
});
node.GetDiagnostics().Verify(
// (1,8): error CS1029: #error: 'version:7.1'
// #error version:7.1
Diagnostic(ErrorCode.ERR_ErrorDirective, "version:7.1").WithArguments("version:7.1"),
// (1,8): error CS8025: Feature 'version' is not available in C# 4. Please use language version 7.1 or greater.
// #error version:7.1
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion4, "version:7.1").WithArguments("version", "7.1").WithLocation(1, 8)
);
}
[Fact]
public void TestErrorWithInvalidVersion()
{
var text = "#error version:A.B";
var node = Parse(text, SourceCodeKind.Regular);
TestRoundTripping(node, text, disallowErrors: false);
VerifyDirectivesSpecial(node, new DirectiveInfo
{
Kind = SyntaxKind.ErrorDirectiveTrivia,
Status = NodeStatus.IsActive,
Text = "version:A.B"
});
node.GetDiagnostics().Verify(
// (1,8): error CS1029: #error: 'version:A.B'
// #error version:A.B
Diagnostic(ErrorCode.ERR_ErrorDirective, "version:A.B").WithArguments("version:A.B").WithLocation(1, 8)
);
}
#endregion
#region #line
......@@ -3874,5 +3970,13 @@ public void TestLoadWithComment()
}
#endregion
private static string GetExpectedVersion()
{
Assembly assembly = typeof(CSharpCompiler).GetTypeInfo().Assembly;
string fileVersion = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
string hash = CommonCompiler.ExtractShortCommitHash(assembly.GetCustomAttribute<CommitHashAttribute>().Hash);
return $"{fileVersion} ({hash})";
}
}
}
......@@ -128,18 +128,15 @@ public CommonCompiler(CommandLineParser parser, string responseFile, string[] ar
/// </summary>
internal virtual string GetAssemblyFileVersion()
{
if (_clientDirectory != null)
{
Assembly assembly = Type.GetTypeInfo().Assembly;
var name = $"{assembly.GetName().Name}.dll";
var filePath = Path.Combine(_clientDirectory, name);
var fileVersionInfo = FileVersionInfo.GetVersionInfo(filePath);
string hash = ExtractShortCommitHash(assembly.GetCustomAttribute<CommitHashAttribute>()?.Hash);
return $"{fileVersionInfo.FileVersion} ({hash})";
}
Assembly assembly = Type.GetTypeInfo().Assembly;
return GetAssemblyFileVersion(assembly);
}
return "";
internal static string GetAssemblyFileVersion(Assembly assembly)
{
string assemblyVersion = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version;
string hash = ExtractShortCommitHash(assembly.GetCustomAttribute<CommitHashAttribute>()?.Hash);
return $"{assemblyVersion} ({hash})";
}
internal static string ExtractShortCommitHash(string hash)
......
......@@ -39,7 +39,7 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeProjectToDefault()
public async Task UpgradeProjectFromCSharp6ToDefault()
{
await TestLanguageVersionUpgradedAsync(
@"
......@@ -55,7 +55,7 @@ void A()
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeProjectToCSharp7()
public async Task UpgradeProjectFromCSharp6ToCSharp7()
{
await TestLanguageVersionUpgradedAsync(
@"
......@@ -71,6 +71,46 @@ void A()
index: 1);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeProjectFromCSharp7ToLatest()
{
await TestLanguageVersionUpgradedAsync(
@"
class Program
{
#error version:[|7.1|]
}",
LanguageVersion.Latest,
new CSharpParseOptions(LanguageVersion.CSharp7));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeProjectFromCSharp7_1ToLatest()
{
await TestLanguageVersionUpgradedAsync(
@"
class Program
{
#error version:[|7.1|]
}",
LanguageVersion.Latest,
new CSharpParseOptions(LanguageVersion.CSharp7_1));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeProjectFromCSharp7ToCSharp7_1()
{
await TestLanguageVersionUpgradedAsync(
@"
class Program
{
#error [|version:7.1|]
}",
LanguageVersion.CSharp7_1,
new CSharpParseOptions(LanguageVersion.CSharp7),
index: 1);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)]
public async Task UpgradeAllProjectsToDefault()
{
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -69,7 +70,9 @@ protected bool TryGetDocumentAndSelectSpan(TestWorkspace workspace, out Document
protected Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, out string annotation, out TextSpan span)
{
var hostDocument = workspace.Documents.Single(d => d.AnnotatedSpans.Any());
var annotatedDocuments = workspace.Documents.Where(d => d.AnnotatedSpans.Any());
Debug.Assert(!annotatedDocuments.IsEmpty(), "No annotated span found");
var hostDocument = annotatedDocuments.Single();
var annotatedSpan = hostDocument.AnnotatedSpans.Single();
annotation = annotatedSpan.Key;
span = annotatedSpan.Value.Single();
......
......@@ -22,9 +22,11 @@ internal class CSharpUpgradeProjectCodeFixProvider : AbstractUpgradeProjectCodeF
private const string CS8025 = nameof(CS8025); // error CS8025: Feature is not available in C# 4. Please use language version X or greater.
private const string CS8026 = nameof(CS8026); // error CS8026: Feature is not available in C# 5. Please use language version X or greater.
private const string CS8059 = nameof(CS8059); // error CS8059: Feature is not available in C# 6. Please use language version X or greater.
private const string CS8107 = nameof(CS8107); // error CS8059: Feature is not available in C# 7.0. Please use language version X or greater.
private const string CS8302 = nameof(CS8302); // error CS8302: Feature is not available in C# 7.1. Please use language version X or greater.
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059);
ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059, CS8107, CS8302);
public override string UpgradeThisProjectResource => CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0;
public override string UpgradeAllProjectsResource => CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0;
......
......@@ -138,7 +138,7 @@ Public Class ParseTree
Return Enumerations(enumString)
End If
ReportError(referencingElement, "{0} is not a valid field type", enumString)
ReportError(referencingElement, "{0} is not a valid field type. You should add a node-kind entry in the syntax.xml.", enumString)
Return Nothing
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册