public override Method VisitMethod(Method method) { if (method == null) return null; // Need to call base visitor so that contract inheritance has already happened Method result = base.VisitMethod(method); #region MustOverride if (method.GetAttribute(Runtime.MustOverrideAttribute) != null && !method.IsVirtual) { this.HandleError(method.Name, Error.MethodMarkedMustOverrideMustBeVirtual, this.GetMethodSignature(method)); } #endregion #region Check constraints when the method overrides a virtual method or implements an interface method if ((method.ImplementedInterfaceMethods != null && method.ImplementedInterfaceMethods.Count > 0) || (method.ImplicitlyImplementedInterfaceMethods != null && method.ImplicitlyImplementedInterfaceMethods.Count > 0) || method.OverriddenMethod != null || (method.IsPropertyGetter && method.DeclaringMember.OverriddenMember != null) ) { #region Purity must be consistent AttributeNode purityMarker = null; Method otherMethod = null; for (int i = 0, n = method.ImplementedInterfaceMethods == null ? 0 : method.ImplementedInterfaceMethods.Count; i < n; i++) { Method m = method.ImplementedInterfaceMethods[i]; if (m == null) continue; purityMarker = GetPurityAttribute(m); if (purityMarker != null) { otherMethod = m; break; // FIXME! if one is marked, all should be marked the same!! } } if (otherMethod == null) { for (int i = 0, n = method.ImplicitlyImplementedInterfaceMethods == null ? 0 : method.ImplicitlyImplementedInterfaceMethods.Count; i < n; i++) { Method m = method.ImplicitlyImplementedInterfaceMethods[i]; if (m == null) continue; purityMarker = GetPurityAttribute(m); if (purityMarker != null) { otherMethod = m; break; // FIXME! if one is marked, all should be marked the same!! } } } if (otherMethod == null){ if (method.OverriddenMethod != null) { purityMarker = GetPurityAttribute(method.OverriddenMethod); if (purityMarker != null) { otherMethod = method.OverriddenMethod; } } } if (otherMethod == null) { if (method.IsPropertyGetter && method.DeclaringMember.OverriddenMember != null) { Property p = method.DeclaringMember.OverriddenMember as Property; if (p != null) { purityMarker = GetPurityAttribute(p.Getter); if (purityMarker != null) { otherMethod = p.Getter; } } } } Method methodToComplainAbout = method; ProxyMethod pm = methodToComplainAbout as ProxyMethod; if (pm != null) { methodToComplainAbout = pm.ProxyFor; } if (purityMarker != null) { if (!PurityLessThanOrEqualTo(otherMethod, methodToComplainAbout)) { string marking; if (otherMethod.IsPure) marking = "[Pure]"; else if (otherMethod.IsConfined) marking = "[Pure]"; else //(otherMethod.IsStateIndependent) marking = "[Pure][Reads(ReadsAttribute.Reads.Nothing)]"; this.HandleError(methodToComplainAbout.Name, Error.MethodInheritingPurityMustBeMarked, this.GetMethodSignature(methodToComplainAbout), this.GetMethodSignature(otherMethod), marking); } } #endregion Purity must be consistent } #endregion Check constraints when the method overrides a virtual method or implements an inteface method #region Additive AttributeNode attrNode = method.GetAttribute(SystemTypes.AdditiveAttribute); AttributeNode overridenAttr = null; bool isAdditive = IsAdditive(attrNode); if (isAdditive && attrNode != null) { if (attrNode.Target == AttributeTargets.ReturnValue) { if (method.ReturnType.IsValueType) { this.HandleError(attrNode, Error.AttributeAllowedOnlyOnReferenceTypeParameters, this.GetTypeName(attrNode.Type)); } } else if (method.DeclaringType.IsValueType) { this.HandleError(attrNode, Error.AttributeAllowedOnlyOnReferenceTypeParameters, this.GetTypeName(attrNode.Type)); } else if (method.IsStatic) { // don't need to check for whether method is a ctor (which would be an error) because // if the method is a ctor, then an error will already have been generated since // ctors are not listed as a valid target for [Additive] this.HandleError(method, Error.StaticMethodCannotBeMarkedWithAttribute, this.GetTypeName(SystemTypes.AdditiveAttribute)); } } Method overriden = method.OverriddenMethod; if (overriden != null) { overridenAttr = overriden.GetAttribute(SystemTypes.AdditiveAttribute); if (IsAdditive(overridenAttr) != isAdditive) this.HandleError(method, Error.OverrideMethodNotMarkedWithAttribute, this.GetTypeName(SystemTypes.AdditiveAttribute)); } #endregion #region Inside attrNode = method.GetAttribute(SystemTypes.InsideAttribute); overridenAttr = null; bool isInside = IsInside(attrNode); if (isInside && attrNode != null) { if (attrNode.Target == AttributeTargets.ReturnValue) { if (method.ReturnType.IsValueType) { this.HandleError(attrNode, Error.AttributeAllowedOnlyOnReferenceTypeParameters, this.GetTypeName(attrNode.Type)); } } else if (method.DeclaringType.IsValueType) { this.HandleError(attrNode, Error.AttributeAllowedOnlyOnReferenceTypeParameters, this.GetTypeName(attrNode.Type)); } else if (method.DeclaringType is Interface) { this.HandleError(attrNode, Error.AttributeNotAllowedOnInterfaceMethods, this.GetTypeName(attrNode.Type)); } else if (method.IsVirtual) { this.HandleError(method, Error.VirtualMethodWithNonVirtualMethodAttribute, this.GetTypeName(SystemTypes.InsideAttribute)); } } #endregion #region Captured attrNode = method.GetAttribute(SystemTypes.CapturedAttribute); overridenAttr = null; bool isCaptured = attrNode != null; if (isCaptured) { if (attrNode.Target != AttributeTargets.ReturnValue && method.DeclaringType.IsValueType) { // if its target is "return", then it will already have gotten an error for that // just because that isn't included in the valid targets for that attribute this.HandleError(attrNode, Error.AttributeAllowedOnlyOnReferenceTypeParameters, this.GetTypeName(attrNode.Type)); } } overriden = method.OverriddenMethod; if (overriden != null) { overridenAttr = overriden.GetAttribute(SystemTypes.CapturedAttribute); if (overridenAttr != null && !isCaptured) this.HandleError(method, Error.OverrideMethodNotMarkedWithAttribute, this.GetTypeName(SystemTypes.CapturedAttribute)); } #endregion #region Can't have both [Rep] and [Peer] bool isRep = method.GetAttribute(SystemTypes.RepAttribute) != null; bool isPeer = method.GetAttribute(SystemTypes.PeerAttribute) != null; if (isRep && isPeer) { this.HandleError(method, Error.ConflictingAttributes, this.GetTypeName(SystemTypes.RepAttribute), this.GetTypeName(SystemTypes.PeerAttribute)); } #endregion #region NoReferenceComparison attrNode = method.GetAttribute(SystemTypes.NoReferenceComparisonAttribute); overridenAttr = null; if (overriden != null) overridenAttr = overriden.GetAttribute(SystemTypes.NoReferenceComparisonAttribute); if (attrNode == null && overridenAttr != null) this.HandleError(method, Error.NoReferenceComparisonAttrNotCopied, method.Name.ToString()); else if (attrNode != null && !(method.IsPure || method.IsConfined || method.IsStateIndependent)) this.HandleError(method, Error.NonPureMarkedNoReferenceComparison); else if (attrNode != null) { NoReferenceComparisonVisitor visitor = new NoReferenceComparisonVisitor(this); this.TransferStateTo(visitor); visitor.Visit(method.Body); if (!visitor.HasNoReferenceComparison) this.HandleError(method, Error.ViolatedNoReferenceComparison, method.Name.ToString()); } #endregion #region ResultNotNewlyAllocated attrNode = method.GetAttribute(SystemTypes.ResultNotNewlyAllocatedAttribute); overridenAttr = null; if (overriden != null) overridenAttr = overriden.GetAttribute(SystemTypes.ResultNotNewlyAllocatedAttribute); if (attrNode == null && overridenAttr != null) this.HandleError(method, Error.ResultNotNewlyAllocatedAttrNotCopied, method.Name.ToString()); else if (attrNode != null && !(method.IsPure || method.IsConfined || method.IsStateIndependent)) this.HandleError(method, Error.NonPureMarkedResultNotNewlyAllocated); else if (attrNode != null && (method.ReturnType == null || method.ReturnType.IsValueType)) this.HandleError(method, Error.NonRefTypeMethodMarkedResultNotNewlyAllocated); #endregion #region Pure methods cannot have ref parameters if (!method.IsPropertyGetter && (method.IsPure || method.IsConfined || method.IsStateIndependent)) { // don't check property getters: they already will have an error if they have out or ref parameters. for (int i = 0, n = method.Parameters == null ? 0 : method.Parameters.Count; i < n; i++) { Parameter p = method.Parameters[i]; if (p.IsOut) continue; Reference r = p.Type as Reference; if (r != null) { this.HandleError(p, Error.PureMethodCannotHaveRefParam); } } } #endregion #region Pure methods cannot have modifies clauses if ((method.IsPure || method.IsConfined || method.IsStateIndependent) && method.Contract != null && method.Contract.Modifies != null && 0 < method.Contract.Modifies.Count) { this.HandleError(method, Error.PureMethodCannotHaveModifies); } #endregion Pure methods cannot have modifies clauses bool b; TypeNode returnType = method.ReturnType; if (returnType != null) returnType = returnType.StripOptionalModifiers(out b); if ((method.GetAttribute(SystemTypes.RepAttribute) != null || method.GetAttribute(SystemTypes.PeerAttribute) != null) && !(returnType.IsReferenceType || (method.IsPropertyGetter && ((Property)method.DeclaringMember).Type.IsReferenceType))) this.HandleError(method, Error.BadUseOfOwnedOnMethod); else if (method.IsVirtual && !method.IsPropertyGetter && MemberIsRep(method)) // note: second conjunct mainly to get Boogie through this.HandleError(method, Error.UseOfRepOnVirtualMethod); #region Make sure purity attributes are used correctly AttributeNode pureAttr = method.GetAttributeFromSelfOrDeclaringMember(SystemTypes.PureAttribute); AttributeNode readsAttr = method.GetAttributeFromSelfOrDeclaringMember(SystemTypes.ReadsAttribute); if (readsAttr != null) { if (pureAttr == null) { this.HandleError(method, Error.ReadsWithoutPure); } Literal l = readsAttr.GetPositionalArgument(0) as Literal; // default ctor for Reads sets it to Owned Microsoft.Contracts.ReadsAttribute.Reads r = l == null ? Microsoft.Contracts.ReadsAttribute.Reads.Owned : (Microsoft.Contracts.ReadsAttribute.Reads)l.Value; switch (r) { case Microsoft.Contracts.ReadsAttribute.Reads.Everything: if (!method.IsPure) this.HandleError(method, Error.InconsistentPurityAttributes); break; case Microsoft.Contracts.ReadsAttribute.Reads.Nothing: if (!method.IsStateIndependent) this.HandleError(method, Error.InconsistentPurityAttributes); break; case Microsoft.Contracts.ReadsAttribute.Reads.Owned: if (!method.IsConfined) this.HandleError(method, Error.PureOwnedNotAllowed); break; } } #endregion Make sure purity attributes are used correctly return result; }
private AttributeNode GetPurityAttribute(Method method) { if (method == null || method.Attributes == null) return null; return method.GetAttributeFromSelfOrDeclaringMember(SystemTypes.PureAttribute); }