From a69b57b1e876de90ad590a21b224044e9dfdcdad Mon Sep 17 00:00:00 2001 From: osmedile Date: Thu, 28 Mar 2019 21:56:56 +0100 Subject: [PATCH] WI #1302 #1295 TypeCobolLinker refactoring #1327 Fix Global scope TypeCobol refactoring but doesn't change its goals: - Resolve type reference - Detect circular reference between typedef - Register link between type and variable in SymbolTable TypeCobolLinker is now in CrossCheck phase. It doesn't check/link dependencies directly but start Type resolution from the main file. This allows to fix #1326 and #1338. It means the loading order of dependencies doesn't affect the work of TypeCobolLinker. As a TypeCobolLinker only start its work from the main file, it means that unused Typedef and procedure in dependencies won't be resolved anymore. Type of used procedure will only be resolved, WI #1295 TypeCobolLinker can now detect circular references between more than 2 types. It can also handle circular references between main file and dependencies. WI #1327 Fix global but it requires new TypeCobolLinker thats why it's merged in the same commit. The correct SymbolTable is now associated with global Node. --- Codegen/src/Nodes/ParameterEntry.cs | 6 +- .../Cobol2002/ParserIntegration.rdzPGM.txt | 2 +- .../Cobol2002/Typedef3-global.rdz.tcbl | 6 +- .../Cobol2002/Typedef3-global.rdzMix.txt | 8 +- .../CircularReferenceType.rdzMix.txt | 14 +- .../CircularReferenceType.rdzPGM.txt | 14 +- TypeCobol/Compiler/CodeModel/SymbolTable.cs | 16 +- TypeCobol/Compiler/CompilationUnit.cs | 12 +- .../NodeBuilder/ProgramClassBuilder.cs | 116 ++++-- .../Compiler/Diagnostics/Cobol2002Checker.cs | 73 +--- .../Compiler/Diagnostics/TypeCobolChecker.cs | 10 + .../Compiler/Diagnostics/TypeCobolLinker.cs | 389 +++++++++++++----- TypeCobol/Compiler/Nodes/Data.cs | 17 +- .../Compiler/Parser/ProgramClassParserStep.cs | 15 +- .../Parser/TemporarySemanticDocument.cs | 16 +- 15 files changed, 472 insertions(+), 242 deletions(-) diff --git a/Codegen/src/Nodes/ParameterEntry.cs b/Codegen/src/Nodes/ParameterEntry.cs index a87d8dfc0..4a67f6d90 100644 --- a/Codegen/src/Nodes/ParameterEntry.cs +++ b/Codegen/src/Nodes/ParameterEntry.cs @@ -49,7 +49,7 @@ public override IEnumerable Lines { //Type exists from Cobol 2002 string typedef = null; if (this.CodeElement.DataType.CobolLanguageLevel >= TypeCobol.Compiler.CobolLanguageLevel.Cobol2002) { - var type = this.Description?.TypeDefinition ?? this.SymbolTable.GetType(this.CodeElement.DataType).FirstOrDefault(); + var type = this.Description?.TypeDefinition; if (type != null) { customtype = type; @@ -84,7 +84,9 @@ public override IEnumerable Lines { if (picture == null && this.CodeElement.Usage == null && this.CodeElement.DataType.CobolLanguageLevel == Compiler.CobolLanguageLevel.Cobol85) {//JCM humm... Type without picture lookup enclosing scope. - var type = this.Description?.TypeDefinition ?? this.SymbolTable.GetType(this.CodeElement.DataType).FirstOrDefault(); + + //TODO seems to be an impossible situation as we check if we are on Compiler.CobolLanguageLevel.Cobol85 + var type = this.Description?.TypeDefinition; if (type != null) { customtype = type; diff --git a/TypeCobol.Test/Parser/Programs/Cobol2002/ParserIntegration.rdzPGM.txt b/TypeCobol.Test/Parser/Programs/Cobol2002/ParserIntegration.rdzPGM.txt index 8f9902c43..ff6f1f988 100644 --- a/TypeCobol.Test/Parser/Programs/Cobol2002/ParserIntegration.rdzPGM.txt +++ b/TypeCobol.Test/Parser/Programs/Cobol2002/ParserIntegration.rdzPGM.txt @@ -1,6 +1,6 @@ --- Diagnostics --- -Line 19[44,48] <27, Error, Syntax> - Syntax error : PICTURE clause incompatible with TYPE clause OffendingSymbol=[44,48:X(21)] Line 19[18,59] <30, Error, Semantics> - Semantic error: TYPE 'rib' is not referenced +Line 19[44,48] <27, Error, Syntax> - Syntax error : PICTURE clause incompatible with TYPE clause OffendingSymbol=[44,48:X(21)] Line 23[44,48] <27, Error, Syntax> - Syntax error : PICTURE clause incompatible with TYPE clause OffendingSymbol=[44,48:X(08)] --- Program --- diff --git a/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdz.tcbl b/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdz.tcbl index c15616e6f..dfdd58562 100644 --- a/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdz.tcbl +++ b/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdz.tcbl @@ -35,19 +35,19 @@ *Complex USED Typedef - 01 Typedef1 TYPEDEF STRICT. + 01 Typedef1 TYPEDEF STRICT global. 05 td-var1 pic X. 05 td-var10. 10 td-var11 pic X. 10 td-var12 pic X. 10 td-var13 type Typedef2. - 01 Typedef2 TYPEDEF STRICT. + 01 Typedef2 TYPEDEF STRICT global. 05 td-var2 pic X. 05 td-var20. 10 td-var21 pic X. 10 td-var22 pic X. 10 td-var23 type MainProgram::Typedef3. - 01 Typedef3 TYPEDEF STRICT. + 01 Typedef3 TYPEDEF STRICT global. 05 td-var3 pic X. 05 td-var30. 10 td-var31 pic X. diff --git a/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdzMix.txt b/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdzMix.txt index 34d39434f..f42d1773a 100644 --- a/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdzMix.txt +++ b/TypeCobol.Test/Parser/Programs/Cobol2002/Typedef3-global.rdzMix.txt @@ -1,4 +1,4 @@ - IDENTIFICATION DIVISION. + IDENTIFICATION DIVISION. PROGRAM-ID. MainProgram. data division. working-storage section. @@ -35,19 +35,19 @@ *Complex USED Typedef - 01 Typedef1 TYPEDEF STRICT. + 01 Typedef1 TYPEDEF STRICT global. 05 td-var1 pic X. 05 td-var10. 10 td-var11 pic X. 10 td-var12 pic X. 10 td-var13 type Typedef2. - 01 Typedef2 TYPEDEF STRICT. + 01 Typedef2 TYPEDEF STRICT global. 05 td-var2 pic X. 05 td-var20. 10 td-var21 pic X. 10 td-var22 pic X. 10 td-var23 type MainProgram::Typedef3. - 01 Typedef3 TYPEDEF STRICT. + 01 Typedef3 TYPEDEF STRICT global. 05 td-var3 pic X. 05 td-var30. 10 td-var31 pic X. diff --git a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt index 0cbf7907e..2b7ebd1c7 100644 --- a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt +++ b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzMix.txt @@ -5,23 +5,13 @@ WORKING-STORAGE SECTION. 01 ThirdType TYPEDEF STRICT. -Line 8[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected +Line 8[13,35] <30, Error, Semantics> - Semantic error: Type circular reference detected : ThirdType -> myType 05 renjgrn TYPE myType. 01 myType TYPEDEF STRICT. 05 myVar PIC X(10). 05 secondGroup pic X. -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected 05 yhrtger TYPE ThirdType. -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected 05 ezgoerk TYPE MySendType. 01 MyGroup. @@ -33,7 +23,7 @@ Line 17[15,20] <30, Error, Semantics> - Semantic error: Variable 'MyVar1' has to 01 MySendType TYPEDEF STRICT. 05 MyVariable PIC X(10). 05 MySecVariable PIC X. -Line 24[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected +Line 24[13,38] <30, Error, Semantics> - Semantic error: Type circular reference detected : MySendType -> myType 05 SelfRef TYPE myType. diff --git a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt index fded7aa4d..d9dff8f4a 100644 --- a/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt +++ b/TypeCobol.Test/Parser/Programs/TypeCobol/CircularReferenceType.rdzPGM.txt @@ -1,17 +1,7 @@ --- Diagnostics --- -Line 8[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:renjgrn] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 13[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:yhrtger] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] -Line 14[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:ezgoerk] +Line 8[13,35] <30, Error, Semantics> - Semantic error: Type circular reference detected : ThirdType -> myType Line 17[15,20] <30, Error, Semantics> - Semantic error: Variable 'MyVar1' has to be limited to level 47 because of 'myType' maximum estimated children level OffendingSymbol=[15,20:MyVar1] -Line 24[16,22] <30, Error, Semantics> - Semantic error: Type circular reference detected OffendingSymbol=[16,22:SelfRef] +Line 24[13,38] <30, Error, Semantics> - Semantic error: Type circular reference detected : MySendType -> myType --- Program --- PROGRAM: CircularRefCheck common:False initial:False recursive:False diff --git a/TypeCobol/Compiler/CodeModel/SymbolTable.cs b/TypeCobol/Compiler/CodeModel/SymbolTable.cs index aa1ca992f..38eec7008 100644 --- a/TypeCobol/Compiler/CodeModel/SymbolTable.cs +++ b/TypeCobol/Compiler/CodeModel/SymbolTable.cs @@ -195,11 +195,8 @@ void AddVariableUnderTypeDefinition([NotNull] DataDefinition data) //TypeDefinition must NOT be added to DataTypeEntries, only its children Debug.Assert(!(data is TypeDefinition)); - //Types are declared in the Declarations SymbolTable - var table = GetTableFromScope(Scope.Declarations); - //Add symbol to the dictionary - Add(table.DataTypeEntries, data); + Add(this.DataTypeEntries, data); } public IEnumerable GetVariables(SymbolReference symbolReference) @@ -686,6 +683,13 @@ private static void GetVariablesUnderTypeDefForPublicType(SymbolTable symbolTabl if (temp != null) { resultDataDefinitions.AddRange(temp); } + + //Stop when we reach the first Global scope, because otherwise a nested program can end up searching in it the global scope of its parent + //Global is the most upper scope that can contains Typedef (GlobalStorage cannot contains typedef) + if (currSymbolTable.CurrentScope == Scope.Global) + { + break; + } currSymbolTable = currSymbolTable.EnclosingScope; } @@ -867,7 +871,7 @@ public void AddDataDefinitionsUnderType([NotNull] DataDefinition data) } } - public IList EmptyTypeDefinitionList = new List(); + public List EmptyTypeDefinitionList = new List(); [NotNull] public IList GetType(DataDefinition symbol) { @@ -882,7 +886,7 @@ public List GetType(SymbolReference symbolReference) [NotNull] - public IList GetType(DataType dataType, string pgmName = null) + public List GetType(DataType dataType, string pgmName = null) { if (dataType.CobolLanguageLevel == CobolLanguageLevel.Cobol85) { diff --git a/TypeCobol/Compiler/CompilationUnit.cs b/TypeCobol/Compiler/CompilationUnit.cs index 793c15538..70cbe0b96 100644 --- a/TypeCobol/Compiler/CompilationUnit.cs +++ b/TypeCobol/Compiler/CompilationUnit.cs @@ -173,7 +173,7 @@ public void RefreshProgramClassDocumentSnapshot() SourceFile root = temporarySnapshot.Root; List diagnostics = new List(); Dictionary nodeCodeElementLinkers = temporarySnapshot.NodeCodeElementLinkers ?? new Dictionary(); - ProgramClassParserStep.CrossCheckPrograms(root); + ProgramClassParserStep.CrossCheckPrograms(root, temporarySnapshot); // Capture the result of the parse in a new snapshot ProgramClassDocumentSnapshot = new ProgramClassDocument( @@ -221,11 +221,17 @@ public void ProduceTemporarySemanticDocument() List newDiagnostics; Dictionary nodeCodeElementLinkers = new Dictionary(); + List typedVariablesOutsideTypedef = new List(); + List typeThatNeedTypeLinking = new List(); + //TODO cast to ImmutableList sometimes fails here - ProgramClassParserStep.CupParseProgramOrClass(TextSourceInfo, ((ImmutableList)codeElementsDocument.Lines), CompilerOptions, CustomSymbols, perfStatsForParserInvocation, out root, out newDiagnostics, out nodeCodeElementLinkers); + ProgramClassParserStep.CupParseProgramOrClass(TextSourceInfo, ((ImmutableList)codeElementsDocument.Lines), CompilerOptions, CustomSymbols, perfStatsForParserInvocation, out root, out newDiagnostics, out nodeCodeElementLinkers, + out typedVariablesOutsideTypedef, + out typeThatNeedTypeLinking); // Capture the produced results - TemporaryProgramClassDocumentSnapshot = new TemporarySemanticDocument(codeElementsDocument, new DocumentVersion(this), codeElementsDocument.Lines, root, newDiagnostics, nodeCodeElementLinkers); + TemporaryProgramClassDocumentSnapshot = new TemporarySemanticDocument(codeElementsDocument, new DocumentVersion(this), codeElementsDocument.Lines, root, newDiagnostics, nodeCodeElementLinkers, + typedVariablesOutsideTypedef, typeThatNeedTypeLinking); // Stop perf measurement PerfStatsForTemporarySemantic.OnStopRefreshParsingStep(); diff --git a/TypeCobol/Compiler/CupParser/NodeBuilder/ProgramClassBuilder.cs b/TypeCobol/Compiler/CupParser/NodeBuilder/ProgramClassBuilder.cs index 6d2799b7c..f5a49ea10 100644 --- a/TypeCobol/Compiler/CupParser/NodeBuilder/ProgramClassBuilder.cs +++ b/TypeCobol/Compiler/CupParser/NodeBuilder/ProgramClassBuilder.cs @@ -24,7 +24,19 @@ public class ProgramClassBuilder : IProgramClassBuilder private Program Program { get; set; } public SyntaxTree SyntaxTree { get; set; } - private TypeDefinition _CurrentTypeDefinition; + private TypeDefinition _CurrentTypeDefinition + { + get => _currentTypeDefinition; + set + { + _currentTypeDefinition = value; + //Reset that this type was already added to the list TypeThatNeedTypeLinking + typeAlreadyAddedToTypeToLink = false; + } + } + + private bool typeAlreadyAddedToTypeToLink = false; + private bool _IsInsideWorkingStorageContext; private bool _IsInsideLinkageSectionContext; private bool _IsInsideLocalStorageSectionContext; @@ -32,6 +44,9 @@ public class ProgramClassBuilder : IProgramClassBuilder private bool _IsInsideGlobalStorageSection; private FunctionDeclaration _ProcedureDeclaration; + public List TypedVariablesOutsideTypedef { get; } = new List(); + public List TypeThatNeedTypeLinking { get; } = new List(); + // Programs can be nested => track current programs being analyzed private Stack programsStack = null; @@ -181,25 +196,6 @@ private void ExitLastLevel1Definition() while (CurrentNode.CodeElement != null && CurrentNode.CodeElement is DataDefinitionEntry) Exit(); } - private void AddToSymbolTable(DataDescription node) - { - var table = node.SymbolTable; - if (node.CodeElement.IsGlobal && table.CurrentScope != SymbolTable.Scope.GlobalStorage) - table = table.GetTableFromScope(SymbolTable.Scope.Global); - else - { - var parent = node.Parent as DataDescription; - while (parent != null) - { - if (parent.CodeElement.IsGlobal && table.CurrentScope != SymbolTable.Scope.GlobalStorage) - table = table.GetTableFromScope(SymbolTable.Scope.Global); - parent = parent.Parent as DataDescription; - } - } - - table.AddVariable(node); - } - public virtual void StartCobolCompilationUnit() { if (TableOfNamespaces == null) @@ -415,36 +411,31 @@ public virtual void StartDataDescriptionEntry(DataDescriptionEntry entry) { SetCurrentNodeToTopLevelItem(entry.LevelNumber); + var symbolTable = SyntaxTree.CurrentNode.SymbolTable; + if (entry.IsGlobal) + symbolTable = symbolTable.GetTableFromScope(SymbolTable.Scope.Global); + //Update DataType of CodeElement by searching info on the declared Type into SymbolTable. //Note that the AST is not complete here, but you can only refer to a Type that has previously been defined. var node = new DataDescription(entry); if (_CurrentTypeDefinition != null) node.ParentTypeDefinition = _CurrentTypeDefinition; - Enter(node); + Enter(node, null, symbolTable); if (entry.Indexes != null && entry.Indexes.Any()) { - var table = node.SymbolTable; + foreach (var index in entry.Indexes) { - if (node.CodeElement.IsGlobal) - table = table.GetTableFromScope(SymbolTable.Scope.Global); - var indexNode = new IndexDefinition(index); - Enter(indexNode, null, table); + Enter(indexNode, null, symbolTable); if (_CurrentTypeDefinition != null) indexNode.ParentTypeDefinition = _CurrentTypeDefinition; - table.AddVariable(indexNode); + symbolTable.AddVariable(indexNode); Exit(); } } - var types = node.SymbolTable.GetType(node); - if (types.Count == 1) - { - entry.DataType.RestrictionLevel = types[0].DataType.RestrictionLevel; - } - //else do nothing, it's an error that will be handled by Cobol2002Checker if(_IsInsideWorkingStorageContext) node.SetFlag(Node.Flag.WorkingSectionNode, true); //Set flag to know that this node belongs to Working Storage Section @@ -457,18 +448,55 @@ public virtual void StartDataDescriptionEntry(DataDescriptionEntry entry) if (_IsInsideGlobalStorageSection) node.SetFlag(Node.Flag.GlobalStorageSection, true); //Set flag to know that this node belongs to Global Storage Section - AddToSymbolTable(node); + node.SymbolTable.AddVariable(node); + CheckIfItsTyped(node, node.CodeElement); } } + private void CheckIfItsTyped(DataDefinition dataDefinition, CommonDataDescriptionAndDataRedefines commonDataDescriptionAndDataRedefines) + { + //Is a type referenced + if (commonDataDescriptionAndDataRedefines.UserDefinedDataType != null) + { + if (_CurrentTypeDefinition != null) + { + _CurrentTypeDefinition.TypedChildren.Add(dataDefinition); + } + else + { + TypedVariablesOutsideTypedef.Add(dataDefinition); + } + } + + //Special case for Depending On. + //Depending on inside typedef can reference other variable declared in type referenced in this typedef + //To resolve variable after "depending on" we first have to resolve all types used in this typedef. + //This resolution must be recursive until all sub types have been resolved. + if (commonDataDescriptionAndDataRedefines.OccursDependingOn != null) + { + if (_CurrentTypeDefinition != null && !typeAlreadyAddedToTypeToLink) + { + TypeThatNeedTypeLinking.Add(_CurrentTypeDefinition); + typeAlreadyAddedToTypeToLink = true; + } + } + } + + public virtual void StartDataRedefinesEntry(DataRedefinesEntry entry) { SetCurrentNodeToTopLevelItem(entry.LevelNumber); + var symbolTable = SyntaxTree.CurrentNode.SymbolTable; + if (entry.IsGlobal) + symbolTable = symbolTable.GetTableFromScope(SymbolTable.Scope.Global); + var node = new DataRedefines(entry); if (_CurrentTypeDefinition != null) node.ParentTypeDefinition = _CurrentTypeDefinition; - Enter(node); + Enter(node, null, symbolTable); node.SymbolTable.AddVariable(node); + + CheckIfItsTyped(node, node.CodeElement); } public virtual void StartDataRenamesEntry(DataRenamesEntry entry) @@ -494,13 +522,17 @@ public virtual void StartDataConditionEntry(DataConditionEntry entry) public virtual void StartTypeDefinitionEntry(DataTypeDescriptionEntry typedef) { SetCurrentNodeToTopLevelItem(typedef.LevelNumber); - var node = new TypeDefinition(typedef); - Enter(node); + // TCTYPE_GLOBAL_TYPEDEF - var table = node.SymbolTable.GetTableFromScope(node.CodeElement.IsGlobal ? SymbolTable.Scope.Global : SymbolTable.Scope.Declarations); - table.AddType(node); + var symbolTable = SyntaxTree.CurrentNode.SymbolTable.GetTableFromScope(typedef.IsGlobal ? SymbolTable.Scope.Global : SymbolTable.Scope.Declarations); + + var node = new TypeDefinition(typedef); + Enter(node, null, symbolTable); + + symbolTable.AddType(node); _CurrentTypeDefinition = node; + CheckIfItsTyped(node, node.CodeElement); } public virtual void StartWorkingStorageSection(WorkingStorageSectionHeader header) @@ -586,6 +618,8 @@ public virtual void EnterUseStatement(UseStatement useStatement) } private Tools.UIDStore uidfactory = new Tools.UIDStore(); + private TypeDefinition _currentTypeDefinition; + public virtual void StartFunctionDeclaration(FunctionDeclarationHeader header) { header.SetLibrary(CurrentProgram.Identification.ProgramName.Name); @@ -617,6 +651,7 @@ public virtual void StartFunctionDeclaration(FunctionDeclarationHeader header) paramNode.SetParent(CurrentNode); CurrentNode.SymbolTable.AddVariable(paramNode); + CheckIfItsTyped(paramNode, paramNode.CodeElement); } foreach (var parameter in declaration.Profile.OutputParameters) //Set Output Parameters { @@ -628,6 +663,7 @@ public virtual void StartFunctionDeclaration(FunctionDeclarationHeader header) paramNode.SetParent(CurrentNode); CurrentNode.SymbolTable.AddVariable(paramNode); + CheckIfItsTyped(paramNode, paramNode.CodeElement); } foreach (var parameter in declaration.Profile.InoutParameters) //Set Inout Parameters { @@ -639,6 +675,7 @@ public virtual void StartFunctionDeclaration(FunctionDeclarationHeader header) paramNode.SetParent(CurrentNode); CurrentNode.SymbolTable.AddVariable(paramNode); + CheckIfItsTyped(paramNode, paramNode.CodeElement); } if (declaration.Profile.ReturningParameter != null) //Set Returning Parameters @@ -650,6 +687,7 @@ public virtual void StartFunctionDeclaration(FunctionDeclarationHeader header) paramNode.SetParent(CurrentNode); CurrentNode.SymbolTable.AddVariable(paramNode); + CheckIfItsTyped(paramNode, paramNode.CodeElement); } } diff --git a/TypeCobol/Compiler/Diagnostics/Cobol2002Checker.cs b/TypeCobol/Compiler/Diagnostics/Cobol2002Checker.cs index ab1a83f95..d0bebc3bf 100644 --- a/TypeCobol/Compiler/Diagnostics/Cobol2002Checker.cs +++ b/TypeCobol/Compiler/Diagnostics/Cobol2002Checker.cs @@ -113,6 +113,9 @@ public static void CheckTypeDefinition(TypeDefinition typeDefinition) token, code: MessageCode.Warning); } } + + //Check circular reference if not already done by TypeCobolLinker + TypeCobolLinker.CheckCircularReferences(typeDefinition); } private static void CheckForValueClause(Node node, string typedefName) @@ -233,8 +236,7 @@ public static void OnNode(Node node) } var type = dataDefinition.DataType; - TypeDefinition foundedType = null; - TypeDefinitionHelper.Check(node, type, out foundedType); //Check if the type exists and is not ambiguous + TypeDefinition foundedType = dataDefinition.TypeDefinition; if (foundedType == null || data == null || data.LevelNumber == null) return; @@ -277,7 +279,13 @@ public static void OnNode(Node node) private static long SimulatedTypeDefLevel(long startingLevel, DataDefinition node) { + if (node == null)//case where the type of variable is not found + { + return startingLevel; + } + var maximalLevelReached = startingLevel; + if (node is TypeDefinition) { @@ -291,22 +299,7 @@ private static long SimulatedTypeDefLevel(long startingLevel, DataDefinition nod var calculatedLevel = startingLevel; if (child.DataType.CobolLanguageLevel > CobolLanguageLevel.Cobol85) //If variable is typed { - /*----- This section should be removed when issue #1009 is fixed ----- */ - /*----- We'll only need child.TypeDefinition --------------------------*/ - TypeDefinition foundType; - if (child.TypeDefinition == null) - { - var foundedTypes = node.SymbolTable.GetType(child.DataType); - if (foundedTypes.Count != 1) - continue; //If none or multiple corresponding type, it's useless to check - - foundType = foundedTypes.First(); - } - else - foundType = child.TypeDefinition; - /* ----------------------------------------------------------------- */ - - calculatedLevel = SimulatedTypeDefLevel(++calculatedLevel, foundType); + calculatedLevel = SimulatedTypeDefLevel(++calculatedLevel, child.TypeDefinition); } else if (child.Children.Count > 0) //If variable is not typed, check if there is children { @@ -323,47 +316,5 @@ private static long SimulatedTypeDefLevel(long startingLevel, DataDefinition nod return maximalLevelReached; } } - - public static class TypeDefinitionHelper - { - /// - /// Generic method to check if a type is referenced or not or if it is ambiguous. - /// - /// - /// - /// - public static void Check(Node node, DataType type, out TypeDefinition foundedType) - { - foundedType = null; - if (type.CobolLanguageLevel == CobolLanguageLevel.Cobol85) - return; //nothing to do, Type exists from Cobol 2002 - var dataDefinition = node as DataDefinition; - if (dataDefinition?.TypeDefinition != null) - { - foundedType = dataDefinition.TypeDefinition; - return; - } - - var found = node.SymbolTable.GetType(type); - if (found.Count < 1) - { - string message = "TYPE \'" + type.Name + "\' is not referenced"; - DiagnosticUtils.AddError(node, message, MessageCode.SemanticTCErrorInParser); - } - else if (found.Count > 1) - { - string message = "Ambiguous reference to TYPE \'" + type.Name + "\'"; - DiagnosticUtils.AddError(node, message, MessageCode.SemanticTCErrorInParser); - } - else - { - //In case TypeDefinition is not already set, or it's not a DataDefinition Node - foundedType = found[0]; - if (dataDefinition != null) - dataDefinition.TypeDefinition = foundedType; - } - - } - - } + } \ No newline at end of file diff --git a/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs b/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs index 2e95df375..35e75a247 100644 --- a/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs +++ b/TypeCobol/Compiler/Diagnostics/TypeCobolChecker.cs @@ -229,6 +229,16 @@ private static void Check(Node node, [NotNull] FunctionCall call, for (int c = 0; c < parameters.Count; c++) { var expected = parameters[c]; + + //Hack until we get a real concept of project to build all of the dependencies + if (expected.CodeElement.UserDefinedDataType != null && expected.TypeDefinition == null) + { + TypeCobolLinker.ResolveType(expected); + if (expected.TypeDefinition != null) + { + TypeCobolLinker.CheckCircularReferences(expected.TypeDefinition); + } + } if (c < callArgsCount) { //Omitted diff --git a/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs b/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs index ec9116646..34c2c0107 100644 --- a/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs +++ b/TypeCobol/Compiler/Diagnostics/TypeCobolLinker.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using JetBrains.Annotations; using TypeCobol.Compiler.CodeElements; using TypeCobol.Compiler.CodeElements.Expressions; using TypeCobol.Compiler.CodeModel; @@ -11,142 +12,344 @@ namespace TypeCobol.Compiler.Diagnostics { - public class TypeCobolLinker : AbstractAstVisitor + /// + /// TypeCobolLinker is responsible for: + /// - Resolve TypeDefinition. + /// - It's the only class that can call SymbolTable.GetType() and set the property TypeDefinition on DataDefinition node + /// - Check circular reference between types + /// - Create a path between type reference and TypeDefinition in the SymbolTable (property SymbolTable.TypesReferences) + /// - It's the only class that can update SymbolTable.TypesReferences + /// - This path is not set for unused types + /// + /// + /// What is the "path" between type ? (SymbolTable.TypesReferences) + /// ---------------------------------------------------------- + /// The path between type is used to search variables which are located under a typedef + /// and then find if a variable outside reference this type (directly or indirectly). + /// The path will link a DataDefinition that use the syntax (type XXXX) to a TypeDefinition. + /// + /// + /// Group1 TypeA TypeB TypeC + /// var1 A11 type typeB B1 C1 + /// var11 type typeA B2 C2 + /// B3 type typeC C3 + /// + /// + /// Eg, with this SymbolReference: + /// var11::A11::B3::C3 + /// + /// We'll first find C3 under TypeC, then we need to know all DataDefinition that references TypeC. + /// So the path must start with: TypeC->B3 + /// Then again, after B3 is found under TypeB, we need to find DataDefinition that references TypeB. + /// The path must continue with: TypeB->A11 + /// ... + /// At the end, the full path path will be: TypeC->B3, TypeB->A11, TypeA->var11 + /// As var11 is a variable outside a TypeDef, the path ends here. + /// + /// With the same SymbolReference but fully qualified: + /// var11::A11::B1::B2::B3::C1::C2::C3 + /// The path will be exactly the same. + /// The path doesn't contains intermediate group-item. + /// + /// + /// Now when there are more Data/Definition/TypeDef + /// + /// Group1 TypeA TypeB TypeC + /// var1 A11 type typeB B1 C1 + /// var11 type typeA B2 C2 + /// B3 type typeC C3 + /// + /// Group2 TypeZ + /// var22 Z11 type typeB + /// var222 type typeZ + /// + /// With a SymbolReference with Group2 it's the same logic. + /// var222::Z11::B3::C3 + /// The path will be: + /// TypeC->B3, TypeB->Z11, TypeZ->var222 + /// + /// + /// + /// How the path is calculated/stored ? (SymbolTable.TypesReferences) + /// ------------------------------------------------------------------ + /// The path will always contains a DataDefinition outside a Type. + /// So we use the SymbolTable of this DataDefinition to store the full path. + /// + /// In the previous example, if Group1 and Group2 are in 2 different SymbolTable, it means the full path will be stored twice. + /// It takes more time to construct the path but it ensure that when we use a path we'll always be sure to find variables accessible/visible to our SymbolTable. + /// + /// + /// If all paths were common to all SymbolTable, then if Group1 and Group2 are in 2 different programs, there would be 2 downsides: + /// - We'll spend time using path that takes us outside our visibility scope. + /// - It's not a real problem on our unit test, but on big programs with a lot of dependencies this would be an issue. + /// - At the end we need to check if the variable is accessible to our scope + /// + /// + /// + /// Optimization on path storage (SymbolTable.TypesReferences) + /// ------------------------------------------------------------ + /// When 2 or more variable inside a typedef reference the same type, TypeCobolLinker will detect that and only store the path once. + /// + /// + /// + public class TypeCobolLinker { - public override bool BeginNode(Node node) - { - return base.BeginNode(node); - } - public override bool BeginCodeElement(CodeElement codeElement) - { - return false; //We don't want to check deeper than Node - } - public override bool Visit(DataDescription dataEntry) - { - TypeReferencer(dataEntry, dataEntry.SymbolTable); - return true; - } - public override bool Visit(Paragraph paragraph) + /// + /// This method will do the 3 actions: + /// - Resolve TypeDefinition + /// - Check circular references + /// - Create a path between type reference and TypeDefinition + /// + /// Variable outside that use the "type" syntax + /// Typedef that need all its typed children to be resolved (only use case for now is "Depending on") + public static void LinkedTypedVariables([NotNull][ItemNotNull] in List typedVariablesOutsideTypedef, + [NotNull][ItemNotNull] in List typeThatNeedTypeLinking) { - return false; - } + //Stack to detect circular reference between types + Stack currentlyCheckedTypedefStack = new Stack(); - public override bool Visit(Section section) - { - return false; - } + foreach (var dataDefinition in typedVariablesOutsideTypedef) + { + //Warning, when you ask to parse multiple input files with CLI, then TypeDefinition of typedVariablesOutsideTypedef can already be resolved - public override bool Visit(EnvironmentDivision environmentDivision) - { - return false; - } - public override bool IsStopVisitingChildren => false; + if (ResolveType(dataDefinition)) //If type has been found in SymbolTable + { + //Reference the path between the typedDataDefChild and its TypeDefinition on the SymbolTable on the original DataDefinition outside a typedef + if (dataDefinition.SymbolTable.TypesReferences.TryGetValue(dataDefinition.TypeDefinition, out var dataDefsThatReferencedThisType)) + { + //Link between the type and the dataDefinition cannot already be done + System.Diagnostics.Debug.Assert(!dataDefsThatReferencedThisType.Contains(dataDefinition)); - /// - /// Do not visit children of IF. - /// Just in case there are statements directly under the procedure division. - /// - public override bool Visit(If _) => false; + //Type already referenced in our SymbolTable, it means all further typed children of the type have already been linked into this SymbolTable + dataDefsThatReferencedThisType.Add(dataDefinition); + } + else + { + dataDefinition.SymbolTable.TypesReferences.Add(dataDefinition.TypeDefinition, new List { dataDefinition }); - /// - /// Do not visit children of Evaluate. - /// Just in case there are statements directly under the procedure division. - /// - public override bool Visit(Evaluate _) => false; + //First time this TypeDefinition is added to dataDefinition.SymbolTable, then link all children of the type + LinkTypedChildren(dataDefinition.TypeDefinition, currentlyCheckedTypedefStack, dataDefinition.SymbolTable); + } + } + } - /// - /// Do not visit children of Perform. - /// Just in case there are statements directly under the procedure division. - /// - public override bool Visit(Perform _) => false; - public override bool Visit(Declaratives declaratives) - { - return false; + //Now link type that use depending On + System.Diagnostics.Debug.Assert(currentlyCheckedTypedefStack.Count == 0, "Stack must be empty"); + foreach (var typeDefinition in typeThatNeedTypeLinking) + { + LinkTypedChildren(typeDefinition, currentlyCheckedTypedefStack, typeDefinition.SymbolTable); + } } - public override bool Visit(ProcedureDivision procedureDivision) - { - var parent = procedureDivision.Parent as Program; - return parent != null && base.Visit(procedureDivision); - } - public override bool Visit(FunctionDeclaration funcDeclare) + public static void CheckCircularReferences([NotNull] TypeDefinition typeDefinition) { - //Get Function declaration parameters - var parameters = funcDeclare.Profile.Parameters; - if(funcDeclare.Profile.ReturningParameter != null) - parameters.Add(funcDeclare.Profile.ReturningParameter); - - foreach (var dataEntry in parameters) + + if (typeDefinition.TypedChildren.Count == 0 //no typed children + || typeDefinition.TypedChildren[0] == null //TypeDefinition of first children could not be resolved + || typeDefinition.TypedChildren[0].TypeDefinition != null) //TypeDefinition of first children already resolved { - TypeReferencer(dataEntry, dataEntry.SymbolTable); + //In these case, nothing to do because no children or job has already be done + return; } - return base.Visit(funcDeclare); + //Stack to detect circular reference between types + Stack currentlyCheckedTypedefStack = new Stack(); + LinkTypedChildren(typeDefinition, currentlyCheckedTypedefStack); } - private void TypeReferencer(DataDescription dataEntry, SymbolTable symbolTable) + /// + /// This method will do the 3 actions: + /// - Resolve TypeDefinition + /// - Check circular references + /// - Create a path between type reference and TypeDefinition + /// + /// + /// + /// + /// + private static void LinkTypedChildren([NotNull] TypeDefinition typeDefinition, + [CanBeNull] Stack currentlyCheckedTypedefStack0, [CanBeNull] SymbolTable symbolTable = null) { - if (symbolTable == null) return; - var types = symbolTable.GetType(dataEntry.DataType); - if (types.Count != 1) + + if (typeDefinition.TypedChildren.Count == 0) { - //Check the existing children, if they use a type - foreach (var child in dataEntry.Children) - { - if (child is DataDescription childDataDesc) - TypeReferencer(childDataDesc, symbolTable); - } return; } - var type = types.First(); - dataEntry.TypeDefinition = type; //Set the TypeDefinition on DataDefinition Node so to avoid symbolTable access + currentlyCheckedTypedefStack0?.Push(typeDefinition); + LinkTypedChildren0(currentlyCheckedTypedefStack0); + currentlyCheckedTypedefStack0?.Pop(); + + - //Check circular type reference - if (dataEntry.IsPartOfATypeDef) + //Only reason to use private method here, is because there are multiple return path in LinkTypedChildren0. + //So we can easily handle currentlyCheckedTypedefStack push/pop just above + void LinkTypedChildren0(Stack currentlyCheckedTypedefStack) { - var circularRefInsideChildren = type.Children.Any(c => + //If all typed children of typedef are resolved it means this typedef has already been fully linked + if (typeDefinition.TypedChildren[typeDefinition.TypedChildren.Count - 1] == null || typeDefinition.TypedChildren[typeDefinition.TypedChildren.Count - 1]?.TypeDefinition != null) { - DataDefinition dataChild = (DataDefinition)c; - var childrenType = symbolTable.GetType(dataChild.DataType).FirstOrDefault(); - if (childrenType == null) return false; - return dataEntry.ParentTypeDefinition == childrenType; //Circular reference detected will return true - }); + //If symbolTable is null, it means we don't want to register link between TypeDefinition and DataDefinition that use it + //So we can stop here. + if (symbolTable == null) + { + return; + } + + currentlyCheckedTypedefStack = null; //As typedef has already been linked, then no need to check circular reference + } - if (type == dataEntry.ParentTypeDefinition || circularRefInsideChildren) + for (var i = 0; i < typeDefinition.TypedChildren.Count; i++) { - DiagnosticUtils.AddError(dataEntry, "Type circular reference detected", - dataEntry.CodeElement, code: MessageCode.SemanticTCErrorInParser); - return; //Do not continue to prevent further work/crash with circular references + var typedDataDefChild = typeDefinition.TypedChildren[i]; + //If a typedDataDefChild is null it means is TypeDefinition could not be resolved or is part of a circular reference. + //So let's continue to the next child. + if (typedDataDefChild == null) + { + continue; + } + + + //Resolve type of typedDataDefChild if not already done yet + if (typedDataDefChild.TypeDefinition == null) + { + if (!ResolveType(typedDataDefChild)) //Use the symbolTable of this typedDataDefChild to resolve the type + { + //No TypeDefinition found + typeDefinition.TypedChildren[i] = null; //set to null so we don't try to check its TypeDefinition another time. + continue; //Go to the next typed child + } + } + + + System.Diagnostics.Debug.Assert(typedDataDefChild.TypeDefinition != null); //type must be resolved now + + + //Detect circular reference + if (currentlyCheckedTypedefStack?.Contains(typedDataDefChild.TypeDefinition) == true) + { + typeDefinition.TypedChildren[i] = null; //set this typed child to null so we don't enter this infinite loop another time + DiagnosticUtils.AddError(typedDataDefChild, "Type circular reference detected : " + + string.Join(" -> ", currentlyCheckedTypedefStack.Select(t => t.Name)), code: MessageCode.SemanticTCErrorInParser); + + continue;//Go to the next typedChildren + //Do not make the link in symbolTable.TypesReferences with method ReferenceThisDataDefByThisType to avoid infinite loop in SymbolTable + } + + + if (symbolTable != null)//If we need to keep references between typed variable and type + { + //Reference the path between the typedDataDefChild and its TypeDefinition on the SymbolTable on the original DataDefinition outside a typedef + if (!ReferenceThisDataDefByThisType(symbolTable, typedDataDefChild)) + { + continue; //Type was already linked, so stop here + } + } + + + //Continue to link children of typedDataDefChild.TypeDefinition + LinkTypedChildren(typedDataDefChild.TypeDefinition, currentlyCheckedTypedefStack, symbolTable); } } + } - if (dataEntry.CodeElement.IsGlobal) - symbolTable = symbolTable.GetTableFromScope(SymbolTable.Scope.Global); + /// + /// Lookup the TypeDefinition from the DataType of this dataDefinition. + /// If no TypeDefinition can be found, then property TypeDefinition stay null. + /// + /// + /// true if type has been resolved + public static bool ResolveType([NotNull] in DataDefinition dataDefinition) + { + //Note : dataDefinition.CodeElement cannot be null, only Index have a null CodeElement and Index cannot be typed - - if (symbolTable.TypesReferences.ContainsKey(type)) //If datatype already exists, add ref to the list + + //Special hack until Visibility are fixed (#1081 and #938) + //As "Global" and "GlobalStorage" Scopes are above "Declarations" they cannot have access to "Declarations" scope. + //So types in "Declarations" scope cannot be reached from the SymbolTable of a variable declared as global or a variable inside global-storage. + + //But a variable NOT global and not in global-storage can access the SymbolTable "Declarations" and "Global". + //So if we are in scopes "Global" or "GlobalStorage" we first need to retrieve a SymbolTable under "Declarations" and resolve the type using this SymbolTable. + List types; + SymbolTable declarationsSymbolTable; + if (dataDefinition.SymbolTable.CurrentScope <= SymbolTable.Scope.Global) { - if (!symbolTable.TypesReferences[type].Contains(dataEntry)) - symbolTable.TypesReferences[type].Add(dataEntry); + //Retrieve the Scope Declarations by retrieving the SymbolTable of the program which is of Scope "Program". + //Then use the EnclosingScope which is of scope "Declarations" + declarationsSymbolTable = dataDefinition.GetProgramNode().SymbolTable.EnclosingScope; } else { - symbolTable.TypesReferences.Add(type, new List {dataEntry}); + declarationsSymbolTable = dataDefinition.SymbolTable; } + System.Diagnostics.Debug.Assert(declarationsSymbolTable.CurrentScope >= SymbolTable.Scope.Declarations, "Scope of SymbolTable must be under Declarations until (#1081 and #938) are fixed"); + + types = declarationsSymbolTable.GetType(dataDefinition.CodeElement.DataType); + //End special hack - //Also add all the typedChildren to reference list - foreach (var dataDescTypeChild in type.Children.Where(c => c is DataDescription)) + //When (#1081 and #938) are fixed, remove the hack above and simply use the following line: + //var types = dataDefinition.SymbolTable.GetType(dataDefinition.CodeElement.DataType); + + + + if (types.Count < 1) + { + string message = "TYPE \'" + dataDefinition.CodeElement.DataType + "\' is not referenced"; + DiagnosticUtils.AddError(dataDefinition, message, MessageCode.SemanticTCErrorInParser); + } + else if (types.Count > 1) { - TypeReferencer(dataDescTypeChild as DataDescription, symbolTable); + string message = "Ambiguous reference to TYPE \'" + dataDefinition.CodeElement.DataType + "\'"; + DiagnosticUtils.AddError(dataDefinition, message, MessageCode.SemanticTCErrorInParser); } + else + { + dataDefinition.TypeDefinition = types[0]; + dataDefinition.DataType.RestrictionLevel = types[0].DataType.RestrictionLevel; + return true; + } + + return false; } - + + /// + /// + /// + /// + /// Data definition that reference a Type. + /// true if reference has been set. False is reference was already made + private static bool ReferenceThisDataDefByThisType([NotNull] in SymbolTable symbolTable, [NotNull] in DataDefinition dataDefinition) + { + //Reminder on symbolTable.TypesReferences Dictionary + //Key is TypeDefinition, Value is List + // + //TypesReferences can be understood/read as: + //TypeDefinition is referenced by these DataDefinitions + + + //Do NOT use the SymbolTable of the DataDefinition because we are linking all types references to the first + //dataDefinition outside typedef (see explanation at top of the class). + if (symbolTable.TypesReferences.TryGetValue(dataDefinition.TypeDefinition, out var dataDefsThatReferencedThisType)) + { + if (dataDefsThatReferencedThisType.Contains(dataDefinition)) + { + return false; + } + dataDefsThatReferencedThisType.Add(dataDefinition); + } + else + { + //Same here, don't use the SymbolTable of the DataDefinition + symbolTable.TypesReferences.Add(dataDefinition.TypeDefinition, new List { dataDefinition }); + } + + return true; + } } } diff --git a/TypeCobol/Compiler/Nodes/Data.cs b/TypeCobol/Compiler/Nodes/Data.cs index 338b508f1..9ab361cce 100644 --- a/TypeCobol/Compiler/Nodes/Data.cs +++ b/TypeCobol/Compiler/Nodes/Data.cs @@ -261,6 +261,7 @@ public TypeDefinition TypeDefinition get { return _typeDefinition; } set { + //Implementation note : Only TypeCobolLinker should set this value if (_typeDefinition == null) _typeDefinition = value; } @@ -815,7 +816,10 @@ public override bool VisitNode(IASTVisitor astVisitor) // [COBOL 2002] public class TypeDefinition: DataDefinition, Parent, IDocumentable { - public TypeDefinition([NotNull] DataTypeDescriptionEntry entry) : base(entry) { } + public TypeDefinition([NotNull] DataTypeDescriptionEntry entry) : base(entry) + { + TypedChildren = new List(); + } [NotNull] public new DataTypeDescriptionEntry CodeElement => (DataTypeDescriptionEntry) base.CodeElement; @@ -826,6 +830,17 @@ public override bool VisitNode(IASTVisitor astVisitor) return base.VisitNode(astVisitor) && astVisitor.Visit(this); } + /// + /// List of all children that reference a type. + /// Element of this list can be null if : + /// - the child reference an unknown type, it'll be set to null in this list. + /// - We detect a circular reference between type. To avoid infinite loop one link of the circular reference will be set to null. + /// + /// ProgramClassBuilder to initialize this list. + /// Only TypeCobolLinker can check the link and set items to null. + /// + [NotNull][ItemCanBeNull] + public List TypedChildren { get; } public override bool IsPartOfATypeDef => true; diff --git a/TypeCobol/Compiler/Parser/ProgramClassParserStep.cs b/TypeCobol/Compiler/Parser/ProgramClassParserStep.cs index 9eb3d07bc..19405b3b5 100644 --- a/TypeCobol/Compiler/Parser/ProgramClassParserStep.cs +++ b/TypeCobol/Compiler/Parser/ProgramClassParserStep.cs @@ -48,7 +48,10 @@ public static void PrepareCupParser() } } } - public static void CupParseProgramOrClass(TextSourceInfo textSourceInfo, ISearchableReadOnlyList codeElementsLines, TypeCobolOptions compilerOptions, SymbolTable customSymbols, PerfStatsForParserInvocation perfStatsForParserInvocation, out SourceFile root, out List diagnostics, out Dictionary nodeCodeElementLinkers) + public static void CupParseProgramOrClass(TextSourceInfo textSourceInfo, ISearchableReadOnlyList codeElementsLines, TypeCobolOptions compilerOptions, SymbolTable customSymbols, PerfStatsForParserInvocation perfStatsForParserInvocation, out SourceFile root, out List diagnostics, + out Dictionary nodeCodeElementLinkers, + out List typedVariablesOutsideTypedef, + out List typeThatNeedTypeLinking) { PrepareCupParser(); #if DEBUG_ANTRL_CUP_TIME @@ -89,8 +92,6 @@ public static void CupParseProgramOrClass(TextSourceInfo textSourceInfo, ISearch perfStatsForParserInvocation.OnStartTreeBuilding(); - //Create link between data definition an Types, will be stored in SymbolTable - root.AcceptASTVisitor(new TypeCobolLinker()); //Stop measuring tree building performance perfStatsForParserInvocation.OnStopTreeBuilding(); @@ -98,6 +99,8 @@ public static void CupParseProgramOrClass(TextSourceInfo textSourceInfo, ISearch // Register compiler results diagnostics = diagReporter.Diagnostics ?? new List(); nodeCodeElementLinkers = builder.NodeCodeElementLinkers; + typedVariablesOutsideTypedef = builder.TypedVariablesOutsideTypedef; + typeThatNeedTypeLinking = builder.TypeThatNeedTypeLinking; if (programClassBuilderError != null) { @@ -105,8 +108,12 @@ public static void CupParseProgramOrClass(TextSourceInfo textSourceInfo, ISearch } } - public static void CrossCheckPrograms(SourceFile root) + public static void CrossCheckPrograms(SourceFile root, TemporarySemanticDocument temporarySemanticDocument) { + //Create link between data definition an Types, will be stored in SymbolTable + TypeCobolLinker.LinkedTypedVariables(temporarySemanticDocument.TypedVariablesOutsideTypedef, + temporarySemanticDocument.TypeThatNeedTypeLinking); + //Complete some information on Node and run checker that need a full AST root.AcceptASTVisitor(new CrossCompleteChecker()); } diff --git a/TypeCobol/Compiler/Parser/TemporarySemanticDocument.cs b/TypeCobol/Compiler/Parser/TemporarySemanticDocument.cs index 0d81b4e17..865b163cf 100644 --- a/TypeCobol/Compiler/Parser/TemporarySemanticDocument.cs +++ b/TypeCobol/Compiler/Parser/TemporarySemanticDocument.cs @@ -12,7 +12,11 @@ namespace TypeCobol.Compiler.Parser public class TemporarySemanticDocument : ICompilerStepDocumentSnapshot { - public TemporarySemanticDocument(CodeElementsDocument previousSnapShot, DocumentVersion codeElementsLinesVersion, ISearchableReadOnlyList codeElementsLines, SourceFile root, [NotNull] List diagnostics, Dictionary nodeCodeElementLinkers) + public TemporarySemanticDocument(CodeElementsDocument previousSnapShot, DocumentVersion codeElementsLinesVersion, + ISearchableReadOnlyList codeElementsLines, SourceFile root, [NotNull] List diagnostics, + Dictionary nodeCodeElementLinkers, + List typedVariablesOutsideTypedef, + List typeThatNeedTypeLinking) { PreviousStepSnapshot = previousSnapShot; Root = root; @@ -21,11 +25,21 @@ public TemporarySemanticDocument(CodeElementsDocument previousSnapShot, Document TextSourceInfo = previousSnapShot.TextSourceInfo; CurrentVersion = codeElementsLinesVersion; Lines = codeElementsLines; + TypedVariablesOutsideTypedef = typedVariablesOutsideTypedef; + TypeThatNeedTypeLinking = typeThatNeedTypeLinking; } public TextSourceInfo TextSourceInfo { get; set; } public SourceFile Root { get; private set; } public Dictionary NodeCodeElementLinkers { get; private set; } + + + [NotNull] [ItemNotNull] + public List TypedVariablesOutsideTypedef { get; } + + [NotNull][ItemNotNull] + public List TypeThatNeedTypeLinking { get; } + /// /// Errors found while parsing Program or Class ///