Step 4.8: Structural Analysis¶
Purpose¶
The Structural Analysis step parses student submission files into Abstract Syntax Trees (ASTs) using ast-grep. This step runs after AI Batch (if applicable) and before Grade, enabling subsequent test functions to perform structural pattern matching on the source code without re-parsing files multiple times.
How It Works¶
- Detect Language — The step identifies the submission language (Python, Java, Node.js, C++, or C).
- Apply Evaluation Scope — When the submission includes an
EvaluationScope, only filenames inscoped_filesare considered. Without a scope, all files are considered for backward compatibility. - Heuristic File Filtering — It identifies files likely to contain source code, skipping binary files, images, and non-code configurations (e.g.,
.png,.json,.yaml,.md). - Parse ASTs — For each identified code file, it uses
ast-grep-pyto parse the content into anSgRootobject. - Store Results — Parsed roots and any caller-supplied changed-line sets are stored in
StructuralAnalysisResult.
If ast-grep-py is not installed or the language is not supported, the step logs a warning and proceeds with an empty result set, allowing the pipeline to continue.
Dependencies¶
| Step | What It Needs |
|---|---|
| Bootstrap | The raw Submission object containing files and language metadata |
Input¶
| Source | Data |
|---|---|
| Pipeline | pipeline_exec.submission → submission files, language, and optional evaluation scope |
Output¶
| Field | Type | Description |
|---|---|---|
data |
StructuralAnalysisResult |
Contains filename-keyed roots and changed_lines mappings |
status |
StepStatus.SUCCESS |
Usually succeeds even if parsing fails for some files (stores None for those files) |
How It Integrates with Grade¶
The GradeStep reads the STRUCTURAL_ANALYSIS step result from the pipeline and passes it as structural_analysis to GraderService.grade_from_tree(). The grader threads this object through the tree traversal into every process_test() call, which forwards it as a kwarg to test_function.execute().
Test functions like ForbiddenKeywordTest can then use this pre-computed AST
to perform efficient queries using ast-grep's pattern matching syntax.
Contribution-aware tests can use StructuralAnalysisResult.changed_lines to
limit findings to added or modified lines.
Key Design Decisions¶
- Pre-computed ASTs — Parsing is done once at the pipeline level rather than inside individual tests to ensure efficiency when multiple structural tests are defined.
- Optional scope —
EvaluationScopeselects the files to parse without introducing platform-specific concepts into the core engine. An explicit empty scope parses no files. - Changed-line passthrough — Changed lines are typed because pipeline and test implementations can act on them; opaque domain context remains in
SubmissionFile.metadata. - Fail-safe — If parsing fails for a specific file (e.g., due to syntax errors), the step stores
Nonefor that file instead of failing the entire pipeline. - Library Choice —
ast-grepwas chosen for its performance (Rust-based) and its ability to provide high-level, language-agnostic pattern matching queries.
Source Files¶
| File | Contents |
|---|---|
autograder/steps/structural_analysis_step.py |
The StructuralAnalysisStep pipeline step |
autograder/models/dataclass/structural_analysis_result.py |
Data container for parsed AST roots |
Next Step¶
After structural analysis is complete, the pipeline proceeds to Step 5: Grade to execute all tests.