提交 716f73eb 编写于 作者: T Tom Meschter

Expose the rule set file path in CommonCommandLineArguments

Currently the `CommonCommandLineParser` handles the `/ruleset` switch by reading the .ruleset file and setting the general and specific diagnostic options. However, it doesn't actually save the file path anywhere. This commit now exposes it through the `CommonCommandLineArguments` type, like any other switch that takes a file path.

This change also corrects a subtle issue. As with other switches that take paths, if the `/ruleset` switch appears more than once the last occurrence should "win", and prior occurences should be ignored. However, the dictionary of specific diagnostic options was shared between processing one `/ruleset` and the next; even though the last one should win you could still end up with settings from the other ones. With this change the set of specific diagnostic options is reset with every `/ruleset` processed, ensuring only the last one has any effect.

Technically this is a breaking change. However, you would have to work pretty hard to pass more than one `/ruleset` switch to the compiler. MSBuild only allows one, at most, so you would have to invoke the compiler directly.
上级 3dff8b3c
......@@ -121,6 +121,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
bool interactiveMode = false;
bool publicSign = false;
string sourceLink = null;
string ruleSetPath = null;
// Process ruleset files first so that diagnostic severity settings specified on the command line via
// /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file.
......@@ -139,7 +140,8 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
}
else
{
generalDiagnosticOption = GetDiagnosticOptionsFromRulesetFile(diagnosticOptions, diagnostics, unquoted, baseDirectory);
ruleSetPath = ParseGenericPathToFile(unquoted, diagnostics, baseDirectory);
generalDiagnosticOption = GetDiagnosticOptionsFromRulesetFile(ruleSetPath, out diagnosticOptions, diagnostics);
}
}
}
......@@ -1291,6 +1293,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
PdbPath = pdbPath,
EmitPdb = emitPdb,
SourceLink = sourceLink,
RuleSetPath = ruleSetPath,
OutputDirectory = outputDirectory,
DocumentationPath = documentationPath,
ErrorLogPath = errorLogPath,
......
......@@ -2178,6 +2178,7 @@ public void RuleSetSwitchPositive()
var file = CreateRuleSetFile(source);
var parsedArgs = DefaultParse(new string[] { @"/ruleset:" + file.Path, "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(expected: file.Path, actual: parsedArgs.RuleSetPath);
Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1012"));
Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions["CA1012"] == ReportDiagnostic.Error);
Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1013"));
......@@ -2203,6 +2204,7 @@ public void RuleSetSwitchQuoted()
var file = CreateRuleSetFile(source);
var parsedArgs = DefaultParse(new string[] { @"/ruleset:" + "\"" + file.Path + "\"", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(expected: file.Path, actual: parsedArgs.RuleSetPath);
}
[Fact]
......@@ -2211,23 +2213,28 @@ public void RuleSetSwitchParseErrors()
var parsedArgs = DefaultParse(new string[] { @"/ruleset", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "ruleset"));
Assert.Null(parsedArgs.RuleSetPath);
parsedArgs = DefaultParse(new string[] { @"/ruleset:", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "ruleset"));
Assert.Null(parsedArgs.RuleSetPath);
parsedArgs = DefaultParse(new string[] { @"/ruleset:blah", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
Diagnostic(ErrorCode.ERR_CantReadRulesetFile).WithArguments(Path.Combine(TempRoot.Root, "blah"), "File not found."));
Assert.Equal(expected: Path.Combine(TempRoot.Root, "blah"), actual: parsedArgs.RuleSetPath);
parsedArgs = DefaultParse(new string[] { @"/ruleset:blah;blah.ruleset", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
Diagnostic(ErrorCode.ERR_CantReadRulesetFile).WithArguments(Path.Combine(TempRoot.Root, "blah;blah.ruleset"), "File not found."));
Assert.Equal(expected: Path.Combine(TempRoot.Root, "blah;blah.ruleset"), actual: parsedArgs.RuleSetPath);
var file = CreateRuleSetFile("Random text");
parsedArgs = DefaultParse(new string[] { @"/ruleset:" + file.Path, "a.cs" }, _baseDirectory);
//parsedArgs.Errors.Verify(
// Diagnostic(ErrorCode.ERR_CantReadRulesetFile).WithArguments(file.Path, "Data at the root level is invalid. Line 1, position 1."));
Assert.Equal(expected: file.Path, actual: parsedArgs.RuleSetPath);
var err = parsedArgs.Errors.Single();
Assert.Equal((int)ErrorCode.ERR_CantReadRulesetFile, err.Code);
......
......@@ -96,6 +96,11 @@ public abstract class CommandLineArguments
/// </summary>
public string SourceLink { get; internal set; }
/// <summary>
/// Absolute path of the .ruleset file or null if not specified.
/// </summary>
public string RuleSetPath { get; internal set; }
/// <summary>
/// True to emit PDB information (to a standalone PDB file or embedded into the PE file).
/// </summary>
......@@ -121,6 +126,8 @@ public abstract class CommandLineArguments
/// </summary>
public string AppConfigPath { get; internal set; }
/// <summary>
/// Errors while parsing the command line arguments.
/// </summary>
......
......@@ -980,9 +980,9 @@ internal static SourceHashAlgorithm TryParseHashAlgorithmName(string arg)
internal abstract void GenerateErrorForNoFilesFoundInRecurse(string path, IList<Diagnostic> errors);
internal ReportDiagnostic GetDiagnosticOptionsFromRulesetFile(Dictionary<string, ReportDiagnostic> diagnosticOptions, IList<Diagnostic> diagnostics, string path, string baseDirectory)
internal ReportDiagnostic GetDiagnosticOptionsFromRulesetFile(string fullPath, out Dictionary<string, ReportDiagnostic> diagnosticOptions, IList<Diagnostic> diagnostics)
{
return RuleSet.GetDiagnosticOptionsFromRulesetFile(diagnosticOptions, path, baseDirectory, diagnostics, _messageProvider);
return RuleSet.GetDiagnosticOptionsFromRulesetFile(fullPath, out diagnosticOptions, diagnostics, _messageProvider);
}
/// <summary>
......
Microsoft.CodeAnalysis.CommandLineArguments.RuleSetPath.get -> string
Microsoft.CodeAnalysis.CommandLineReference.CommandLineReference(string reference, Microsoft.CodeAnalysis.MetadataReferenceProperties properties) -> void
Microsoft.CodeAnalysis.CommandLineSourceFile.CommandLineSourceFile(string path, bool isScript) -> void
Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void
......
......@@ -284,29 +284,18 @@ public static ImmutableArray<string> GetEffectiveIncludesFromFile(string filePat
/// </summary>
public static ReportDiagnostic GetDiagnosticOptionsFromRulesetFile(string rulesetFileFullPath, out Dictionary<string, ReportDiagnostic> specificDiagnosticOptions)
{
specificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>();
if (rulesetFileFullPath == null)
{
return ReportDiagnostic.Default;
}
return GetDiagnosticOptionsFromRulesetFile(specificDiagnosticOptions, rulesetFileFullPath, null, null);
return GetDiagnosticOptionsFromRulesetFile(rulesetFileFullPath, out specificDiagnosticOptions, null, null);
}
internal static ReportDiagnostic GetDiagnosticOptionsFromRulesetFile(Dictionary<string, ReportDiagnostic> diagnosticOptions, string path, string baseDirectory, IList<Diagnostic> diagnosticsOpt, CommonMessageProvider messageProviderOpt)
internal static ReportDiagnostic GetDiagnosticOptionsFromRulesetFile(string rulesetFileFullPath, out Dictionary<string, ReportDiagnostic> diagnosticOptions, IList<Diagnostic> diagnosticsOpt, CommonMessageProvider messageProviderOpt)
{
var resolvedPath = FileUtilities.ResolveRelativePath(path, baseDirectory);
if (resolvedPath == null)
{
if (diagnosticsOpt != null && messageProviderOpt != null)
{
diagnosticsOpt.Add(Diagnostic.Create(messageProviderOpt, messageProviderOpt.FTL_InputFileNameTooLong, path));
}
diagnosticOptions = new Dictionary<string, ReportDiagnostic>();
if (rulesetFileFullPath == null)
{
return ReportDiagnostic.Default;
}
return GetDiagnosticOptionsFromRulesetFile(diagnosticOptions, resolvedPath, diagnosticsOpt, messageProviderOpt);
return GetDiagnosticOptionsFromRulesetFile(diagnosticOptions, rulesetFileFullPath, diagnosticsOpt, messageProviderOpt);
}
private static ReportDiagnostic GetDiagnosticOptionsFromRulesetFile(Dictionary<string, ReportDiagnostic> diagnosticOptions, string resolvedPath, IList<Diagnostic> diagnosticsOpt, CommonMessageProvider messageProviderOpt)
......
......@@ -157,6 +157,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim interactiveMode As Boolean = False
Dim instrumentationKinds As ArrayBuilder(Of InstrumentationKind) = ArrayBuilder(Of InstrumentationKind).GetInstance()
Dim sourceLink As String = Nothing
Dim ruleSetPath As String = Nothing
' Process ruleset files first so that diagnostic severity settings specified on the command line via
' /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file.
......@@ -171,7 +172,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Continue For
End If
generalDiagnosticOption = GetDiagnosticOptionsFromRulesetFile(specificDiagnosticOptionsFromRuleSet, diagnostics, unquoted, baseDirectory)
ruleSetPath = ParseGenericPathToFile(unquoted, diagnostics, baseDirectory)
generalDiagnosticOption = GetDiagnosticOptionsFromRulesetFile(ruleSetPath, specificDiagnosticOptionsFromRuleSet, diagnostics)
End If
Next
End If
......@@ -1386,6 +1388,7 @@ lVbRuntimePlus:
.OutputLevel = outputLevel,
.EmitPdb = emitPdb,
.SourceLink = sourceLink,
.RuleSetPath = ruleSetPath,
.DefaultCoreLibraryReference = defaultCoreLibraryReference,
.PreferredUILang = preferredUILang,
.ReportAnalyzer = reportAnalyzer,
......
......@@ -2282,6 +2282,7 @@ End Module").Path
Dim file = CreateRuleSetFile(source)
Dim parsedArgs = DefaultParse(New String() {"/ruleset:" + file.Path, "a.cs"}, _baseDirectory)
parsedArgs.Errors.Verify()
Assert.Equal(expected:=file.Path, actual:=parsedArgs.RuleSetPath)
Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1012"))
Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions("CA1012") = ReportDiagnostic.Error)
Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1013"))
......@@ -2306,6 +2307,7 @@ End Module").Path
Dim file = CreateRuleSetFile(source)
Dim parsedArgs = DefaultParse(New String() {"/ruleset:" + """" + file.Path + """", "a.cs"}, _baseDirectory)
parsedArgs.Errors.Verify()
Assert.Equal(expected:=file.Path, actual:=parsedArgs.RuleSetPath)
End Sub
<Fact>
......@@ -2313,23 +2315,28 @@ End Module").Path
Dim parsedArgs = DefaultParse(New String() {"/ruleset", "a.cs"}, _baseDirectory)
parsedArgs.Errors.Verify(
Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("ruleset", ":<file>"))
Assert.Null(parsedArgs.RuleSetPath)
parsedArgs = DefaultParse(New String() {"/ruleset", "a.cs"}, _baseDirectory)
parsedArgs.Errors.Verify(
Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("ruleset", ":<file>"))
Assert.Null(parsedArgs.RuleSetPath)
parsedArgs = DefaultParse(New String() {"/ruleset:blah", "a.cs"}, _baseDirectory)
parsedArgs.Errors.Verify(
Diagnostic(ERRID.ERR_CantReadRulesetFile).WithArguments(Path.Combine(TempRoot.Root, "blah"), "File not found."))
Assert.Equal(expected:=Path.Combine(TempRoot.Root, "blah"), actual:=parsedArgs.RuleSetPath)
parsedArgs = DefaultParse(New String() {"/ruleset:blah;blah.ruleset", "a.cs"}, _baseDirectory)
parsedArgs.Errors.Verify(
Diagnostic(ERRID.ERR_CantReadRulesetFile).WithArguments(Path.Combine(TempRoot.Root, "blah;blah.ruleset"), "File not found."))
Assert.Equal(expected:=Path.Combine(TempRoot.Root, "blah;blah.ruleset"), actual:=parsedArgs.RuleSetPath)
Dim file = CreateRuleSetFile(New XDocument())
parsedArgs = DefaultParse(New String() {"/ruleset:" + file.Path, "a.cs"}, _baseDirectory)
'parsedArgs.Errors.Verify(
' Diagnostic(ERRID.ERR_CantReadRulesetFile).WithArguments(file.Path, "Root element is missing."))
Assert.Equal(expected:=file.Path, actual:=parsedArgs.RuleSetPath)
Dim err = parsedArgs.Errors.Single()
Assert.Equal(ERRID.ERR_CantReadRulesetFile, err.Code)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册