private static void Check(Node node, [NotNull] FunctionCall call, [NotNull] FunctionDeclaration definition) { var table = node.SymbolTable; var parameters = definition.Profile.Parameters; var callerProfile = call.AsProfile(node); var callArgsCount = call.Arguments != null ? call.Arguments.Length : 0; if (callArgsCount > parameters.Count) { var m = string.Format("Function '{0}' only takes {1} parameter(s)", call.FunctionName, parameters.Count); DiagnosticUtils.AddError(node, m); } if (callerProfile.InputParameters.Count != definition.Profile.InputParameters.Count || callerProfile.InoutParameters.Count != definition.Profile.InoutParameters.Count || callerProfile.OutputParameters.Count != definition.Profile.OutputParameters.Count) { var m = string.Format("No suitable function signature found for '{0}' {1}", call.FunctionName, callerProfile.GetSignature()); DiagnosticUtils.AddError(node, m); } for (int c = 0; c < parameters.Count; c++) { var expected = parameters[c]; if (c < callArgsCount) { //Omitted if (call.Arguments[c].IsOmitted) { if (expected.IsOmittable) { continue; } else { DiagnosticUtils.AddError(node, "Omitted not allowed for this parameter"); return; } } var actual = call.Arguments[c].StorageAreaOrValue; if (actual.IsLiteral) { continue; //TODO } var callArgName = actual.StorageArea.ToString(); var found = node.GetDataDefinitionFromStorageAreaDictionary(actual.StorageArea); if (found == null) { continue; } var actualDataDefinition = found; var actualSpecialRegister = actual.StorageArea as StorageAreaPropertySpecialRegister; if (actualSpecialRegister != null) { var tokenType = actualSpecialRegister.SpecialRegisterName.TokenType; if (tokenType == TokenType.LENGTH) { if (call is ProcedureCall) { ProcedureCall procedureCall = call as ProcedureCall; if (procedureCall.OutputParameters.Contains(call.Arguments[c])) { DiagnosticUtils.AddError(node, "LENGTH cannot be used as an output", actualSpecialRegister.SpecialRegisterName); continue; } } // accepted format is "PIC [S]9(5..9) comp-5" if (expected.PrimitiveDataType.Name != "Numeric" || expected.Length < 5 || expected.Length > 9 || expected.Usage != DataUsage.NativeBinary) { DiagnosticUtils.AddError(node, "LENGTH can only be used as PIC S9(5..9) comp-5", actualSpecialRegister.SpecialRegisterName); continue; } } else if (tokenType == TokenType.ADDRESS && expected.Usage == DataUsage.Pointer) { if (!actualDataDefinition.IsFlagSet(Node.Flag.LinkageSectionNode) && call.Arguments[c].SharingMode.Value == ParameterSharingMode.ByReference) { DiagnosticUtils.AddError(node, "ADDRESS OF can only be used with a LINKAGE variable, or with a sharing mode BY CONTENT/BY VALUE", actualSpecialRegister.SpecialRegisterName); } continue; } else if (tokenType == TokenType.LINAGE_COUNTER) { //Do not know what to do : RFC DiagnosticUtils.AddError(node, "LINAGE_COUNTER not allowed yet with procedure"); return; } continue; //If it's a special register we don't want to check more rules. } //TODO use SubscriptExpression and ReferenceModifier of the StorageArea to correct the type //Ex: MyVar1(1:10) has a length of 10 and is of type Alphanumeric //Ex: MyArray(1) only target one element of the array, so we need to get the type of this element. //If the actual dataDefinition is a table occurence try to match it with subscripts //If the actual dataDefinition is under a table occurence, then don't care about subscripts long actualMinOccurencesCount = actualDataDefinition.MinOccurencesCount; long actualMaxOccurencesCount = actualDataDefinition.MaxOccurencesCount; bool actualHasUnboundedNumberOfOccurences = actualDataDefinition.HasUnboundedNumberOfOccurences; NumericVariable actualOccursDependingOn = actualDataDefinition.OccursDependingOn; bool actualIsTableOccurence = actualDataDefinition.IsTableOccurence; if (actualDataDefinition.IsTableOccurence) { var subscriptedStorageArea = actual.StorageArea as DataOrConditionStorageArea; if (subscriptedStorageArea != null && subscriptedStorageArea.Subscripts.Count > 0) { //if there are subscripts //Do not allow ALL if (subscriptedStorageArea.Subscripts.Any(s => s.ALL != null)) { DiagnosticUtils.AddError(node, "You cannot use ALL for procedure argument"); return; } actualMinOccurencesCount = 0; actualMaxOccurencesCount = 0; actualHasUnboundedNumberOfOccurences = false; actualOccursDependingOn = null; actualIsTableOccurence = false; } } //Cobol 85 Type will be checked with their picture if (actualDataDefinition.DataType.CobolLanguageLevel > CobolLanguageLevel.Cobol85 || expected.DataType.CobolLanguageLevel > CobolLanguageLevel.Cobol85) { if (actualDataDefinition.DataType.CobolLanguageLevel == CobolLanguageLevel.Cobol85 || expected.DataType.CobolLanguageLevel == CobolLanguageLevel.Cobol85) { var m = string.Format( "Function '{0}' expected parameter '{1}' of type {2} and received '{3}' of type {4} ", call.FunctionName, expected.Name, expected.DataType, callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.DataType); DiagnosticUtils.AddError(node, m); } else if (actualDataDefinition.DataType != expected.DataType) { TypeDefinition callerType = actualDataDefinition.TypeDefinition; TypeDefinition calleeType = expected.TypeDefinition; if (callerType == null || calleeType == null) { //Ignore, it's an unknown DataType. It's already checked } else if (!Equals(callerType.QualifiedName, calleeType.QualifiedName)) { var m = string.Format( "Function '{0}' expected parameter '{1}' of type {2} and received '{3}' of type {4} ", call.FunctionName, calleeType.Name, calleeType.DataType, callArgName ?? string.Format("position {0}", c + 1), callerType.DataType); DiagnosticUtils.AddError(node, m); } } } if (actualDataDefinition.Picture != null && expected.Picture != null && actualDataDefinition.Picture.NormalizedValue != expected.Picture.NormalizedValue) { var m = string.Format( "Function '{0}' expected parameter '{1}' with picture {2} and received '{3}' with picture {4}", call.FunctionName, expected.Name, expected.Picture.Value, callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.Picture.Value); DiagnosticUtils.AddError(node, m); } // if (dataDefinitionOfActual.Length != expectedParameter.Length) // { // var m = // string.Format( // "Function '{0}' expected parameter '{1}' of length {2} and received '{3}' of length {4}", // call.FunctionName, expectedParameter.Name, expectedParameter.Length, // callArgName ?? string.Format("position {0}", c + 1), dataDefinitionOfActual.Length); // DiagnosticUtils.AddError(e, m); // } if (actualDataDefinition.Usage != expected.Usage) { var m = string.Format( "Function '{0}' expected parameter '{1}' of usage {2} and received '{3}' of usage {4}", call.FunctionName, expected.Name, expected.Usage, callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.Usage); DiagnosticUtils.AddError(node, m); } if (actualDataDefinition.IsJustified != expected.IsJustified) { var m = string.Format( "Function '{0}' expected parameter '{1}' {2} and received '{3}' {4}", call.FunctionName, expected.Name, expected.IsJustified ? "justified" : "non-justified", callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.IsJustified ? "justified" : "non-justified"); DiagnosticUtils.AddError(node, m); } if (actualDataDefinition.IsGroupUsageNational != expected.IsGroupUsageNational) { var m = string.Format( "Function '{0}' expected parameter '{1}' {2} and received '{3}' {4}", call.FunctionName, expected.Name, expected.IsGroupUsageNational ? "national group-usage" : "non national group-usage", callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.IsGroupUsageNational ? "national group-usage" : "non national group-usage"); DiagnosticUtils.AddError(node, m); } //Array if (actualIsTableOccurence != expected.IsTableOccurence) { var m = string.Format( "Function '{0}' expected parameter '{1}' to {2} an array and received '{3}' which {4} an array", call.FunctionName, expected.Name, expected.IsTableOccurence ? "be" : "be NOT", actualDataDefinition.Name, actualIsTableOccurence ? "is" : "is NOT "); DiagnosticUtils.AddError(node, m); } else if (actualIsTableOccurence && expected.IsTableOccurence) { if (actualMinOccurencesCount != expected.MinOccurencesCount) { var m = string.Format( "Function '{0}' expected parameter '{1}' to have at least {2} occurences and received '{3}' with a minimum of {4} occurences", call.FunctionName, expected.Name, expected.MinOccurencesCount, callArgName ?? string.Format("position {0}", c + 1), actualMinOccurencesCount); DiagnosticUtils.AddError(node, m); } if (actualMaxOccurencesCount != expected.MaxOccurencesCount) { var m = string.Format( "Function '{0}' expected parameter '{1}' to have at most {2} occurences and received '{3}' with a maximum of {4} occurences", call.FunctionName, expected.Name, expected.MaxOccurencesCount, callArgName ?? string.Format("position {0}", c + 1), actualMaxOccurencesCount); DiagnosticUtils.AddError(node, m); } } if (actualOccursDependingOn != expected.OccursDependingOn) { var m = string.Format( "Function '{0}' expected parameter '{1}' occurs depending on ({2}) occurences and received '{3}' occurs depending on ({4})", call.FunctionName, expected.Name, expected.OccursDependingOn, callArgName ?? string.Format("position {0}", c + 1), actualOccursDependingOn); DiagnosticUtils.AddError(node, m); } if (actualHasUnboundedNumberOfOccurences != expected.HasUnboundedNumberOfOccurences) { var m = string.Format( "Function '{0}' expected parameter '{1}' {2} and received '{3}' {4}", call.FunctionName, expected.Name, expected.HasUnboundedNumberOfOccurences ? "has unbounded number of occurences" : "hasn't unbounded number of occurences", callArgName ?? string.Format("position {0}", c + 1), actualHasUnboundedNumberOfOccurences ? "has unbounded number of occurences" : "hasn't unbounded number of occurences"); DiagnosticUtils.AddError(node, m); } if (actualDataDefinition.SignIsSeparate != expected.SignIsSeparate) { var m = string.Format( "Function '{0}' expected parameter '{1}' {2} and received '{3}' {4}", call.FunctionName, expected.Name, expected.HasUnboundedNumberOfOccurences ? "has unbounded number of occurences" : "hasn't unbounded number of occurences", callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.HasUnboundedNumberOfOccurences ? "has unbounded number of occurences" : "hasn't unbounded number of occurences"); DiagnosticUtils.AddError(node, m); } if (actualDataDefinition.SignPosition != expected.SignPosition) { var m = string.Format( "Function '{0}' expected parameter '{1}' with sign position {2} and received '{3}' with sign position {4}", call.FunctionName, expected.Name, expected.SignPosition == null ? "empty" : expected.SignPosition.ToString(), callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.SignPosition == null ? "empty" : actualDataDefinition.SignPosition.ToString()); DiagnosticUtils.AddError(node, m); } if (actualDataDefinition.IsSynchronized != expected.IsSynchronized) { var m = string.Format( "Function '{0}' expected parameter '{1}' {2} and received '{3}' {4}", call.FunctionName, expected.Name, expected.IsSynchronized ? "synchonized" : "not synchronized", callArgName ?? string.Format("position {0}", c + 1), actualDataDefinition.IsSynchronized ? "synchonized" : "not synchronized"); DiagnosticUtils.AddError(node, m); } if (actualDataDefinition.ObjectReferenceClass != expected.ObjectReferenceClass) { var m = string.Format( "Function '{0}' expected parameter '{1}' and received '{2}' with wrong object reference.", call.FunctionName, expected.Name, callArgName ?? string.Format("position {0}", c + 1)); DiagnosticUtils.AddError(node, m); } } else { var m = string.Format("Function '{0}' is missing parameter '{1}' of type {2} and length {3}", call.FunctionName, expected.Name, expected.DataType, expected.Length); DiagnosticUtils.AddError(node, m); } } }