|
| 1 | +// Copyright by the contributors to the Dafny Project |
| 2 | +// SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | +using System.Diagnostics.Contracts; |
| 5 | +using Microsoft.Boogie; |
| 6 | +using Bpl = Microsoft.Boogie; |
| 7 | + |
| 8 | +namespace Microsoft.Dafny { |
| 9 | + // ------------------- PrintEffectEnforcement ------------------- |
| 10 | + |
| 11 | + public class PrintEffectEnforcement : IRewriter { |
| 12 | + internal PrintEffectEnforcement(ErrorReporter reporter) : base(reporter) { |
| 13 | + Contract.Requires(reporter != null); |
| 14 | + } |
| 15 | + |
| 16 | + internal override void PostDecreasesResolve(ModuleDefinition m) { |
| 17 | + foreach (var decl in m.TopLevelDecls) { |
| 18 | + if (decl is IteratorDecl iter) { |
| 19 | + var hasPrintAttribute = HasPrintAttribute(iter.Attributes); |
| 20 | + if (!hasPrintAttribute && iter.Body != null) { |
| 21 | + if (DafnyOptions.O.EnforcePrintEffects) { |
| 22 | + iter.Body.Body.Iter(stmt => CheckNoPrintEffects(stmt, iter)); |
| 23 | + } |
| 24 | + } |
| 25 | + } else if (decl is TopLevelDeclWithMembers cl) { |
| 26 | + foreach (var member in cl.Members) { |
| 27 | + var hasPrintAttribute = HasPrintAttribute(member.Attributes); |
| 28 | + if (member is Function f) { |
| 29 | + if (hasPrintAttribute) { |
| 30 | + Reporter.Error(MessageSource.Rewriter, member.tok, ":print attribute is not allowed on functions"); |
| 31 | + } |
| 32 | + if (f.ByMethodDecl != null && DafnyOptions.O.EnforcePrintEffects) { |
| 33 | + f.ByMethodDecl.Body.Body.Iter(stmt => CheckNoPrintEffects(stmt, f.ByMethodDecl)); |
| 34 | + } |
| 35 | + } else if (member is Method method) { |
| 36 | + if (hasPrintAttribute) { |
| 37 | + if (member.IsGhost) { |
| 38 | + Reporter.Error(MessageSource.Rewriter, member.tok, ":print attribute is not allowed on ghost methods"); |
| 39 | + } else if (method.OverriddenMethod != null && !HasPrintAttribute(method.OverriddenMethod.Attributes, false)) { |
| 40 | + Reporter.Error(MessageSource.Rewriter, member.tok, |
| 41 | + "not allowed to override a non-printing method with a possibly printing method ('{0}')", method.Name); |
| 42 | + } |
| 43 | + } else if (!member.IsGhost && method.Body != null) { |
| 44 | + if (DafnyOptions.O.EnforcePrintEffects) { |
| 45 | + method.Body.Body.Iter(stmt => CheckNoPrintEffects(stmt, method)); |
| 46 | + } |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + bool HasPrintAttribute(Attributes attrs, bool checkParameters = true) { |
| 55 | + var printAttribute = Attributes.Find(attrs, "print"); |
| 56 | + if (checkParameters && printAttribute != null && printAttribute.Args.Count != 0) { |
| 57 | + Reporter.Error(MessageSource.Rewriter, printAttribute.Args[0].tok, ":print attribute does not take any arguments"); |
| 58 | + } |
| 59 | + return printAttribute != null; |
| 60 | + } |
| 61 | + |
| 62 | + private void CheckNoPrintEffects(Statement stmt, IMethodCodeContext codeContext) { |
| 63 | + if (stmt is PrintStmt) { |
| 64 | + var method = codeContext as Method; |
| 65 | + if (method != null && method.IsByMethod) { |
| 66 | + Reporter.Error(MessageSource.Rewriter, stmt.Tok, "a function-by-method is not allowed to use print statements"); |
| 67 | + } else { |
| 68 | + Reporter.Error(MessageSource.Rewriter, stmt.Tok, |
| 69 | + "to use a print statement, the enclosing {0} must be marked with {{:print}}", method?.WhatKind ?? ((IteratorDecl)codeContext).WhatKind); |
| 70 | + } |
| 71 | + } else if (stmt is CallStmt call) { |
| 72 | + if (HasPrintAttribute(call.Method.Attributes, false)) { |
| 73 | + var method = codeContext as Method; |
| 74 | + if (method != null && method.IsByMethod) { |
| 75 | + Reporter.Error(MessageSource.Rewriter, stmt.Tok, "a function-by-method is not allowed to call a method with print effects"); |
| 76 | + } else { |
| 77 | + Reporter.Error(MessageSource.Rewriter, stmt.Tok, |
| 78 | + "to call a method with print effects, the enclosing {0} must be marked with {{:print}}", |
| 79 | + method?.WhatKind ?? ((IteratorDecl)codeContext).WhatKind); |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + stmt.SubStatements.Iter(s => CheckNoPrintEffects(s, codeContext)); |
| 84 | + } |
| 85 | + } |
| 86 | +} |
0 commit comments