Skip to content

Commit b6f2868

Browse files
committed
#54 - update function analysis to track return statements and code path completeness
1 parent 7e5ba63 commit b6f2868

File tree

6 files changed

+39
-43
lines changed

6 files changed

+39
-43
lines changed

src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public AddressedInstructions Visit(FunctionDeclaration visitable)
139139
}
140140

141141
result.AddRange(visitable.Statements.Accept(This));
142-
if (!visitable.HasReturnStatement())
142+
if (!visitable.HasReturnStatement)
143143
result.Add(new Return());
144144

145145
result.Add(new EndBlock(BlockType.Function, blockId: functionInfo.ToString()), functionInfo.End.Name);

src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,22 @@ internal class DeclarationVisitor : VisitorNoReturnBase<IAbstractSyntaxTreeNode>
2020
private readonly ISymbolTableStorage _symbolTables;
2121
private readonly IAmbiguousInvocationStorage _ambiguousInvocations;
2222
private readonly IVisitor<TypeValue, Type> _typeBuilder;
23+
private readonly IVisitor<FunctionDeclaration, ReturnAnalyzerResult> _returnAnalyzer;
2324

2425
public DeclarationVisitor(
2526
IFunctionWithUndefinedReturnStorage functionStorage,
2627
IMethodStorage methodStorage,
2728
ISymbolTableStorage symbolTables,
2829
IAmbiguousInvocationStorage ambiguousInvocations,
29-
IVisitor<TypeValue, Type> typeBuilder)
30+
IVisitor<TypeValue, Type> typeBuilder,
31+
IVisitor<FunctionDeclaration, ReturnAnalyzerResult> returnAnalyzer)
3032
{
3133
_functionStorage = functionStorage;
3234
_methodStorage = methodStorage;
3335
_symbolTables = symbolTables;
3436
_ambiguousInvocations = ambiguousInvocations;
3537
_typeBuilder = typeBuilder;
38+
_returnAnalyzer = returnAnalyzer;
3639
}
3740

3841
public override VisitUnit Visit(IAbstractSyntaxTreeNode visitable)
@@ -71,6 +74,9 @@ public VisitUnit Visit(LexicalDeclaration visitable)
7174

7275
public VisitUnit Visit(FunctionDeclaration visitable)
7376
{
77+
var returnAnalyzerResult = visitable.Accept(_returnAnalyzer);
78+
visitable.ReturnStatements = returnAnalyzerResult.ReturnStatements;
79+
7480
var parentTable = _symbolTables[visitable.Parent.Scope];
7581
var indexOfFirstDefaultArgument = visitable.Arguments.AsValueEnumerable()
7682
.Select((x, i) => new { Argument = x, Index = i })
@@ -88,7 +94,8 @@ public VisitUnit Visit(FunctionDeclaration visitable)
8894
visitable.Name,
8995
parameters,
9096
visitable.ReturnTypeValue.Accept(_typeBuilder),
91-
visitable.IsEmpty);
97+
visitable.IsEmpty,
98+
returnAnalyzerResult.CodePathEndedWithReturn);
9299
if (functionSymbolId.Equals(parentTable.FindSymbol(functionSymbolId)?.Id))
93100
throw new OverloadAlreadyExists(visitable.Name, functionSymbolId);
94101

@@ -108,7 +115,7 @@ public VisitUnit Visit(FunctionDeclaration visitable)
108115
Type undefined = "undefined";
109116
if (functionSymbol.Type.Equals(undefined))
110117
{
111-
if (visitable.HasReturnStatement())
118+
if (visitable.HasReturnStatement)
112119
_functionStorage.Save(functionSymbol, visitable);
113120
else
114121
functionSymbol.DefineReturnType("void");

src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -491,37 +491,26 @@ public Type Visit(FunctionDeclaration visitable)
491491
_functionStorage.RemoveIfPresent(symbol);
492492
visitable.Statements.Accept(This);
493493

494-
var returnStatements = visitable.ReturnStatements
495-
.Select(x => new
496-
{
497-
Statement = x,
498-
Type = x.Accept(This)
499-
});
500494
Type undefined = "undefined";
501-
if (symbol.Type.Equals(undefined))
495+
HashSet<Type> returnTypes = [];
496+
for (var i = 0; i < visitable.ReturnStatements.Count; i++)
502497
{
503-
var returnStatementTypes = returnStatements
504-
.GroupBy(x => x.Type)
505-
.Select(x => x.Key)
506-
.ToList();
507-
if (returnStatementTypes.Count > 1)
498+
var returnStatementType = visitable.ReturnStatements[i].Accept(This);
499+
returnTypes.Add(returnStatementType);
500+
if (returnTypes.Count > 1 && symbol.Type.Equals(undefined))
508501
throw new CannotDefineType(visitable.Segment);
509-
symbol.DefineReturnType(returnStatementTypes.ElementAtOrDefault(0) ?? "void");
510-
}
511-
else
512-
{
513-
var wrongReturn = returnStatements
514-
.FirstOrDefault(x => !symbol.Type.Equals(x.Type));
515-
if (wrongReturn is not null)
502+
if (!symbol.Type.Equals(undefined) && !symbol.Type.Equals(returnStatementType))
516503
throw new WrongReturnType(
517-
wrongReturn.Statement.Segment,
504+
visitable.ReturnStatements[i].Segment,
518505
expected: symbol.Type,
519-
actual: wrongReturn.Type);
506+
actual: returnStatementType);
520507
}
521508

509+
if (symbol.Type.Equals(undefined))
510+
symbol.DefineReturnType(returnTypes.Single());
511+
522512
Type @void = "void";
523-
var hasReturnStatement = visitable.HasReturnStatement();
524-
if (!symbol.Type.Equals(@void) && !hasReturnStatement)
513+
if (!symbol.Type.Equals(@void) && !symbol.AllCodePathsEndedWithReturn)
525514
throw new FunctionWithoutReturnStatement(visitable.Segment);
526515

527516
if (symbol.Type is NullType)

src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/FunctionDeclaration.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ public partial class FunctionDeclaration : AfterTypesAreLoadedDeclaration
1818
public BlockStatement Statements { get; }
1919
public bool IsEmpty => Statements.Count == 0;
2020

21+
public IReadOnlyList<ReturnStatement> ReturnStatements { get; set; } = [];
22+
public bool HasReturnStatement => ReturnStatements.Count > 0;
23+
2124
public string ComputedFunctionAddress { get; set; } = string.Empty;
2225

2326
public FunctionDeclaration(
@@ -33,11 +36,6 @@ public FunctionDeclaration(
3336
Statements = blockStatement;
3437
Statements.Parent = this;
3538
Children = [Statements];
36-
37-
ReturnStatements = Statements
38-
.GetAllNodes()
39-
.OfType<ReturnStatement>()
40-
.ToArray();
4139
}
4240

4341
/// <summary>Стратегия "блока" - углубление скоупа</summary>
@@ -52,11 +50,6 @@ public override void InitScope(Scope? scope = null)
5250
ReturnTypeValue.Scope = Parent.Scope;
5351
}
5452

55-
public bool HasReturnStatement() =>
56-
ReturnStatements.Count > 0;
57-
58-
public IReadOnlyCollection<ReturnStatement> ReturnStatements { get; }
59-
6053
protected override string NodeRepresentation() =>
6154
ZString.Concat<string, char, string>("function", ' ', Name);
6255
}

src/Domain/HydraScript.Domain.IR/Impl/Symbols/FunctionSymbol.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ public class FunctionSymbol(
88
string name,
99
IReadOnlyList<ISymbol> parameters,
1010
Type type,
11-
bool isEmpty) : Symbol(name, type)
11+
bool isEmpty,
12+
bool allCodePathsEndedWithReturn) : Symbol(name, type)
1213
{
1314
private Type _returnType = type;
1415
/// <summary>Тип возврата функции</summary>
@@ -21,6 +22,7 @@ public class FunctionSymbol(
2122

2223
public IReadOnlyList<ISymbol> Parameters { get; } = parameters;
2324
public bool IsEmpty { get; } = isEmpty;
25+
public bool AllCodePathsEndedWithReturn { get; } = allCodePathsEndedWithReturn;
2426

2527
public void DefineReturnType(Type returnType) =>
2628
_returnType = returnType;

tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ public void StorageIsEmptyAfterFlushTest()
2121
name: FunctionName,
2222
parameters: [],
2323
"undefined",
24-
isEmpty: false);
24+
isEmpty: false,
25+
allCodePathsEndedWithReturn: false);
2526

2627
var decl = new FunctionDeclaration(
2728
name: new IdentifierReference(FunctionName),
@@ -70,27 +71,31 @@ public void StorageIsCorrectOrderTest()
7071
name: "key2",
7172
parameters: [],
7273
"undefined",
73-
isEmpty: false);
74+
isEmpty: false,
75+
allCodePathsEndedWithReturn: false);
7476

7577
storage.Save(new FunctionSymbol(
7678
name: "key1",
7779
parameters: [],
7880
"undefined",
79-
isEmpty: false), declaration: declarations[0]);
81+
isEmpty: false,
82+
allCodePathsEndedWithReturn: false), declaration: declarations[0]);
8083

8184
storage.Save(removable, declaration: declarations[1]);
8285

8386
storage.Save(new FunctionSymbol(
8487
name: "key3",
8588
parameters: [],
8689
"undefined",
87-
isEmpty: false), declaration: declarations[2]);
90+
isEmpty: false,
91+
allCodePathsEndedWithReturn: false), declaration: declarations[2]);
8892

8993
storage.Save(new FunctionSymbol(
9094
name: "key4",
9195
parameters: [],
9296
"undefined",
93-
isEmpty: false), declaration: declarations[3]);
97+
isEmpty: false,
98+
allCodePathsEndedWithReturn: false), declaration: declarations[3]);
9499

95100
storage.RemoveIfPresent(removable);
96101

0 commit comments

Comments
 (0)