Skip to content

Commit

Permalink
enable break and label diagnostic
Browse files Browse the repository at this point in the history
  • Loading branch information
CppCXY committed Apr 7, 2024
1 parent 74ca695 commit 35b098a
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 93 deletions.
1 change: 1 addition & 0 deletions .idea/.idea.EmmyLuaAnalyzer/.idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

namespace EmmyLua.CodeAnalysis.Compilation.Analyzer.FlowAnalyzer.ControlFlow;

// many bug need fix
public class CfgBuilder
{
private ControlFlowGraph _graph = null!;

private Dictionary<CfgNode, CfgNode> _loopExits = new();

private Dictionary<CfgNode, Dictionary<string, CfgNode>> _scopeLabels = new();
private Dictionary<string, CfgNode> _scopeLabels = new();

private Stack<CfgNode> _loops = new();

private class GotoNode(CfgNode source, LuaGotoStatSyntax gotoStat)
{
Expand All @@ -25,36 +26,24 @@ public ControlFlowGraph Build(LuaBlockSyntax block)
{
_graph = new ControlFlowGraph();
var lastNode = BuildBlock(block, _graph.EntryNode);
RecordScope(_graph.EntryNode);
if (lastNode != _graph.ExitNode)
{
_graph.AddEdge(lastNode, _graph.ExitNode);
}

foreach (var gotoNode in _gotoNodes)
{
var scopeNode = FindScopeStart(gotoNode.Source);
if (scopeNode is not null)
if (gotoNode.GotoStat.LabelName is { } label)
{
if (_scopeLabels.TryGetValue(scopeNode, out var labels))
if (_scopeLabels.TryGetValue(label.RepresentText, out var target))
{
if (gotoNode.GotoStat.LabelName is { } label)
{
if (labels.TryGetValue(label.RepresentText, out var target))
{
_graph.AddEdge(gotoNode.Source, target);
}
else
{
// label.PushDiagnostic(DiagnosticSeverity.Error, $"No Visible label {label.RepresentText}");
}
}

continue;
_graph.AddEdge(gotoNode.Source, target);
}
else
{
label.PushDiagnostic(DiagnosticSeverity.Error, $"No Visible label {label.RepresentText}");
}
}

// gotoNode.GotoStat.Goto.PushDiagnostic(DiagnosticSeverity.Error, "Label not found");
}

var graph = _graph;
Expand Down Expand Up @@ -91,7 +80,7 @@ private CfgNode BuildBlock(LuaBlockSyntax block, CfgNode firstCfgNode)
case LuaReturnStatSyntax returnStat:
{
currentBlock = BuildReturn(returnStat, currentBlock);
if (stat != block.StatList.Last())
if (!stat.Equals(block.StatList.Last()))
{
currentBlock = _graph.CreateNode();
}
Expand Down Expand Up @@ -160,9 +149,15 @@ private CfgNode BuildIf(LuaIfStatSyntax ifStat, CfgNode sourceBlock)
lastBlocks.Add(BuildBlock(ifStat.ThenBlock, thenBlock));
}

var existElseClause = false;
foreach (var elseIfOrElseClause in ifStat.IfClauseStatementList)
{
var elseIfCondition = elseIfOrElseClause.Condition;
if (elseIfCondition is null)
{
existElseClause = true;
}

var elseIfBlock = _graph.CreateNode();
_graph.AddEdge(sourceBlock, elseIfBlock, elseIfCondition);
if (elseIfOrElseClause.Block is not null)
Expand All @@ -172,6 +167,11 @@ private CfgNode BuildIf(LuaIfStatSyntax ifStat, CfgNode sourceBlock)
}

var nextBlock = _graph.CreateNode();
if (!existElseClause)
{
_graph.AddEdge(sourceBlock, nextBlock);
}

foreach (var lastBlock in lastBlocks)
{
if (lastBlock != _graph.ExitNode)
Expand All @@ -186,77 +186,87 @@ private CfgNode BuildIf(LuaIfStatSyntax ifStat, CfgNode sourceBlock)
private CfgNode BuildWhile(LuaWhileStatSyntax whileStat, CfgNode sourceBlock)
{
var condition = whileStat.Condition;
var loopBlock = _graph.CreateNode(CfgNodeKind.Loop);
_graph.AddEdge(sourceBlock, loopBlock, condition);
var loopNode = _graph.CreateNode(CfgNodeKind.Loop);
_loops.Push(loopNode);
_graph.AddEdge(sourceBlock, loopNode, condition);
var bodyBlock = _graph.CreateNode();
_graph.AddEdge(loopBlock, bodyBlock);
_graph.AddEdge(loopNode, bodyBlock);

var nextBlock = _graph.CreateNode();
_loopExits.Add(loopBlock, nextBlock);
_graph.AddEdge(sourceBlock, nextBlock);
_loopExits.Add(loopNode, nextBlock);
if (whileStat.Block is not null)
{
var lastBlock = BuildBlock(whileStat.Block, bodyBlock);
_graph.AddEdge(lastBlock, loopBlock);
_graph.AddEdge(lastBlock, loopNode);
_graph.AddEdge(lastBlock, nextBlock);
}

_loops.Pop();
return nextBlock;
}

private CfgNode BuildRepeat(LuaRepeatStatSyntax repeatStat, CfgNode sourceBlock)
{
var loopBlock = _graph.CreateNode(CfgNodeKind.Loop);
_graph.AddEdge(sourceBlock, loopBlock);
var loopNode = _graph.CreateNode(CfgNodeKind.Loop);
_loops.Push(loopNode);
_graph.AddEdge(sourceBlock, loopNode);
var bodyBlock = _graph.CreateNode();
_graph.AddEdge(loopBlock, bodyBlock);
_graph.AddEdge(loopNode, bodyBlock);

var nextBlock = _graph.CreateNode();
_loopExits.Add(loopBlock, nextBlock);
_loopExits.Add(loopNode, nextBlock);
if (repeatStat.Block is not null)
{
var lastBlock = BuildBlock(repeatStat.Block, bodyBlock);
_graph.AddEdge(lastBlock, loopBlock, repeatStat.Condition);
_graph.AddEdge(lastBlock, loopNode, repeatStat.Condition);
_graph.AddEdge(lastBlock, nextBlock);
}

_loops.Pop();
return nextBlock;
}

private CfgNode BuildForStat(LuaForStatSyntax forStat, CfgNode sourceBlock)
{
var loopBlock = _graph.CreateNode(CfgNodeKind.Loop);
_graph.AddEdge(sourceBlock, loopBlock);
var loopNode = _graph.CreateNode(CfgNodeKind.Loop);
_loops.Push(loopNode);
_graph.AddEdge(sourceBlock, loopNode);
var bodyBlock = _graph.CreateNode();
_graph.AddEdge(loopBlock, bodyBlock);
_graph.AddEdge(loopNode, bodyBlock);

var nextBlock = _graph.CreateNode();
_loopExits.Add(loopBlock, nextBlock);
_loopExits.Add(loopNode, nextBlock);
if (forStat.Block is not null)
{
var lastBlock = BuildBlock(forStat.Block, bodyBlock);
_graph.AddEdge(lastBlock, loopBlock);
_graph.AddEdge(lastBlock, loopNode);
_graph.AddEdge(lastBlock, nextBlock);
}

_loops.Pop();
return nextBlock;
}

private CfgNode BuildForRangeStat(LuaForRangeStatSyntax forRangeStat, CfgNode sourceBlock)
{
var loopBlock = _graph.CreateNode(CfgNodeKind.Loop);
_graph.AddEdge(sourceBlock, loopBlock);
var loopNode = _graph.CreateNode(CfgNodeKind.Loop);
_loops.Push(loopNode);
_graph.AddEdge(sourceBlock, loopNode);
var bodyBlock = _graph.CreateNode();
_graph.AddEdge(loopBlock, bodyBlock);
_graph.AddEdge(loopNode, bodyBlock);

var nextBlock = _graph.CreateNode();
_loopExits.Add(loopBlock, nextBlock);
_graph.AddEdge(sourceBlock, nextBlock);
_loopExits.Add(loopNode, nextBlock);
if (forRangeStat.Block is not null)
{
var lastBlock = BuildBlock(forRangeStat.Block, bodyBlock);
_graph.AddEdge(lastBlock, loopBlock);
_graph.AddEdge(lastBlock, loopNode);
_graph.AddEdge(lastBlock, nextBlock);
}

_loops.Pop();
return nextBlock;
}

Expand All @@ -269,16 +279,16 @@ private CfgNode BuildReturn(LuaReturnStatSyntax returnStat, CfgNode sourceBlock)

private CfgNode BuildBreak(LuaBreakStatSyntax breakStat, CfgNode sourceBlock)
{
var loop = sourceBlock;
while (loop is { Kind: not CfgNodeKind.Loop } node)
CfgNode? loop = null;
if (_loops.Count != 0)
{
loop = _graph.GetPredecessors(node).FirstOrDefault();
loop = _loops.Peek();
}

var nextBlock = _graph.CreateNode();
if (loop is null)
{
// breakStat.PushDiagnostic(DiagnosticSeverity.Error, "Break statement outside of loop");
breakStat.PushDiagnostic(DiagnosticSeverity.Error, "Break statement outside of loop");
return nextBlock;
}

Expand All @@ -290,37 +300,15 @@ private CfgNode BuildBreak(LuaBreakStatSyntax breakStat, CfgNode sourceBlock)
return nextBlock;
}

private CfgNode? FindScopeStart(CfgNode? node)
{
while (node is not null && !_scopeLabels.ContainsKey(node))
{
node = _graph.GetPredecessors(node).FirstOrDefault();
}

return node;
}

private void RecordScope(CfgNode scopeNode)
{
if (!_scopeLabels.ContainsKey(scopeNode))
{
_scopeLabels[scopeNode] = new();
}
}

private CfgNode BuildLabel(LuaLabelStatSyntax labelStat, CfgNode sourceBlock)
{
var labelBlock = _graph.CreateNode(CfgNodeKind.Label);
_graph.AddEdge(sourceBlock, labelBlock);
if (labelStat.Name is not null)
if (labelStat.Name is {RepresentText: {} name })
{
var scopeNode = FindScopeStart(sourceBlock);
if (scopeNode is not null)
if (!_scopeLabels.TryAdd(name, labelBlock))
{
if (_scopeLabels.TryGetValue(scopeNode, out var labels))
{
labels[labelStat.Name.RepresentText] = labelBlock;
}
labelStat.PushDiagnostic(DiagnosticSeverity.Error, $"Label {name} already defined");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using EmmyLua.CodeAnalysis.Syntax.Node.SyntaxNodes;

namespace EmmyLua.CodeAnalysis.Compilation.Analyzer.FlowAnalyzer.ControlFlow;
namespace EmmyLua.CodeAnalysis.Compilation.Analyzer.FlowAnalyzer.ControlFlow;

public readonly struct CfgEdge(int sourceIndex, int targetIndex)
{
Expand Down
6 changes: 3 additions & 3 deletions EmmyLua/CodeAnalysis/Document/SourceRange.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
namespace EmmyLua.CodeAnalysis.Document;

public struct SourceRange(int startOffset = 0, int length = 0)
public readonly struct SourceRange(int startOffset = 0, int length = 0)
{
public static SourceRange Empty = new();

public int StartOffset { get; set; } = startOffset;
public int Length { get; set; } = length;
public int StartOffset { get; init; } = startOffset;
public int Length { get; init; } = length;

public int EndOffset => StartOffset + Length;

Expand Down
17 changes: 17 additions & 0 deletions EmmyLua/CodeAnalysis/Syntax/Node/LuaSyntaxElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,23 @@ public IEnumerable<T> NextOfType<T>()
return token?.Parent as LuaSyntaxNode;
}

public LuaSyntaxNode? NameNodeAt(int line, int col)
{
var token = TokenAt(line, col);
if (token is null)
{
return null;
}

if (token is LuaNameToken or LuaNumberToken or LuaStringToken)
{
return token.Parent as LuaSyntaxNode;
}

token = TokenLeftBiasedAt(line, col);
return token?.Parent as LuaSyntaxNode;
}

public LuaSyntaxNode? FindNode(SourceRange range, LuaSyntaxKind kind)
{
LuaSyntaxNode? node = this as LuaSyntaxNode;
Expand Down
1 change: 0 additions & 1 deletion EmmyLua/CodeAnalysis/Workspace/LuaFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public class LuaFeatures

public Dictionary<string, string> VirtualModule { get; set; } = new()
{
{ "socket.core", "xxx.lua" }
};

public HashSet<string> RequireLikeFunction { get; set; } =
Expand Down
3 changes: 2 additions & 1 deletion EmmyLua/CodeAnalysis/Workspace/LuaWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ public LuaWorkspace(LuaFeatures features)

public void LoadWorkspace(string workspace, bool notFilter = false)
{
var excludeFolders = Features.ExcludeFolders.Select(it => Path.Combine(workspace, it)).ToList();
var files =
Features.Extensions.SelectMany(it => Directory.GetFiles(workspace, it, SearchOption.AllDirectories));
if (!notFilter)
{
files = files.Where(it => !Features.ExcludeFolders.Any(it.Contains));
files = files.Where(it => !excludeFolders.Any(it.StartsWith));
}

var documents =
Expand Down
3 changes: 2 additions & 1 deletion LanguageServer/Completion/CompletionHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using EmmyLua.CodeAnalysis.Workspace;
using LanguageServer.Configuration;
using LanguageServer.Util;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
Expand All @@ -7,7 +8,7 @@
namespace LanguageServer.Completion;

// ReSharper disable once ClassNeverInstantiated.Global
public class CompletionHandler(LuaWorkspace workspace) : CompletionHandlerBase
public class CompletionHandler(LuaWorkspace workspace, LuaConfig config) : CompletionHandlerBase

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, linux-x64, linux-x64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, osx-x64, darwin-x64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, osx-arm64, darwin-arm64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, win-x64, win32-x64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, linux-x64, linux-x64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, osx-x64, darwin-x64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, osx-arm64, darwin-arm64)

Parameter 'config' is unread.

Check warning on line 11 in LanguageServer/Completion/CompletionHandler.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, win-x64, win32-x64)

Parameter 'config' is unread.
{
private CompletionBuilder Builder { get; } = new();

Expand Down
Loading

0 comments on commit 35b098a

Please sign in to comment.