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