Skip to content

STYLE038 measures a function across files when a macro replaces its body with a qmacro_block from the macro's file (3-line function reported as 100 lines) #14

Closed

Description

@lookibed

Summary

STYLE038 (function length) measures a function as fn.body.at.last_line - fn.at.line + 1. When a function annotation replaces func.body with a block built by qmacro_block in the macro's own file, fn.body.at points into that file, and the lint subtracts a line number of one file from a line number of another: a three-line function in the user's file is reported as 100 lines long because the macro's quasi-block sits at line 101 of the macro file. Setting the generated block's at (and its statements') to func.at in the macro restores the measurement, so the rule is workable, but the number it reports is meaningless whenever a macro splices foreign line info into a body, and the lint fires on the user's code for something the macro did.

Repro

wrap_macro.das (lines 6 to 95 are ninety // padding comment lines, so that the quasi-block lands past line 80 of this file):

options gen2
module wrap_macro shared

require daslib/ast_boost
require daslib/templates_boost

// padding line 1 ... padding line 90 (ninety comment lines)

[function_macro(name = "wraps")]
class WrapsAnnotation : AstFunctionAnnotation {
    def override apply(var func : FunctionPtr; var group : ModuleGroup; var args : AnnotationArgumentList; var errors : das_string) : bool {
        var wrapped = qmacro_block() {
            let entered = true
            $e(func.body)
        }
        func.body = unsafe(reinterpret<ExprBlock?>(wrapped))
        return true
    }
}

To generate it exactly:

{ printf 'options gen2\nmodule wrap_macro shared\n\nrequire daslib/ast_boost\nrequire daslib/templates_boost\n\n'; for i in $(seq 1 90); do echo "// padding line $i"; done; printf '\n[function_macro(name = "wraps")]\nclass WrapsAnnotation : AstFunctionAnnotation {\n    def override apply(var func : FunctionPtr; var group : ModuleGroup; var args : AnnotationArgumentList; var errors : das_string) : bool {\n        var wrapped = qmacro_block() {\n            let entered = true\n            $e(func.body)\n        }\n        func.body = unsafe(reinterpret<ExprBlock?>(wrapped))\n        return true\n    }\n}\n'; } > wrap_macro.das

wrap_use.das:

options gen2
require wrap_macro

[wraps]
def answer : int {
    return 42
}

[export]
def main {
    print("{answer()}\n")
}

Observed / expected

daslang wrap_use.das prints 42.

daslang utils/lint/main.das -- wrap_use.das:

style warning: STYLE038: function 'answer' is 100 lines long (max 80); split it into smaller functions at wrap_use.das:5:4
    > def answer : int {

Expected: no STYLE038 for a three-line function. The computation in daslib/style_lint.das (st038_check_length: let nlines = int(body_at.last_line) - int(at.line) + 1) could skip or clamp the measurement when body_at.fileInfo is not at.fileInfo, or measure from the function's own at.last_line, so that a macro's splice never changes the length of the user's function.

Version

daslang 0.6.4, commit 69a589623 (Linux x64, Release).

Activity

  1. lookibed commented on Nov 9, 2026

    @lookibed
    OwnerAuthor

    Re-checked by the owner from this issue's reproducer on daScript abf1d40 (0.6.5): fixed upstream (GaijinEntertainment/daScript 2131620 GaijinEntertainment#4472 dasLLVM, 1404d5a GaijinEntertainment#4480 optimizer, f386f92 GaijinEntertainment#4482 cache and tools). The c2das-re probes were re-pinned in the repin PRs (lookibed/c2das-re#142-GaijinEntertainment#147; ruling 38 addendum 4).

  2. lookibed commented on Nov 9, 2026

    @lookibed
    OwnerAuthor

    Fixed upstream: GaijinEntertainment#4238 is closed and the fix is in master abf1d40e35 (0.6.5). Verified by running this issue's repro on a build of that commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions