diff --git a/workspace-d/source/workspaced/com/dcdext.d b/workspace-d/source/workspaced/com/dcdext.d index a8ce4db..6cf3e87 100644 --- a/workspace-d/source/workspaced/com/dcdext.d +++ b/workspace-d/source/workspaced/com/dcdext.d @@ -884,6 +884,15 @@ class DCDExtComponent : ComponentWrapper if (token >= tokens.length || !tokens[token].isLikeIdentifier) return null; + Module _parsed; + RollbackAllocator rba; + Module parsed() + { + if (_parsed) + return _parsed; + return _parsed = parseModule(tokens, "stdin", &rba); + } + Related[] ret; switch (tokens[token].type) @@ -908,8 +917,6 @@ class DCDExtComponent : ComponentWrapper // if lister auto finder = new IfFinder(); finder.target = tokens[token].index; - RollbackAllocator rba; - auto parsed = parseModule(tokens, "stdin", &rba); finder.visit(parsed); foreach (ifToken; finder.foundIf) ret ~= Related(Related.Type.controlFlow, [ifToken.index, ifToken.index + ifToken.tokenText.length]); @@ -929,8 +936,6 @@ class DCDExtComponent : ComponentWrapper finder.isLoop = !(tokens[token].type == tok!"break" || tokens[token].type == tok!"continue"); if (token + 1 < tokens.length && tokens[token + 1].type == tok!"identifier") finder.label = tokens[token + 1].text; - RollbackAllocator rba; - auto parsed = parseModule(tokens, "stdin", &rba); finder.visit(parsed); if (finder.isLoop && finder.foundBlock.length) @@ -951,8 +956,6 @@ class DCDExtComponent : ComponentWrapper // switch/case lister auto finder = new SwitchFinder(); finder.target = tokens[token].index; - RollbackAllocator rba; - auto parsed = parseModule(tokens, "stdin", &rba); finder.visit(parsed); foreach (switchToken; finder.foundSwitch) ret ~= Related(Related.Type.controlFlow, [switchToken.index, switchToken.index + switchToken.tokenText.length]); @@ -961,8 +964,6 @@ class DCDExtComponent : ComponentWrapper // return effect lister auto finder = new ReturnFinder(); finder.target = tokens[token].index; - RollbackAllocator rba; - auto parsed = parseModule(tokens, "stdin", &rba); finder.visit(parsed); foreach (switchToken; finder.related) ret ~= Related(Related.Type.controlFlow, [switchToken.index, switchToken.index + switchToken.tokenText.length]); @@ -2149,6 +2150,9 @@ final class IfFinder : ASTVisitor alias visit = ASTVisitor.visit; + mixin ASTSearchScopeLimit!("target", FunctionBody); + mixin ASTFinisher; + static foreach (If; AliasSeq!(IfStatement, ConditionalStatement)) override void visit(const If ifStatement) { @@ -2279,6 +2283,7 @@ final class IfFinder : ASTVisitor if (v.index == target) { foundIf = currentIf; + exitVisitor = true; return; } } @@ -2340,6 +2345,9 @@ final class SwitchFinder : ASTVisitor alias visit = ASTVisitor.visit; + mixin ASTSearchScopeLimit!("target", FunctionBody); + mixin ASTFinisher; + override void visit(const SwitchStatement stmt) { if (foundSwitch.length) @@ -2404,6 +2412,7 @@ final class SwitchFinder : ASTVisitor if (v.index == target) { foundSwitch = currentSwitch; + exitVisitor = true; return; } } @@ -2457,6 +2466,9 @@ final class BreakFinder : ASTVisitor alias visit = ASTVisitor.visit; + mixin ASTSearchScopeLimit!("target", FunctionBody); + mixin ASTFinisher; + override void visit(const LabeledStatement stmt) { if (foundBlock.length) @@ -2622,6 +2634,7 @@ final class BreakFinder : ASTVisitor if (v.index == target) { foundBlock = currentBlock; + exitVisitor = true; return; } } diff --git a/workspace-d/source/workspaced/dparseext.d b/workspace-d/source/workspaced/dparseext.d index c24799e..eb6508d 100644 --- a/workspace-d/source/workspaced/dparseext.d +++ b/workspace-d/source/workspaced/dparseext.d @@ -434,3 +434,31 @@ C[] substr(C)(C[] s, size_t start, size_t end) return s[start .. start]; return s[start .. end]; } + +/// Overrides visit for each ASTScopes to only travese them when the ast node +/// tokens contain a token at index `mixin(member)` +mixin template ASTSearchScopeLimit(string member, ASTScopes...) +{ + static foreach (ASTScope; ASTScopes) + override void visit(const ASTScope n) + { + if (!n.tokens.length || + (mixin(member) >= n.tokens[0].index && mixin(member) <= n.tokens[$ - 1].index + n.tokens[$ - 1].textLength)) + super.visit(n); + } +} + +/// Defines a `exitVisitor` method, when it's set to true, almost all traversing +/// will be skipped. This is realized by overriding DeclarationOrStatement and +/// just returning if it's true. +mixin template ASTFinisher() +{ + bool exitVisitor; + + override void visit(const DeclarationOrStatement decl) + { + if (exitVisitor) + return; + super.visit(decl); + } +}