Commit d157f4ae authored by Jesse McGrew's avatar Jesse McGrew
Browse files

Added <SUPPRESS-WARNINGS?> to control warning suppression from source code. [ZILF-195]

parent aa5d1b2e05cf
......@@ -37,6 +37,8 @@ namespace Zilf.Diagnostics
[NotNull]
readonly HashSet<string> suppressions = new HashSet<string>();
bool suppressAllTheThings = false;
[NotNull]
public IReadOnlyCollection<Diagnostic> Diagnostics => diagnostics;
public int ErrorCount => Diagnostics.Count(d => d.Severity == Severity.Error || d.Severity == Severity.Fatal);
......@@ -58,9 +60,28 @@ namespace Zilf.Diagnostics
OutputWriter = outputWriter ?? Console.Error;
}
public void AddSuppression([NotNull] string code)
public void Suppress([NotNull] string code)
{
if (!suppressAllTheThings)
suppressions.Add(code);
}
public void Suppress([NotNull, ItemNotNull] IEnumerable<string> codes)
{
suppressions.Add(code);
if (!suppressAllTheThings)
suppressions.UnionWith(codes);
}
public void SuppressAll()
{
suppressAllTheThings = true;
suppressions.Clear();
}
public void SuppressNone()
{
suppressAllTheThings = false;
suppressions.Clear();
}
public void Handle([NotNull] Diagnostic diag)
......@@ -80,14 +101,22 @@ namespace Zilf.Diagnostics
}
}
if (diag.Severity < Severity.Error && suppressions.Contains(diag.Code))
if (IsSuppressed(diag))
{
suppressedDiagnostics.Add(diag);
}
else
{
{
OutputWriter.WriteLine(Formatter.Format(diag));
}
}
private bool IsSuppressed([NotNull] Diagnostic diag)
{
if (diag.Severity >= Severity.Error)
return false;
return suppressAllTheThings || suppressions.Contains(diag.Code);
}
}
}
......@@ -722,7 +722,7 @@ namespace Zilf.Interpreter
}
else
{
throw new ArgumentOutOfRangeException(nameof(paramType));
throw new ArgumentOutOfRangeException(nameof(paramType), $"Unhandled type '{paramType}'");
}
// modifiers
......
......@@ -103,7 +103,7 @@ namespace Zilf.Interpreter
}
[NotNull]
readonly DiagnosticManager diagnostics;
public DiagnosticManager DiagnosticManager { get; }
readonly bool ignoreCase;
[NotNull]
......@@ -154,8 +154,8 @@ namespace Zilf.Interpreter
{
this.ignoreCase = ignoreCase;
diagnostics = new DiagnosticManager();
diagnostics.TooManyErrors += (sender, args) =>
this.DiagnosticManager = new DiagnosticManager();
this.DiagnosticManager.TooManyErrors += (sender, args) =>
{
if (RunMode == RunMode.Compiler)
{
......@@ -247,23 +247,18 @@ namespace Zilf.Interpreter
public bool WarningsAsErrors
{
get => diagnostics.WarningsAsErrors;
set => diagnostics.WarningsAsErrors = value;
get => DiagnosticManager.WarningsAsErrors;
set => DiagnosticManager.WarningsAsErrors = value;
}
public void SuppressDiagnostic([NotNull] string code)
{
diagnostics.AddSuppression(code);
}
public int ErrorCount => diagnostics.ErrorCount;
public int ErrorCount => DiagnosticManager.ErrorCount;
public int WarningCount => diagnostics.WarningCount;
public int WarningCount => DiagnosticManager.WarningCount;
public int SuppressedWarningCount => diagnostics.SuppressedWarningCount;
public int SuppressedWarningCount => DiagnosticManager.SuppressedWarningCount;
[NotNull]
public IReadOnlyCollection<Diagnostic> Diagnostics => diagnostics.Diagnostics;
public IReadOnlyCollection<Diagnostic> Diagnostics => DiagnosticManager.Diagnostics;
[NotNull]
public ZEnvironment ZEnvironment => zenv;
......@@ -775,7 +770,7 @@ namespace Zilf.Interpreter
}
}
public void HandleError([NotNull] ZilErrorBase ex) => diagnostics.Handle(ex.Diagnostic);
public void HandleError([NotNull] ZilErrorBase ex) => DiagnosticManager.Handle(ex.Diagnostic);
/// <exception cref="FileNotFoundException">The file wasn't found in any include path.</exception>
[NotNull]
......
......@@ -95,7 +95,7 @@ namespace Zilf.Interpreter
[NotNull]
[Subr]
public static ZilObject LOOKUP(Context ctx, [NotNull] string str, [NotNull] ObList oblist)
public static ZilObject LOOKUP([NotNull] Context ctx, [NotNull] string str, [NotNull] ObList oblist)
{
return oblist.Contains(str) ? oblist[str] : ctx.FALSE;
}
......@@ -103,8 +103,8 @@ namespace Zilf.Interpreter
/// <exception cref="InterpreterError"><paramref name="oblist"/> already contains an atom named <paramref name="stringOrAtom"/>, or <paramref name="stringOrAtom"/> is an atom that is already on a different OBLIST.</exception>
[NotNull]
[Subr]
public static ZilObject INSERT(Context ctx,
[Either(typeof(string), typeof(ZilAtom))] object stringOrAtom,
public static ZilObject INSERT([NotNull] Context ctx,
[NotNull, Either(typeof(string), typeof(ZilAtom))] object stringOrAtom,
[NotNull] ObList oblist)
{
switch (stringOrAtom)
......
......@@ -384,6 +384,66 @@ namespace Zilf.Interpreter
return ctx.TRUE;
}
public static class WarningParams
{
[ZilSequenceParam]
[ParamDesc("all-none-or-codes")]
public struct CodesOrWildcard
{
[Either(typeof(Wildcard), typeof(AtomParams.StringOrAtom[]))]
public object Content;
public StdAtom? GetWildcard()
{
return Content is Wildcard w ? w.Atom.StdAtom : (StdAtom?)null;
}
[CanBeNull]
public string[] GetCodes()
{
return Content is AtomParams.StringOrAtom[] sas
? sas.Select(sa => sa.ToString()).ToArray()
: null;
}
}
[ZilSequenceParam]
public struct Wildcard
{
[Decl("<OR 'ALL 'NONE>")]
public ZilAtom Atom;
public StdAtom StdAtom => Atom.StdAtom;
}
}
[NotNull]
[Subr("SUPPRESS-WARNINGS?")]
public static ZilObject SUPPRESS_WARNINGS_P([NotNull] Context ctx,
WarningParams.CodesOrWildcard codesOrWildcard)
{
switch (codesOrWildcard.GetWildcard())
{
case StdAtom.ALL:
ctx.DiagnosticManager.SuppressAll();
break;
case StdAtom.NONE:
ctx.DiagnosticManager.SuppressNone();
break;
default:
var codes = codesOrWildcard.GetCodes();
System.Diagnostics.Debug.Assert(codes != null);
foreach (var code in codes)
ctx.DiagnosticManager.Suppress(code.ToString());
break;
}
return ctx.TRUE;
}
#region IDE Help
[NotNull]
......
......@@ -40,6 +40,7 @@ namespace Zilf.Language
ADECL,
ADJ,
ADJECTIVE,
ALL,
AND,
ANY,
APPLICABLE,
......
......@@ -269,7 +269,7 @@ namespace Zilf
AddImplicitIncludePaths(ctx.IncludePaths, newInFile, mode.Value);
foreach (var code in suppressedDiagnosticCodes)
ctx.SuppressDiagnostic(code);
ctx.DiagnosticManager.Suppress(code);
inFile = newInFile;
outFile = newOutFile;
......
......@@ -37,8 +37,8 @@ namespace Zilf.Tests.Integration
[NotNull]
protected readonly StringBuilder input = new StringBuilder();
[NotNull]
protected readonly List<(Predicate<IReadOnlyCollection<Diagnostic>>, string message)> warningChecks =
new List<(Predicate<IReadOnlyCollection<Diagnostic>>, string message)>();
protected readonly List<(Predicate<ZlrHelperRunResult>, string message)> warningChecks =
new List<(Predicate<ZlrHelperRunResult>, string message)>();
protected bool wantCompileOutput;
protected bool wantDebugInfo;
......@@ -108,7 +108,7 @@ namespace Zilf.Tests.Integration
[NotNull]
public TThis WithWarnings()
{
warningChecks.Add((diags => diags.Any(d => d.Severity == Severity.Warning),
warningChecks.Add((res => res.Diagnostics.Any(d => d.Severity == Severity.Warning),
"Expected a nonzero number of warnings."));
return (TThis)this;
}
......@@ -123,7 +123,7 @@ namespace Zilf.Tests.Integration
{
foreach (var code in expectedWarningCodes)
{
warningChecks.Add((diags => diags.Any(d => DiagnosticCodeMatches(d, code)),
warningChecks.Add((res => res.Diagnostics.Any(d => DiagnosticCodeMatches(d, code)),
$"Expected a diagnostic with code '{code}'."));
}
return (TThis)this;
......@@ -132,17 +132,26 @@ namespace Zilf.Tests.Integration
[NotNull]
public TThis WithoutWarnings()
{
warningChecks.Add((diags => diags.All(d => d.Severity != Severity.Warning),
warningChecks.Add((res => res.Diagnostics.All(d => d.Severity != Severity.Warning),
"Expected no warnings."));
return (TThis)this;
}
[NotNull]
public TThis WithoutUnsuppressedWarnings()
{
warningChecks.Add(
(res => res.Diagnostics.Count(d => d.Severity == Severity.Warning) == res.SuppressedWarningCount,
"Expected all warnings to be suppressed."));
return (TThis)this;
}
[NotNull]
public TThis WithoutWarnings(params string[] unexpectedWarningCodes)
{
foreach (var code in unexpectedWarningCodes)
{
warningChecks.Add((diags => !diags.Any(d => DiagnosticCodeMatches(d, code)),
warningChecks.Add((res => !res.Diagnostics.Any(d => DiagnosticCodeMatches(d, code)),
$"Expected no diagnostic with code '{code}'."));
}
return (TThis)this;
......@@ -183,7 +192,7 @@ namespace Zilf.Tests.Integration
protected void CheckWarnings(ZlrHelperRunResult res)
{
foreach (var (check, message) in warningChecks)
if (!check(res.Diagnostics))
if (!check(res))
Assert.Fail(message);
}
}
......@@ -375,6 +384,7 @@ namespace Zilf.Tests.Integration
{
WarningCount = helper.WarningCount,
Diagnostics = helper.Diagnostics,
SuppressedWarningCount = helper.SuppressedWarningCount,
});
return new CodeMatchingResult(output);
......
......@@ -245,5 +245,28 @@ namespace Zilf.Tests.Integration
.DoesNotCompile("ZIL0204", // no such {0} variable '{1}', using the {2} instead
diag => diag.Severity == Severity.Error);
}
[TestMethod]
public void Warnings_Can_Be_Suppressed()
{
AssertRoutine("", ".X")
.WithGlobal("<GLOBAL X 5>")
.WithGlobal("<SUPPRESS-WARNINGS? \"ZIL0204\">")
.WithoutUnsuppressedWarnings()
.GivesNumber("5");
AssertRoutine("", ".X")
.WithGlobal("<GLOBAL X 5>")
.WithGlobal("<SUPPRESS-WARNINGS? ALL>")
.WithoutUnsuppressedWarnings()
.GivesNumber("5");
AssertRoutine("", ".X")
.WithGlobal("<GLOBAL X 5>")
.WithGlobal("<SUPPRESS-WARNINGS? \"ZIL0204\">")
.WithGlobal("<SUPPRESS-WARNINGS? NONE>")
.WithWarnings("ZIL0204")
.GivesNumber("5");
}
}
}
......@@ -49,6 +49,7 @@ namespace Zilf.Tests.Integration
// ReSharper disable once NotAccessedField.Global
public string Output;
public int WarningCount;
public int SuppressedWarningCount;
public int ErrorCount;
public IReadOnlyCollection<Diagnostic> Diagnostics;
}
......@@ -56,7 +57,7 @@ namespace Zilf.Tests.Integration
sealed class ZlrHelper : IDisposable
{
public static void RunAndAssert([NotNull] string code, string input, [NotNull] string expectedOutput,
IEnumerable<(Predicate<IReadOnlyCollection<Diagnostic>>, string message)> warningChecks = null,
IEnumerable<(Predicate<ZlrHelperRunResult>, string message)> warningChecks = null,
bool wantCompileOutput = false)
{
var helper = new ZlrHelper(code, input);
......@@ -75,8 +76,16 @@ namespace Zilf.Tests.Integration
Assert.IsTrue(helper.Assemble(), "Failed to assemble");
if (warningChecks != null)
{
var result = new ZlrHelperRunResult
{
Diagnostics = helper.Diagnostics,
ErrorCount = helper.ErrorCount,
WarningCount = helper.WarningCount,
Status = ZlrTestStatus.Finished,
SuppressedWarningCount = helper.SuppressedWarningCount,
};
foreach (var (check, message) in warningChecks)
if (!check(helper.Diagnostics))
if (!check(result))
Assert.Fail(message);
}
string actualOutput = compileOutput + helper.Execute();
......@@ -134,8 +143,9 @@ namespace Zilf.Tests.Integration
public int ErrorCount { get; private set; }
public int WarningCount { get; private set; }
public int SuppressedWarningCount { get; private set; }
[CanBeNull]
public IReadOnlyCollection<Diagnostic> Diagnostics { get; private set; }
public IReadOnlyCollection<Diagnostic> Diagnostics { get; private set; } // includes suppressed
public ZlrHelper([NotNull] string code, [CanBeNull] string input)
{
......@@ -217,6 +227,7 @@ namespace Zilf.Tests.Integration
ErrorCount = result.ErrorCount;
WarningCount = result.WarningCount;
Diagnostics = result.Diagnostics;
SuppressedWarningCount = result.SuppressedWarningCount;
if (result.Success)
{
PrintZapCode();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment