Skip to content

Commit

Permalink
Refactor reporting code (#4929)
Browse files Browse the repository at this point in the history
### Description
Make reporting of related locations more consistent, so related
locations are always shown on a separate line. Example:
```
-git-issue-864y.dfy(6,9): Error: duplicate name of top-level declaration: A [Related location] git-issue-864y.dfy(7,8)
+git-issue-864y.dfy(6,9): Error: duplicate name of top-level declaration: A
+git-issue-864y.dfy(7,8): Related location
```

### How has this been tested?
Expect files have been updated to match the new related location format.

<small>By submitting this pull request, I confirm that my contribution
is made under the terms of the [MIT
license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>
  • Loading branch information
keyboardDrummer authored Jan 10, 2024
1 parent 2ef450b commit 7d14cad
Show file tree
Hide file tree
Showing 26 changed files with 643 additions and 564 deletions.
76 changes: 76 additions & 0 deletions Source/DafnyCore/AST/Types/MapType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Microsoft.Dafny;

public class MapType : CollectionType {
public bool Finite {
get { return finite; }
set { finite = value; }
}
private bool finite;
public Type Range {
get { return range; }
}
private Type range;
public override void SetTypeArgs(Type domain, Type range) {
base.SetTypeArgs(domain, range);
Contract.Assume(this.range == null); // Can only set once. This is really a precondition.
this.range = range;
}
public MapType(bool finite, Type domain, Type range) : base(domain, range) {
Contract.Requires((domain == null && range == null) || (domain != null && range != null));
this.finite = finite;
this.range = range;
}
public Type Domain {
get { return Arg; }
}
public override string CollectionTypeName { get { return finite ? "map" : "imap"; } }
[System.Diagnostics.Contracts.Pure]
public override string TypeName(DafnyOptions options, ModuleDefinition context, bool parseAble) {
Contract.Ensures(Contract.Result<string>() != null);
var targs = HasTypeArg() ? this.TypeArgsToString(options, context, parseAble) : "";
return CollectionTypeName + targs;
}
public override bool Equals(Type that, bool keepConstraints = false) {
var t = that.NormalizeExpand(keepConstraints) as MapType;
return t != null && Finite == t.Finite && Arg.Equals(t.Arg, keepConstraints) && Range.Equals(t.Range, keepConstraints);
}

public override Type Subst(IDictionary<TypeParameter, Type> subst) {
var dom = Domain.Subst(subst);
if (dom is InferredTypeProxy) {
((InferredTypeProxy)dom).KeepConstraints = true;
}
var ran = Range.Subst(subst);
if (ran is InferredTypeProxy) {
((InferredTypeProxy)ran).KeepConstraints = true;
}
if (dom == Domain && ran == Range) {
return this;
} else {
return new MapType(Finite, dom, ran);
}
}

public override Type ReplaceTypeArguments(List<Type> arguments) {
return new MapType(Finite, arguments[0], arguments[1]);
}

public override bool SupportsEquality {
get {
// A map type supports equality if both its Keys type and Values type does. It is checked
// that the Keys type always supports equality, so we only need to check the Values type here.
return range.SupportsEquality;
}
}
public override bool ComputeMayInvolveReferences(ISet<DatatypeDecl> visitedDatatypes) {
return Domain.ComputeMayInvolveReferences(visitedDatatypes) || Range.ComputeMayInvolveReferences(visitedDatatypes);
}

public override BinaryExpr.ResolvedOpcode ResolvedOpcodeForIn => BinaryExpr.ResolvedOpcode.InMap;
public override ComprehensionExpr.CollectionBoundedPool GetBoundedPool(Expression source) {
return new ComprehensionExpr.MapBoundedPool(source, Domain, Domain, Finite);
}
}
71 changes: 0 additions & 71 deletions Source/DafnyCore/AST/Types/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2124,77 +2124,6 @@ public override ComprehensionExpr.CollectionBoundedPool GetBoundedPool(Expressio
return new ComprehensionExpr.SeqBoundedPool(source, Arg, Arg);
}
}
public class MapType : CollectionType {
public bool Finite {
get { return finite; }
set { finite = value; }
}
private bool finite;
public Type Range {
get { return range; }
}
private Type range;
public override void SetTypeArgs(Type domain, Type range) {
base.SetTypeArgs(domain, range);
Contract.Assume(this.range == null); // Can only set once. This is really a precondition.
this.range = range;
}
public MapType(bool finite, Type domain, Type range) : base(domain, range) {
Contract.Requires((domain == null && range == null) || (domain != null && range != null));
this.finite = finite;
this.range = range;
}
public Type Domain {
get { return Arg; }
}
public override string CollectionTypeName { get { return finite ? "map" : "imap"; } }
[System.Diagnostics.Contracts.Pure]
public override string TypeName(DafnyOptions options, ModuleDefinition context, bool parseAble) {
Contract.Ensures(Contract.Result<string>() != null);
var targs = HasTypeArg() ? this.TypeArgsToString(options, context, parseAble) : "";
return CollectionTypeName + targs;
}
public override bool Equals(Type that, bool keepConstraints = false) {
var t = that.NormalizeExpand(keepConstraints) as MapType;
return t != null && Finite == t.Finite && Arg.Equals(t.Arg, keepConstraints) && Range.Equals(t.Range, keepConstraints);
}

public override Type Subst(IDictionary<TypeParameter, Type> subst) {
var dom = Domain.Subst(subst);
if (dom is InferredTypeProxy) {
((InferredTypeProxy)dom).KeepConstraints = true;
}
var ran = Range.Subst(subst);
if (ran is InferredTypeProxy) {
((InferredTypeProxy)ran).KeepConstraints = true;
}
if (dom == Domain && ran == Range) {
return this;
} else {
return new MapType(Finite, dom, ran);
}
}

public override Type ReplaceTypeArguments(List<Type> arguments) {
return new MapType(Finite, arguments[0], arguments[1]);
}

public override bool SupportsEquality {
get {
// A map type supports equality if both its Keys type and Values type does. It is checked
// that the Keys type always supports equality, so we only need to check the Values type here.
return range.SupportsEquality;
}
}
public override bool ComputeMayInvolveReferences(ISet<DatatypeDecl> visitedDatatypes) {
return Domain.ComputeMayInvolveReferences(visitedDatatypes) || Range.ComputeMayInvolveReferences(visitedDatatypes);
}

public override BinaryExpr.ResolvedOpcode ResolvedOpcodeForIn => BinaryExpr.ResolvedOpcode.InMap;
public override ComprehensionExpr.CollectionBoundedPool GetBoundedPool(Expression source) {
return new ComprehensionExpr.MapBoundedPool(source, Domain, Domain, Finite);
}
}

public abstract class TypeProxy : Type {
public override IEnumerable<INode> Children => Enumerable.Empty<Node>();
Expand Down
8 changes: 6 additions & 2 deletions Source/DafnyCore/DafnyConsolePrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,15 @@ private string GetFileLine(Uri uri, int lineIndex) {
if (0 <= lineIndex && lineIndex < lines.Count) {
return lines[lineIndex];
}
return "<nonexistent line>";
return null;
}

public void WriteSourceCodeSnippet(IToken tok, TextWriter tw) {
string line = GetFileLine(tok.Uri, tok.line - 1);
if (line == null) {
return;
}

string lineNumber = tok.line.ToString();
string lineNumberSpaces = new string(' ', lineNumber.Length);
string columnSpaces = new string(' ', tok.col - 1);
Expand Down Expand Up @@ -149,4 +153,4 @@ public override void ReportEndVerifyImplementation(Implementation implementation
var impl = new ImplementationLogEntry(implementation.VerboseName, implementation.tok);
VerificationResults.Add(new ConsoleLogEntry(impl, DistillVerificationResult(result)));
}
}
}
80 changes: 46 additions & 34 deletions Source/DafnyCore/Generic/ConsoleErrorReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,51 +18,63 @@ private ConsoleColor ColorForLevel(ErrorLevel level) {
}

protected override bool MessageCore(MessageSource source, ErrorLevel level, string errorId, IToken tok, string msg) {
if (base.MessageCore(source, level, errorId, tok, msg) && (Options is { PrintTooltips: true } || level != ErrorLevel.Info)) {
// Extra indent added to make it easier to distinguish multiline error messages for clients that rely on the CLI
msg = msg.Replace("\n", "\n ");
var printMessage = base.MessageCore(source, level, errorId, tok, msg) && (Options is { PrintTooltips: true } || level != ErrorLevel.Info);
if (!printMessage) {
return false;
}

// Extra indent added to make it easier to distinguish multiline error messages for clients that rely on the CLI
msg = msg.Replace("\n", "\n ");

ConsoleColor previousColor = Console.ForegroundColor;
if (Options.OutputWriter == Console.Out) {
Console.ForegroundColor = ColorForLevel(level);
}
var errorLine = ErrorToString(level, tok, msg);

ConsoleColor previousColor = Console.ForegroundColor;
if (Options.OutputWriter == Console.Out) {
Console.ForegroundColor = ColorForLevel(level);
if (Options.Verbose && !String.IsNullOrEmpty(errorId) && errorId != "none") {
errorLine += " (ID: " + errorId + ")\n";
var info = ErrorRegistry.GetDetail(errorId);
if (info != null) {
errorLine += info; // already ends with eol character
}
var errorLine = ErrorToString(level, tok, msg);
while (tok is NestedToken nestedToken) {
tok = nestedToken.Inner;
if (tok.Filepath == nestedToken.Filepath &&
tok.line == nestedToken.line &&
tok.col == nestedToken.col) {
continue;
}
msg = nestedToken.Message ?? "[Related location]";
errorLine += $" {msg} {tok.TokenToString(Options)}";
} else {
errorLine += "\n";
}

var innerToken = tok;
while (innerToken is NestedToken nestedToken) {
innerToken = nestedToken.Inner;
if (innerToken.Filepath == nestedToken.Filepath &&
innerToken.line == nestedToken.line &&
innerToken.col == nestedToken.col) {
continue;
}

if (Options.Verbose && !String.IsNullOrEmpty(errorId) && errorId != "none") {
errorLine += " (ID: " + errorId + ")\n";
var info = ErrorRegistry.GetDetail(errorId);
if (info != null) {
errorLine += info; // already ends with eol character
}
var innerMessage = nestedToken.Message;
if (innerMessage == null) {
innerMessage = "Related location";
} else {
errorLine += "\n";
innerMessage = "Related location: " + innerMessage;
}

Options.OutputWriter.Write(errorLine);
errorLine += $"{innerToken.TokenToString(Options)}: {innerMessage}\n";
}

if (Options.Get(DafnyConsolePrinter.ShowSnippets) && tok.Uri != null) {
TextWriter tw = new StringWriter();
new DafnyConsolePrinter(Options).WriteSourceCodeSnippet(tok.ToRange(), tw);
Options.OutputWriter.Write(tw.ToString());
}
Options.OutputWriter.Write(errorLine);

if (Options.OutputWriter == Console.Out) {
Console.ForegroundColor = previousColor;
}
return true;

if (Options.Get(DafnyConsolePrinter.ShowSnippets) && tok.Uri != null) {
TextWriter tw = new StringWriter();
new DafnyConsolePrinter(Options).WriteSourceCodeSnippet(tok.ToRange(), tw);
Options.OutputWriter.Write(tw.ToString());
}

if (Options.OutputWriter == Console.Out) {
Console.ForegroundColor = previousColor;
}

return false;
return true;
}

public ConsoleErrorReporter(DafnyOptions options) : base(options) {
Expand Down
2 changes: 1 addition & 1 deletion Source/DafnyCore/Generic/ErrorReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected ErrorReporter(DafnyOptions options) {
public bool HasErrorsUntilResolver => ErrorCountUntilResolver > 0;
public int ErrorCountUntilResolver => CountExceptVerifierAndCompiler(ErrorLevel.Error);

public virtual bool Message(MessageSource source, ErrorLevel level, string errorId, IToken tok, string msg) {
public bool Message(MessageSource source, ErrorLevel level, string errorId, IToken tok, string msg) {
if (Options.WarningsAsErrors && level == ErrorLevel.Warning) {
level = ErrorLevel.Error;
}
Expand Down
11 changes: 6 additions & 5 deletions Source/DafnyCore/JsonDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ private static JsonObject SerializeRelated(Boogie.IToken tok, string? category,
}

private static IEnumerable<JsonNode> SerializeInnerTokens(Boogie.IToken tok) {
while (tok is NestedToken ntok) {
tok = ntok.Inner;
yield return SerializeRelated(tok, null, "Related location");
while (tok is NestedToken nestedToken) {
tok = nestedToken.Inner;
var message = nestedToken.Message != null ? "Related location: " + nestedToken.Message : "Related location";
yield return SerializeRelated(tok, null, message);
}
}

Expand Down Expand Up @@ -112,7 +113,7 @@ public DafnyJsonConsolePrinter(DafnyOptions options) : base(options) {
public class JsonConsoleErrorReporter : BatchErrorReporter {
protected override bool MessageCore(MessageSource source, ErrorLevel level, string errorID, Dafny.IToken tok, string msg) {
if (base.MessageCore(source, level, errorID, tok, msg) && (Options is { PrintTooltips: true } || level != ErrorLevel.Info)) {
new DiagnosticMessageData(source, level, tok, null, msg, null).WriteJsonTo(Options.OutputWriter);
new DiagnosticMessageData(source, level, tok, level == ErrorLevel.Error ? "Error" : null, msg, null).WriteJsonTo(Options.OutputWriter);
return true;
}

Expand All @@ -121,4 +122,4 @@ protected override bool MessageCore(MessageSource source, ErrorLevel level, stri

public JsonConsoleErrorReporter(DafnyOptions options) : base(options) {
}
}
}
3 changes: 2 additions & 1 deletion Source/DafnyCore/Resolver/ProgramResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ protected void InstantiateReplaceableModules(Program dafnyProgram) {
if (compiledModule.Implements is { Kind: ImplementationKind.Replacement }) {
var target = compiledModule.Implements.Target.Def;
if (target.Replacement != null) {
Reporter!.Error(MessageSource.Compiler, new NestedToken(compiledModule.Tok, target.Replacement.Tok, "Other replacing module:"),
Reporter!.Error(MessageSource.Compiler, new NestedToken(compiledModule.Tok, target.Replacement.Tok,
$"other replacing module"),
"a replaceable module may only be replaced once");
} else {
target.Replacement = compiledModule.Replacement ?? compiledModule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ protected override WorkspaceSymbolRegistrationOptions CreateRegistrationOptions(
Name = name,
ContainerName = def.Kind.ToString(),
Kind = FromDafnySymbolKind(def.Kind),
Location = Workspace.Util.CreateLocation(def.NameToken)
Location = Workspace.DiagnosticUtil.CreateLocation(def.NameToken)
}, matchDistance);
}
}
Expand Down
Loading

0 comments on commit 7d14cad

Please sign in to comment.