/// <summary> /// Returns true if the given class declaration is a P# machine state. /// </summary> /// <param name="compilation">Compilation</param> /// <param name="classDecl">Class declaration</param> /// <returns>Boolean value</returns> internal static bool IsMachineState(CodeAnalysis.Compilation compilation, ClassDeclarationSyntax classDecl) { var result = false; if (classDecl.BaseList == null) { return(result); } var model = compilation.GetSemanticModel(classDecl.SyntaxTree); var symbol = model.GetDeclaredSymbol(classDecl); while (true) { if (symbol.ToString() == typeof(MachineState).FullName) { result = true; break; } else if (symbol.BaseType != null) { symbol = symbol.BaseType; continue; } break; } return(result); }
/// <summary> /// Checks for multiple handlers for the same event. /// </summary> /// <param name="state">State</param> /// <param name="compilation">Compilation</param> private void CheckForMultipleSameEventHandlers(ClassDeclarationSyntax state, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(state.SyntaxTree); var eventHandlers = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventGotoState") || model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventPushState") || model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventDoAction") || model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.IgnoreEvents") || model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.DeferEvents")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count > 0). Where(val => val.ArgumentList.Arguments[0].Expression is TypeOfExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as TypeOfExpressionSyntax). Select(val => val.Type as IdentifierNameSyntax). Select(val => val.Identifier.ValueText). ToList(); var eventOccurrences = eventHandlers.GroupBy(val => val); foreach (var e in eventOccurrences.Where(val => val.Count() > 1)) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot declare more than one handler for event '" + e.Key + "'.")); } }
/// <summary> /// Checks that a state does not have a duplicate liveness attribute. /// </summary> /// <param name="state">State</param> /// <param name="compilation">Compilation</param> private void CheckForDuplicateLivenessAttributes(ClassDeclarationSyntax state, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(state.SyntaxTree); var hotAttributes = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.Hot")). ToList(); var coldAttributes = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.Cold")). ToList(); if (hotAttributes.Count > 0 && coldAttributes.Count > 0) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "A monitor state cannot declare both " + "hot and cold liveness attributes.")); } }
/// <summary> /// Checks that a state does not have a duplicate exit action. /// </summary> /// <param name="state">State</param> /// <param name="compilation">Compilation</param> private void CheckForDuplicateOnExit(ClassDeclarationSyntax state, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(state.SyntaxTree); var onExitAttribute = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnExit")). FirstOrDefault(); var onExitMethod = state.DescendantNodes().OfType <MethodDeclarationSyntax>(). Where(val => val.Modifiers.Any(SyntaxKind.OverrideKeyword)). Where(val => val.Identifier.ValueText.Equals("OnExit")). FirstOrDefault(); if (onExitAttribute != null && onExitMethod != null) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot have two exit actions.")); } }
/// <summary> /// Checks that a machine has an start state. /// </summary> /// <param name="machine">Machine</param> /// <param name="compilation">Compilation</param> private void CheckForStartState(ClassDeclarationSyntax machine, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(machine.SyntaxTree); var stateAttributes = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.Start")). ToList(); if (stateAttributes.Count == 0) { base.ErrorLog.Add(Tuple.Create(machine.Identifier, this.GetTypeOfMachine().ToLower() + " '" + machine.Identifier.ValueText + "' must declare a start state.")); } else if (stateAttributes.Count > 1) { base.ErrorLog.Add(Tuple.Create(machine.Identifier, this.GetTypeOfMachine().ToLower() + " '" + machine.Identifier.ValueText + "' must declare only one start state.")); } }
/// <summary> /// Checks that a machine has an start state. /// </summary> private void CheckForStartState(ClassDeclarationSyntax machine, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(machine.SyntaxTree); var stateAttributes = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.Start")). ToList(); if (stateAttributes.Count == 0) { this.WarningLog.Add(Tuple.Create(machine.Identifier, $"{this.GetTypeOfMachine().ToLower()} " + $"'{machine.Identifier.ValueText}' must declare a start state (unless the machine " + "inherits a start state from a base machine, or is partial, and one state has been " + "already declared in another part of the declaration).")); } else if (stateAttributes.Count > 1) { this.ErrorLog.Add(Tuple.Create(machine.Identifier, $"{this.GetTypeOfMachine().ToLower()} " + $"'{machine.Identifier.ValueText}' must declare only one start state.")); } }
/// <summary> /// Discovers the available actions of the given machine. /// </summary> /// <param name="machine">Machine</param> /// <param name="compilation">Compilation</param> private void DiscoverMachineActions(ClassDeclarationSyntax machine, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(machine.SyntaxTree); var onEntryActionNames = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEntry")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is LiteralExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as LiteralExpressionSyntax). Select(val => val.Token.ValueText). ToList(); var onEntryNameOfActionNames = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEntry")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as InvocationExpressionSyntax). Where(val => val.Expression is IdentifierNameSyntax). Where(val => (val.Expression as IdentifierNameSyntax).Identifier.ValueText.Equals("nameof")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is IdentifierNameSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as IdentifierNameSyntax). Select(val => val.Identifier.ValueText). ToList(); var onExitActionNames = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnExit")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is LiteralExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as LiteralExpressionSyntax). Select(val => val.Token.ValueText). ToList(); var onExitNameOfActionNames = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnExit")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as InvocationExpressionSyntax). Where(val => val.Expression is IdentifierNameSyntax). Where(val => (val.Expression as IdentifierNameSyntax).Identifier.ValueText.Equals("nameof")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is IdentifierNameSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as IdentifierNameSyntax). Select(val => val.Identifier.ValueText). ToList(); var onEventDoActionNames = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventDoAction")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 2). Where(val => val.ArgumentList.Arguments[1].Expression is LiteralExpressionSyntax). Select(val => val.ArgumentList.Arguments[1].Expression as LiteralExpressionSyntax). Select(val => val.Token.ValueText). ToList(); var onEventDoNameOfActionNames = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). SelectMany(val => val.AttributeLists). SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventDoAction")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 2). Where(val => val.ArgumentList.Arguments[1].Expression is InvocationExpressionSyntax). Select(val => val.ArgumentList.Arguments[1].Expression as InvocationExpressionSyntax). Where(val => val.Expression is IdentifierNameSyntax). Where(val => (val.Expression as IdentifierNameSyntax).Identifier.ValueText.Equals("nameof")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count == 1). Where(val => val.ArgumentList.Arguments[0].Expression is IdentifierNameSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as IdentifierNameSyntax). Select(val => val.Identifier.ValueText). ToList(); var actionNames = new HashSet <string>(); actionNames.UnionWith(onEntryActionNames); actionNames.UnionWith(onEntryNameOfActionNames); actionNames.UnionWith(onExitActionNames); actionNames.UnionWith(onExitNameOfActionNames); actionNames.UnionWith(onEventDoActionNames); actionNames.UnionWith(onEventDoNameOfActionNames); this.Actions.Add(machine, new List <MethodDeclarationSyntax>()); foreach (var actionName in actionNames) { var action = machine.DescendantNodes().OfType <MethodDeclarationSyntax>(). Where(val => val.ParameterList != null). Where(val => val.ParameterList.Parameters.Count == 0). Where(val => val.Identifier.ValueText.Equals(actionName)). FirstOrDefault(); if (action != null) { this.Actions[machine].Add(action); } } var states = machine.DescendantNodes().OfType <ClassDeclarationSyntax>(). Where(val => this.IsState(compilation, val)). ToList(); var stateMethods = states.SelectMany(val => val.DescendantNodes().OfType <MethodDeclarationSyntax>()). Where(val => val.Modifiers.Any(SyntaxKind.OverrideKeyword)). ToList(); foreach (var method in stateMethods) { this.Actions[machine].Add(method); } }
/// <summary> /// Checks for correct wildcard usage. /// If "defer *" then: /// no other event should be deferred. /// If "ignore *" or "on * do action" then: /// no other action or ignore should be defined. /// If "On * goto" or "On * push" then: /// no other transition, action or ignore should be defined. /// </summary> /// <param name="state">State</param> /// <param name="compilation">Compilation</param> private void CheckForCorrectWildcardUse(ClassDeclarationSyntax state, CodeAnalysis.Compilation compilation) { var model = compilation.GetSemanticModel(state.SyntaxTree); var ignoreTypes = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.IgnoreEvents")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count > 0). SelectMany(val => val.ArgumentList.Arguments). Where(val => val.Expression is TypeOfExpressionSyntax). Select(val => val.Expression as TypeOfExpressionSyntax); var deferTypes = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.DeferEvents")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count > 0). SelectMany(val => val.ArgumentList.Arguments). Where(val => val.Expression is TypeOfExpressionSyntax). Select(val => val.Expression as TypeOfExpressionSyntax); var actionTypes = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventDoAction")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count > 0). Where(val => val.ArgumentList.Arguments[0].Expression is TypeOfExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as TypeOfExpressionSyntax); var transitionTypes = state.AttributeLists. SelectMany(val => val.Attributes). Where(val => model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventGotoState") || model.GetTypeInfo(val).Type.ToDisplayString().Equals("Microsoft.PSharp.OnEventPushState")). Where(val => val.ArgumentList != null). Where(val => val.ArgumentList.Arguments.Count > 0). Where(val => val.ArgumentList.Arguments[0].Expression is TypeOfExpressionSyntax). Select(val => val.ArgumentList.Arguments[0].Expression as TypeOfExpressionSyntax); var convertToStringSet = new Func <IEnumerable <TypeOfExpressionSyntax>, HashSet <string> >(ls => { var eventHandlers = new HashSet <string>(ls. Where(val => val.Type is IdentifierNameSyntax). Select(val => val.Type as IdentifierNameSyntax). Select(val => val.ToFullString())); eventHandlers.UnionWith(ls. Where(val => val.Type is QualifiedNameSyntax). Select(val => val.Type as QualifiedNameSyntax). Select(val => val.ToFullString())); return(eventHandlers); }); var ignoredEvents = convertToStringSet(ignoreTypes); var deferredEvents = convertToStringSet(deferTypes); var actionEvents = convertToStringSet(actionTypes); var transitionEvents = convertToStringSet(transitionTypes); var isWildCard = new Func <string, bool>(s => s == "WildCardEvent" || s == "Microsoft.PSharp.WildCardEvent"); var hasWildCard = new Func <HashSet <string>, bool>(set => set.Contains("WildCardEvent") || set.Contains("Microsoft.PSharp.WildCardEvent")); if (hasWildCard(deferredEvents)) { foreach (var e in deferredEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot defer other event '" + e + "' when deferring the wildcard event.")); } } if (hasWildCard(ignoredEvents)) { foreach (var e in ignoredEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot ignore other event '" + e + "' when ignoring the wildcard event.")); } foreach (var e in actionEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot define action on '" + e + "' when ignoring the wildcard event.")); } } if (hasWildCard(actionEvents)) { foreach (var e in ignoredEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot ignore other event '" + e + "' when defining an action on the wildcard event.")); } foreach (var e in actionEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot define action on '" + e + "' when defining an action on the wildcard event.")); } } if (hasWildCard(transitionEvents)) { foreach (var e in ignoredEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot ignore other event '" + e + "' when defining a transition on the wildcard event.")); } foreach (var e in actionEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot define action on '" + e + "' when defining a transition on the wildcard event.")); } foreach (var e in transitionEvents.Where(s => !isWildCard(s))) { base.ErrorLog.Add(Tuple.Create(state.Identifier, "State '" + state.Identifier.ValueText + "' cannot define a transition on '" + e + "' when defining a transition on the wildcard event.")); } } }