Skip to content

Commit 9da3833

Browse files
authored
Merge pull request #764 from viperproject/sem-highlight
Add information required for LSP features to Parse AST
2 parents 399835d + 9f67812 commit 9da3833

75 files changed

Lines changed: 3697 additions & 2979 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/scala/viper/silver/ast/Position.scala

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ import java.nio.file.Path
1212
import viper.silver.parser.FastParser
1313

1414
/** A trait describing the position of occurrence of an AST node. */
15-
sealed trait Position
15+
sealed trait Position {
16+
def deltaColumn(delta: Int): Position
17+
}
1618

1719
/** Describes ''no location'' for cases where a `Position` is expected, but not available. */
1820
case object NoPosition extends Position {
21+
override def deltaColumn(delta: Int) = this
1922
override def toString = "<no position>"
2023
}
2124

@@ -25,6 +28,13 @@ trait HasLineColumn extends Position {
2528

2629
def column: Int
2730

31+
def <=(other: HasLineColumn): Boolean = {
32+
if (line < other.line) true
33+
else if (line > other.line) false
34+
else column <= other.column
35+
}
36+
37+
def deltaColumn(delta: Int): HasLineColumn
2838
override def toString = s"$line.$column"
2939
}
3040

@@ -35,7 +45,9 @@ trait HasIdentifier extends HasLineColumn {
3545
def id: String
3646
}
3747

38-
case class LineColumnPosition(line: Int, column: Int) extends HasLineColumn
48+
case class LineColumnPosition(line: Int, column: Int) extends HasLineColumn {
49+
override def deltaColumn(delta: Int) = LineColumnPosition(line, column + delta)
50+
}
3951

4052
/** Represents a source code position by referencing a file, a line and a column.
4153
* Optionally, an additional end position can be specified.
@@ -57,6 +69,7 @@ trait AbstractSourcePosition extends HasLineColumn {
5769
private def lineColComponent(lc_pos: HasLineColumn) =
5870
s"${lc_pos.line}.${lc_pos.column}"
5971

72+
override def deltaColumn(delta: Int): AbstractSourcePosition
6073
override def toString: String = end match {
6174
case None =>
6275
s"$fileComponent${lineColComponent(start)}"
@@ -79,12 +92,16 @@ object AbstractSourcePosition {
7992
class SourcePosition(val file: Path, val start: HasLineColumn, val end: Option[HasLineColumn])
8093
extends AbstractSourcePosition with StructuralEquality {
8194

95+
override def deltaColumn(delta: Int): SourcePosition =
96+
new SourcePosition(file, start.deltaColumn(delta), end.map(_.deltaColumn(delta)))
8297
protected val equalityDefiningMembers: Seq[Object] =
8398
file :: start :: end :: Nil
8499
}
85100

86101
class IdentifierPosition(val file: Path, val start: HasLineColumn, val end: Option[HasLineColumn], val id: String)
87102
extends AbstractSourcePosition with StructuralEquality with HasIdentifier {
103+
override def deltaColumn(delta: Int): IdentifierPosition =
104+
new IdentifierPosition(file, start.deltaColumn(delta), end.map(_.deltaColumn(delta)), id)
88105
protected val equalityDefiningMembers: Seq[Object] =
89106
file :: start :: end :: id :: Nil
90107
}
@@ -122,13 +139,19 @@ case class TranslatedPosition(pos: AbstractSourcePosition) extends AbstractSourc
122139
val file: Path = pos.file
123140
val start: HasLineColumn = pos.start
124141
val end: Option[HasLineColumn] = pos.end
142+
override def deltaColumn(delta: Int): TranslatedPosition =
143+
new TranslatedPosition(pos.deltaColumn(delta))
125144
}
126145

127146
case class FilePosition(file: Path, vline: Int, col: Int) extends util.parsing.input.Position with HasLineColumn {
128147
override lazy val line: Int = vline
129148
override lazy val column: Int = col
130149
override lazy val lineContents: String = toString
131150
override lazy val toString: String = s"${file.getFileName}@$vline.$col"
151+
override def deltaColumn(delta: Int): FilePosition = FilePosition(file, vline, col + delta)
152+
}
153+
object FilePosition {
154+
def apply(pos: (Int, Int))(implicit file: Path): FilePosition = FilePosition(file, pos._1, pos._2)
132155
}
133156

134157
/**
@@ -138,4 +161,5 @@ case class FilePosition(file: Path, vline: Int, col: Int) extends util.parsing.i
138161
*/
139162
case class VirtualPosition(identifier: String) extends Position {
140163
override def toString: String = identifier
164+
override def deltaColumn(delta: Int): VirtualPosition = this
141165
}

src/main/scala/viper/silver/ast/Program.scala

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -214,18 +214,20 @@ case class Program(domains: Seq[Domain], fields: Seq[Field], functions: Seq[Func
214214
}
215215

216216
def checkNamesInScope(currentScope: Scope, dMap: immutable.HashMap[String, Declaration]) : Seq[ConsistencyError] = {
217-
var declarationMap = dMap
217+
var newMap = immutable.HashMap.empty[String, Declaration]
218218
var s: Seq[ConsistencyError] = Seq.empty[ConsistencyError]
219219
//check name declarations
220220
currentScope.scopedDecls.foreach(l=> {
221221
if(!Consistency.validUserDefinedIdentifier(l.name))
222222
s :+= ConsistencyError(s"${l.name} is not a valid identifier.", l.pos)
223223

224-
declarationMap.get(l.name) match {
225-
case Some(_: Declaration) => s :+= ConsistencyError(s"Duplicate identifier ${l.name} found.", l.pos)
226-
case None => declarationMap += (l.name -> l)
224+
newMap.get(l.name) match {
225+
case Some(_: Declaration) => s :+= ConsistencyError(s"Duplicate identifier `${l.name}` found.", l.pos)
226+
case None => newMap += (l.name -> l)
227227
}
228228
})
229+
// Override duplicate keys in old map
230+
val declarationMap = dMap ++ newMap
229231

230232
//check name uses
231233
Visitor.visitOpt(currentScope.asInstanceOf[Node], Nodes.subnodes){ n => {
@@ -268,7 +270,7 @@ case class Program(domains: Seq[Domain], fields: Seq[Field], functions: Seq[Func
268270

269271
lazy val groundTypeInstances = DomainInstances.findNecessaryTypeInstances(this)
270272

271-
lazy val members: Seq[Member with Serializable] = domains ++ fields ++ functions ++ predicates ++ methods
273+
val members: Seq[Member with Serializable] = domains ++ fields ++ functions ++ predicates ++ methods ++ extensions
272274

273275
def findFieldOptionally(name: String): Option[Field] = this.fieldsByName.get(name)
274276

@@ -409,11 +411,13 @@ case class Method(name: String, formalArgs: Seq[LocalVarDecl], formalReturns: Se
409411
}
410412

411413
def deepCollectInBody[A](f: PartialFunction[Node, A]): Seq[A] = body match {
414+
case null => Nil
415+
case None => Nil
412416
case Some(actualBody) => actualBody.deepCollect(f)
413-
case None => Seq()
414417
}
415418

416-
val scopedDecls: Seq[Declaration] = formalArgs ++ formalReturns
419+
def labels: Seq[Label] = deepCollectInBody { case l: Label => l }
420+
val scopedDecls: Seq[Declaration] = formalArgs ++ formalReturns ++ labels
417421

418422
override lazy val check: Seq[ConsistencyError] =
419423
pres.flatMap(Consistency.checkPre) ++
@@ -444,19 +448,6 @@ case class Method(name: String, formalArgs: Seq[LocalVarDecl], formalReturns: Se
444448
def toCfg(simplify: Boolean = true, detect: Boolean = true): SilverCfg = CfgGenerator.methodToCfg(this, simplify, detect)
445449
}
446450

447-
object MethodWithLabelsInScope {
448-
def apply(name: String, formalArgs: Seq[LocalVarDecl], formalReturns: Seq[LocalVarDecl], pres: Seq[Exp], posts: Seq[Exp], body: Option[Seqn])
449-
(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): Method = {
450-
val newBody = body match {
451-
case Some(actualBody) =>
452-
val newScopedDecls = actualBody.scopedSeqnDeclarations ++ actualBody.deepCollect({case l: Label => l})
453-
Some(actualBody.copy(scopedSeqnDeclarations = newScopedDecls)(actualBody.pos, actualBody.info, actualBody.errT))
454-
case _ => body
455-
}
456-
Method(name, formalArgs, formalReturns, pres, posts, newBody)(pos, info, errT)
457-
}
458-
}
459-
460451
/** A function declaration */
461452
case class Function(name: String, formalArgs: Seq[LocalVarDecl], typ: Type, pres: Seq[Exp], posts: Seq[Exp], body: Option[Exp])(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends Member with FuncLike with Contracted {
462453
override lazy val check : Seq[ConsistencyError] =
@@ -826,7 +817,7 @@ object BackendFunc {
826817
/**
827818
* The Extension Member trait provides the way to expand the Ast to include new Top Level declarations
828819
*/
829-
trait ExtensionMember extends Member{
820+
trait ExtensionMember extends Member with Serializable {
830821
def extensionSubnodes: Seq[Node]
831822
def prettyPrint: PrettyPrintPrimitives#Cont
832823
}

src/main/scala/viper/silver/ast/utility/Consistency.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ object Consistency {
2929
recordIfNot(suspect, !property, message)
3030

3131
/** Names that are not allowed for use in programs. */
32-
def reservedNames: Set[String] = FastParserCompanion.basicKeywords
32+
def reservedNames: Set[String] = FastParserCompanion.basicKeywords.map(_.keyword)
3333

3434
/** Returns true iff the string `name` is a valid identifier. */
3535
val identFirstLetter = "[a-zA-Z$_]"
@@ -171,7 +171,7 @@ object Consistency {
171171
for (c@MethodCall(_, _, targets) <- b; t <- targets if argVars.contains(t)) {
172172
s :+= ConsistencyError(s"$c is a reassignment of formal argument $t.", c.pos)
173173
}
174-
for (n@NewStmt(l, _) <- b if argVars.contains(l)){
174+
for (n@NewStmt(l, _) <- b if argVars.contains(l)) {
175175
s :+= ConsistencyError(s"$n is a reassignment of formal argument $l.", n.pos)
176176
}
177177
s
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package viper.silver.ast.utility
2+
3+
import java.nio.file.Path
4+
import scala.io.Source
5+
import scala.util.{Using, Try}
6+
7+
trait FileLoader {
8+
def loadContent(path: Path): Try[String]
9+
}
10+
11+
trait DiskLoader extends FileLoader {
12+
override def loadContent(path: Path): Try[String] = {
13+
Using(Source.fromFile(path.toFile()))(_.mkString)
14+
}
15+
}
16+
17+
object DiskLoader extends DiskLoader

src/main/scala/viper/silver/ast/utility/QuantifiedPermissions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ object QuantifiedPermissions {
141141
val currentRoot = toVisit.dequeue()
142142

143143
val relevantNodes: Seq[Node] = currentRoot match {
144-
case m@Method(_, _, _, pres, posts, _) if m != root =>
144+
case m: Method if m != root =>
145145
// use only specification of called methods
146-
pres ++ posts
146+
m.pres ++ m.posts
147147
case f@Function(_, _, _, pres, posts, _) if f != root =>
148148
// use only specification of called functions
149149
pres ++ posts

src/main/scala/viper/silver/ast/utility/rewriter/Rewritable.scala

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@
66

77
package viper.silver.ast.utility.rewriter
88

9-
import viper.silver.parser.{PDomain, PDomainFunction, PFields, PFunction, PMethod, PPredicate}
10-
import viper.silver.parser.Transformer.ParseTreeDuplicationError
9+
import viper.silver.parser.PNode
1110
import viper.silver.ast.{AtomicType, BackendFuncApp, DomainFuncApp, ErrorTrafo, FuncApp, Info, Node, Position}
1211

1312
import scala.reflect.runtime.{universe => reflection}
1413

14+
trait HasExtraValList {
15+
def getExtraVals: Seq[Any]
16+
}
17+
trait HasExtraVars {
18+
def copyExtraVars(from: Any): Unit
19+
}
20+
1521
/**
1622
* Trait Rewritable provides an interface that specifies which methods are required for the rewriter to work with.
1723
* For classes that implement product (especially case classes) everything is already implemented here and one only has to extend this base class
@@ -37,27 +43,26 @@ trait Rewritable extends Product {
3743
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
3844

3945
// Add additional arguments to constructor call, besides children
40-
var firstArgList = children
46+
val firstArgList = children
4147
var secondArgList = Seq.empty[Any]
4248
import viper.silver.ast.{DomainType, DomainAxiom, FuncApp, DomainFunc, DomainFuncApp}
43-
import viper.silver.parser.{PAxiom, PMagicWandExp, PNode, PDomainType}
4449
this match {
50+
// TODO: remove the following cases by implementing `HasExtraValList` on the respective classes
4551
case dt: DomainType => secondArgList = Seq(dt.typeParameters)
4652
case da: DomainAxiom => secondArgList = Seq(da.pos, da.info, da.domainName, da.errT)
4753
case fa: FuncApp => secondArgList = Seq(fa.pos, fa.info, fa.typ, fa.errT)
4854
case df: DomainFunc => secondArgList = Seq(df.pos, df.info, df.domainName, df.errT)
4955
case df: DomainFuncApp => secondArgList = Seq(df.pos, df.info, df.typ, df.domainName, df.errT)
5056
case ba: BackendFuncApp => secondArgList = Seq(ba.pos, ba.info, ba.typ, ba.interpretation, ba.errT)
5157
case no: Node => secondArgList = no.getMetadata
52-
case pa: PAxiom => secondArgList = Seq(pa.domainName) ++ Seq(pos.getOrElse(pa.pos), pa.annotations)
53-
case pm: PMagicWandExp => firstArgList = Seq(children.head) ++ children.drop(2) ++ Seq(pos.getOrElse(pm.pos))
54-
case pd: PDomainFunction => secondArgList = Seq(pd.domainName) ++ Seq(pos.getOrElse(pd.pos), pd.annotations)
55-
case pd: PDomain => secondArgList = Seq(pos.getOrElse(pd.pos), pd.annotations)
56-
case pm: PMethod => secondArgList = Seq(pos.getOrElse(pm.pos), pm.annotations)
57-
case pp: PPredicate => secondArgList = Seq(pos.getOrElse(pp.pos), pp.annotations)
58-
case pf: PFunction => secondArgList = Seq(pos.getOrElse(pf.pos), pf.annotations)
59-
case pf: PFields => secondArgList = Seq(pos.getOrElse(pf.pos), pf.annotations)
60-
case pn: PNode => secondArgList = Seq(pos.getOrElse(pn.pos))
58+
59+
case evl: HasExtraValList => {
60+
secondArgList = evl.getExtraVals.map(ev => {
61+
// Replace positions with the new one
62+
val replace = pos.isDefined && ev.isInstanceOf[(_, _)] && ev.asInstanceOf[(_, _)]._1.isInstanceOf[Position] && ev.asInstanceOf[(_, _)]._2.isInstanceOf[Position]
63+
if (replace) pos.get else ev
64+
})
65+
}
6166
case _ =>
6267
}
6368

@@ -69,9 +74,9 @@ trait Rewritable extends Product {
6974
}
7075

7176
// Copy member values, as they aren't in the parameters' list.
72-
this match {
73-
case dt: PDomainType =>
74-
newNode.asInstanceOf[PDomainType].kind = dt.kind
77+
newNode match {
78+
case ev: HasExtraVars =>
79+
ev.copyExtraVars(this)
7580
case _ =>
7681
}
7782

@@ -116,4 +121,9 @@ trait Rewritable extends Product {
116121
newNode.asInstanceOf[this.type]
117122
}
118123
}
124+
125+
def initProperties(): Unit = ()
119126
}
127+
128+
case class ParseTreeDuplicationError(original: PNode, newChildren: Seq[Any])
129+
extends RuntimeException(s"Cannot duplicate $original with new children $newChildren")

src/main/scala/viper/silver/ast/utility/rewriter/Strategy.scala

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ class Strategy[N <: Rewritable : reflection.TypeTag : scala.reflect.ClassTag, C
356356
case Traverse.Innermost => rewriteInnermost(node, contextUsed)
357357
}
358358
changed = !(result eq node)
359+
result.initProperties()
359360
result
360361
}
361362

@@ -424,6 +425,8 @@ class Strategy[N <: Rewritable : reflection.TypeTag : scala.reflect.ClassTag, C
424425
rewriteTopDown(t5, context)).asInstanceOf[A]
425426

426427
case Some(value) => Some(rewriteTopDown(value, context)).asInstanceOf[A]
428+
case Left(value) => Left(rewriteTopDown(value, context)).asInstanceOf[A]
429+
case Right(value) => Right(rewriteTopDown(value, context)).asInstanceOf[A]
427430

428431
case node: N @unchecked =>
429432
// Rewrite node and context
@@ -477,6 +480,8 @@ class Strategy[N <: Rewritable : reflection.TypeTag : scala.reflect.ClassTag, C
477480
rewriteBottomUp(t5, context)).asInstanceOf[A]
478481

479482
case Some(value) => Some(rewriteBottomUp(value, context)).asInstanceOf[A]
483+
case Left(value) => Left(rewriteBottomUp(value, context)).asInstanceOf[A]
484+
case Right(value) => Right(rewriteBottomUp(value, context)).asInstanceOf[A]
480485

481486
case node: N @unchecked =>
482487
val c = context.addAncestor(node).asInstanceOf[C]
@@ -528,6 +533,8 @@ class Strategy[N <: Rewritable : reflection.TypeTag : scala.reflect.ClassTag, C
528533
rewriteInnermost(t5, context)).asInstanceOf[A]
529534

530535
case Some(value) => Some(rewriteInnermost(value, context)).asInstanceOf[A]
536+
case Left(value) => Left(rewriteInnermost(value, context)).asInstanceOf[A]
537+
case Right(value) => Right(rewriteInnermost(value, context)).asInstanceOf[A]
531538

532539
case node: N @unchecked =>
533540
// Rewrite node and context
@@ -595,12 +602,14 @@ class RepeatedStrategy[N <: Rewritable](s: StrategyInterface[N]) extends Strateg
595602
* @return rewritten root
596603
*/
597604
override def execute[T <: N](node: N): T = {
598-
val result: T = s.execute[T](node)
599-
if (!s.hasChanged) {
600-
result
601-
} else {
602-
execute[T](result)
605+
var result: T = s.execute[T](node)
606+
var j = 1
607+
while (s.hasChanged) {
608+
result = s.execute[T](result)
609+
j += 1
610+
assert(j < 10000, "Infinite loop detected")
603611
}
612+
result
604613
}
605614

606615
/**
@@ -616,12 +625,13 @@ class RepeatedStrategy[N <: Rewritable](s: StrategyInterface[N]) extends Strateg
616625
node
617626
}
618627
else {
619-
val result = s.execute[T](node)
620-
if (s.hasChanged) {
621-
result
622-
} else {
623-
execute[T](result, i - 1)
628+
var result: T = s.execute[T](node)
629+
var j = 1
630+
while (s.hasChanged && j < i) {
631+
result = s.execute[T](result)
632+
j += 1
624633
}
634+
result
625635
}
626636
}
627637

0 commit comments

Comments
 (0)