abstract class Analyzer::Crystal::CrystalEngine

Direct Known Subclasses

Defined in:

analyzer/engines/crystal_engine.cr

Constant Summary

CLOSES_BLOCK_RE = /^end\b/
DEF_DECL_RE = /^(?:(?:private|protected)\s+)?def\s+(?:self\.)?([A-Za-z_]\w*[!?=]?)/
DO_WORD_RE = /\bdo\b/

Precompiled block-structure probes used by extract_crystal_do_block / extract_crystal_def_block on every body line. Constant shapes — load once, match many times. Cheap includes? gates reject the common non-structural body lines before any PCRE2 scan.

END_WORD_RE = /\bend\b/
HEREDOC_OPEN = /(?<!['"])<<-['"]?([A-Z_]\w*)/

Opening token of a Crystal heredoc: <<-DELIM, optionally quoted (<<-'SQL'). The delimiter must start with an uppercase letter or underscore — the universal Crystal convention (MD/HTML/SQL/EOF/…) — which keeps the matcher from firing on arr << -value. The negative lookbehind rejects a <<- that sits inside a string literal ("<<-MD"), so a stray quoted token can't open a phantom heredoc and blank the rest of the file.

HTTP_ROUTE_VERBS = ["get", "post", "put", "delete", "patch", "head", "options"] of ::String

Shared verb "/path" matchers for Kemal/Lucky/Grip-style DSLs. One combined PCRE2 scan replaces the previous 7–8 sequential per-verb .scan calls that ran on every source line. Leftmost match wins, which matches real one-verb-per-line route DSLs and the prior first-matching-verb behaviour for those lines. Optional extra covers framework-only verbs (Lucky's trace).

INTERPOLATION_RE = /\#\{([^}]+)\}/

Crystal "…" strings interpolate #{expr}. The Kemal/Lucky/ Amber/Marten/Grip route extractors capture the literal characters between quotes, so get "/api/#{VERSION}/items" came out as /api/#{VERSION}/items with the #{VERSION} leaking into the URL. Rewrite it as {name} so the path- parameter extractor picks it up and the URL template reads cleanly. Mirrors the Python f-string, Ruby #{}, and PHP $var fixes earlier this pass.

Most route literals have no interpolation; skip the PCRE2 gsub when the #\{ marker is absent.

NAMESPACE_DECL_RE = /^(?:abstract\s+)?(?:module|class|struct)\s+([A-Z]\w*(?:::[A-Z]\w*)*)/

Track the enclosing module/class by indentation rather than by counting do/end. Method bodies in real controllers are full of heredocs, {% … %} macros, and XML.build do … end blocks that throw off keyword-based depth counting and prematurely "close" the module, dropping every method defined after them. Indentation is the one signal those constructs can't corrupt: a namespace stays open until a line dedents to or past the column where it was declared.

Precompiled: this loop runs once per line of every .cr file when building the cross-file action index (--include-callee / --ai-context). The shapes never change, so hoist the two PCRE2 patterns to class constants.

SIMPLE_ROUTE_RE = /(?:^|[^.\w])(get|post|put|delete|patch|head|options|ws)\s*(?:\(\s*)?['"](.+?)['"]/

Group 1 = verb (or ws), group 2 = path. ws is folded in so a single scan covers WebSocket routes too; the caller maps ws → GET + protocol=ws.

STRUCT_OPEN_RE = /(?:^|=[^=>])\s*(?:if|unless|case|begin|while|until|for|class|module|def|macro)\b/

Instance Method Summary

Instance methods inherited from class Analyzer

analyze analyze, base_path : String base_path, base_paths : Array(String) base_paths, callees_needed? : Bool callees_needed?, http_header_name(name : String) : String | Nil http_header_name, line_number_for_index(content : String, char_index : Int32) : Int32 line_number_for_index, logger : NoirLogger logger, parallel_analyze(files : Array(String), &block : String -> Nil) parallel_analyze, read_file_content(path : String) : String read_file_content, result : Array(Endpoint) result, unique_params(params : Array(Param)) : Array(Param) unique_params, url : String url, web_root_path(path : String, markers : Array(String)) : String web_root_path

Constructor methods inherited from class Analyzer

new(options : Hash(String, YAML::Any)) new

Instance methods inherited from module FileHelper

all_files : Array(String) all_files, get_files_by_extension(extension : String) : Array(String) get_files_by_extension, get_files_by_extensions(extensions : Array(String)) : Array(String) get_files_by_extensions, get_files_by_prefix(prefix : String) : Array(String) get_files_by_prefix, get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String) get_files_by_prefix_and_extension, get_public_dir_files(base_path : String, folder : String) : Array(String) get_public_dir_files, get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String) get_public_files

Instance Method Detail

def analyze #

[View source]
abstract def analyze_file(path : String) : Array(Endpoint) #

[View source]