1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ //
5
+ // Copyright (c) 2011-2024 ETH Zurich.
6
+
7
+ import org .scalatest .funsuite .AnyFunSuite
8
+ import viper .silver .ast .Program
9
+ import viper .silver .frontend ._
10
+ import viper .silver .logger .ViperStdOutLogger
11
+ import viper .silver .reporter .StdIOReporter
12
+ import viper .silver .parser .{PProgram , PAnnotatedExp , PWhile }
13
+
14
+ class DocAnnotationTests extends AnyFunSuite {
15
+ object AstProvider extends ViperAstProvider (StdIOReporter (), ViperStdOutLogger (" DocAnnotationTestsLogger" ).get) {
16
+ def setCode (code : String ): Unit = {
17
+ _input = Some (code)
18
+ }
19
+
20
+ override def reset (input : java.nio.file.Path ): Unit = {
21
+ if (state < DefaultStates .Initialized ) sys.error(" The translator has not been initialized." )
22
+ _state = DefaultStates .InputSet
23
+ _inputFile = Some (input)
24
+ /** must be set by [[setCode ]] */
25
+ // _input = None
26
+ _errors = Seq ()
27
+ _parsingResult = None
28
+ _semanticAnalysisResult = None
29
+ _verificationResult = None
30
+ _program = None
31
+ resetMessages()
32
+ }
33
+ }
34
+
35
+ def generatePAstAndAst (code : String ): Option [(PProgram , Program )] = {
36
+ val code_id = code.hashCode.asInstanceOf [Short ].toString
37
+ AstProvider .setCode(code)
38
+ AstProvider .execute(Seq (" --ignoreFile" , code_id))
39
+
40
+ if (AstProvider .errors.isEmpty) {
41
+ Some (AstProvider .parsingResult, AstProvider .translationResult)
42
+ } else {
43
+ AstProvider .logger.error(s " An error occurred while translating ${AstProvider .errors}" )
44
+ None
45
+ }
46
+ }
47
+
48
+ test(" Basic parsing of documentation" ) {
49
+ import viper .silver .ast ._
50
+
51
+ val code =
52
+ """ /// a field
53
+ |field f: Int
54
+ |/// P is a predicate
55
+ |predicate P(x: Int)
56
+ |
57
+ |/// a function
58
+ |function fun(x: Int): Int {
59
+ | (x / 1 == x) ? 42 : 0
60
+ |}
61
+ |/// a second function
62
+ |function fun2(x: Int): Int
63
+ | /// precondition
64
+ | requires x > 0
65
+ | /// post-
66
+ | /// condition
67
+ | ensures result > 0
68
+ |
69
+ |/// very important domain
70
+ |domain ImportantType {
71
+ |
72
+ | /// this function
73
+ | /// is crucial
74
+ | function phi(ImportantType): Int
75
+ |
76
+ | /// the only axiom
77
+ | axiom {
78
+ | /// documenting an expression
79
+ | true
80
+ | }
81
+ |}
82
+ |
83
+ |/// a macro
84
+ |define plus(a, b) (a+b)
85
+ |
86
+ |/// this is a method
87
+ |/// it does something
88
+ |method m(x: Ref, y: Ref)
89
+ | /// this documents the first precondition
90
+ | requires acc(x.f)
91
+ | /// documentation of the second precondition
92
+ | requires acc(y.f)
93
+ |{
94
+ | var local: Bool
95
+ |
96
+ | while (true)///the invariant
97
+ | /// is always true
98
+ | invariant true
99
+ | /// termination
100
+ | decreases x.f
101
+ | {}
102
+ |
103
+ |}
104
+ |""" .stripMargin
105
+
106
+ val pAst : PProgram = generatePAstAndAst(code).get._1
107
+
108
+ val fieldAnn = pAst.fields.head.annotations.head.values.inner.first.get.str
109
+ assert(fieldAnn == " a field" )
110
+
111
+ val predicateAnnotation = pAst.predicates.head.annotations.head.values.inner.first.get.str
112
+ assert(predicateAnnotation == " P is a predicate" )
113
+
114
+ val functionAnnotation = pAst.functions.head.annotations.head.values.inner.first.get.str
115
+ assert(functionAnnotation == " a function" )
116
+
117
+ val fun2Annotation = pAst.functions(1 ).annotations.head.values.inner.first.get.str
118
+ val fun2PreAnnotations = pAst.functions(1 ).pres.head.annotations.map(_.values.inner.first.get.str)
119
+ val fun2PostAnnotations = pAst.functions(1 ).posts.head.annotations.map(_.values.inner.first.get.str)
120
+ assert(fun2Annotation == " a second function" )
121
+ assert(fun2PreAnnotations == Seq (" precondition" ))
122
+ assert(fun2PostAnnotations == Seq (" post-" , " condition" ))
123
+
124
+ val domainAnnotation = pAst.domains.head.annotations.head.values.inner.first.get.str
125
+ assert(domainAnnotation == " very important domain" )
126
+
127
+ val domainFunctionAnnotations = pAst.domains.head.members.inner.funcs.head.annotations.map(_.values.inner.first.get.str)
128
+ assert(domainFunctionAnnotations == Seq (" this function" , " is crucial" ))
129
+
130
+ val axiomAnnotations = pAst.domains.head.members.inner.axioms.head.annotations.map(_.values.inner.first.get.str)
131
+ assert(axiomAnnotations == Seq (" the only axiom" ))
132
+ val axiomExpAnnotations = pAst.domains.head.members.inner.axioms.head.exp.e.inner.asInstanceOf [PAnnotatedExp ].annotation.values.inner.first.get.str
133
+ assert(axiomExpAnnotations == " documenting an expression" )
134
+
135
+ val macroAnnotation = pAst.macros.head.annotations.head.values.inner.first.get.str
136
+ assert(macroAnnotation == " a macro" )
137
+
138
+ val methodAnnotations = pAst.methods.head.annotations.map(_.values.inner.first.get.str)
139
+ assert(methodAnnotations == Seq (" this is a method" , " it does something" ))
140
+
141
+ val methodPreAnnotations = pAst.methods.head.pres.toSeq.map(_.annotations.head.values.inner.first.get.str)
142
+ assert(methodPreAnnotations == Seq (" this documents the first precondition" , " documentation of the second precondition" ))
143
+
144
+ val loopInvariantAnnotations = pAst.methods.head.body.get.ss.inner.inner.collect {
145
+ case (_, w : PWhile ) => w.invs.toSeq.flatMap(_.annotations.map(_.values.inner.first.get.str))
146
+ }.flatten
147
+ assert(loopInvariantAnnotations == Seq (" the invariant" , " is always true" , " termination" ))
148
+ }
149
+ }
0 commit comments