Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation warning: Statement does nothing #664

Closed
lars-reimann opened this issue Oct 22, 2023 · 1 comment · Fixed by #787
Closed

Validation warning: Statement does nothing #664

lars-reimann opened this issue Oct 22, 2023 · 1 comment · Fixed by #787
Assignees
Labels
enhancement 💡 New feature or request released Included in a release validation ✔️ Improved or new static checks

Comments

@lars-reimann
Copy link
Member

lars-reimann commented Oct 22, 2023

Show a warning if we detect that a statement has no effect. For assignment statements, the assignee list must contain only wildcards, and the RHS must have no effect:

package tests.validation.other.statements.assignments.hasNoEffect

step myFunction() -> a: Int {
    // $TEST$ warning "This statement does nothing."
    »_ = 1 + 2;«

    // $TEST$ no warning "This statement does nothing."
    »val a = 1;«
    // $TEST$ no warning "This statement does nothing."
    »yield a = 1;«

    () {
        // $TEST$ warning "This statement does nothing."
        »_ = 1 + 2;«

        // $TEST$ no warning "This statement does nothing."
        »val a = 1;«
        // $TEST$ no warning "This statement does nothing."
        »yield a = 1;«
    };
}

For expression statements, the expression must have no effect:

package tests.validation.other.statements.expressionStatements.hasNoEffect

fun impureFunction()
@Pure fun pureFunction() -> a: Int

class MyClass() {
    fun impureFunction()
    @Pure fun pureFunction()
}

segment pureStep() {
    val a = pureFunction();
}

segment impureStep() {
    impureFunction();
}

segment recursiveA() {
    recursiveB();
}

segment recursiveB() {
    recursiveA();
}

segment myStep() {
    // $TEST$ warning "This statement does nothing."
    »1 + 2;«
    // $TEST$ warning "This statement does nothing."
    »pureFunction();«
    // $TEST$ warning "This statement does nothing."
    »MyClass().pureFunction();«

    // $TEST$ no warning "This statement does nothing."
    »impureFunction();«
    // $TEST$ no warning "This statement does nothing."
    »MyClass().impureFunction();«

    () {
        // $TEST$ warning "This statement does nothing."
        »1 + 2;«
        // $TEST$ warning "This statement does nothing."
        »pureFunction();«
        // $TEST$ warning "This statement does nothing."
        »MyClass().pureFunction();«

        // $TEST$ no warning "This statement does nothing."
        »impureFunction();«
        // $TEST$ no warning "This statement does nothing."
        »MyClass().impureFunction();«
    };

    // $TEST$ warning "This statement does nothing."
    »(() {
        pureFunction();
        MyClass().pureFunction();
    })();«

    // $TEST$ warning "This statement does nothing."
    »pureStep();«

    // $TEST$ no warning "This statement does nothing."
    »(() {
        impureFunction();
    })();«

    // $TEST$ no warning "This statement does nothing."
    »(() {
        MyClass().impureFunction();
    })();«

    // $TEST$ no warning "This statement does nothing."
    »impureStep();«

    // $TEST$ no warning "This statement does nothing."
    »recursiveA();«
}

For side-effect inference:

package tests.staticAnalysis.sideEffects

// Positive examples -----------------------------------------------------------

annotation ShouldHaveNoSideEffects

// Call to class constructor

class C()

@ShouldHaveNoSideEffects
step callOfClassConstructor() {
    C();
}

// Call to enum variant constructor

enum MyEnum {
    Variant
}

@ShouldHaveNoSideEffects
step callOfEnumVariantConstructor() {
    MyEnum.Variant();
}

// Function without side effects

@Pure
fun pureFunction()

@NoSideEffects
fun functionWithoutSideEffects()

@ShouldHaveNoSideEffects
step callToPureFunction() {
    pureFunction();
    functionWithoutSideEffects();
}

// Lambdas without side effects

@ShouldHaveNoSideEffects
step callToPureLambdas() {
    (() {})();
    (() -> null)();

    () {
        (() {})();
    };

    () -> (() -> null)();
}

// Steps without side effects

step pureStep() {}

@ShouldHaveNoSideEffects
step callToPureSteps() {
    pureStep();
}

// Uncalled lambdas

step pureStepWithUncalledLambdas() {
    () -> impureFunction();
}

@ShouldHaveNoSideEffects
step uncalledLambdas() {
    pureStepWithUncalledLambdas();
}

// Function as result

@ShouldHaveNoSideEffects
step pureFunctionAsResult() {
    (() -> pureFunction)()();
}

// Negative examples -----------------------------------------------------------

annotation ShouldHaveSideEffects

// Callable type

@ShouldHaveSideEffects
step callToCallableType(f: () -> ()) {
    f();
}

// Function with side effects

fun impureFunction()

@ShouldHaveSideEffects
step callToImpureFunction() {
    impureFunction();
}

// Lambdas with side effects

@ShouldHaveSideEffects
step callToImpureLambdas() {
    (() { impureFunction(); })();
    (() -> impureFunction())();

    () {
        (() { impureFunction(); })();
    };

    () -> (() -> impureFunction())();
}

// Steps with side effects

step impureStep() {
    impureFunction();
}

@ShouldHaveSideEffects
step callToImpureSteps() {
    impureStep();
}

// Recursion

@ShouldHaveSideEffects
step recursion() {
    recursion();
}

// Unresolved callable

@ShouldHaveSideEffects
step unresolvedCallable() {
    unresolved();
}

// Function as parameter

@ShouldHaveSideEffects
step impureFunctionAsParameter() {
    ((f) -> f())(pureFunction); // This is actually pure, but we match in a conservative manner. Can be improved later.
    ((f) -> f())(impureFunction);
}
@lars-reimann lars-reimann changed the title StatementDoesNothing (needs purity/side effect analysis) Validation warning: Statement does nothing Oct 22, 2023
@lars-reimann lars-reimann added the validation ✔️ Improved or new static checks label Oct 22, 2023
@lars-reimann lars-reimann self-assigned this Oct 30, 2023
@lars-reimann lars-reimann added the enhancement 💡 New feature or request label Nov 6, 2023
@lars-reimann lars-reimann added this to the purity-analysis milestone Nov 12, 2023
This was referenced Nov 14, 2023
@lars-reimann lars-reimann linked a pull request Nov 21, 2023 that will close this issue
lars-reimann added a commit that referenced this issue Nov 21, 2023
Closes #664

### Summary of Changes

Show a warning if a statement has no effect.
lars-reimann pushed a commit that referenced this issue Nov 22, 2023
## [0.4.0](v0.3.0...v0.4.0) (2023-11-22)

### Features

* add endless recursion as an impurity reason ([#788](#788)) ([98acdde](98acdde))
* call graph computer (without closures) ([#782](#782)) ([34bf182](34bf182))
* check types of constant parameters ([#775](#775)) ([0a02850](0a02850)), closes [#668](#668)
* check whether purity of callable parameters of functions is set properly ([#777](#777)) ([f8fd907](f8fd907)), closes [#732](#732)
* compute purity/side effects for expressions ([#785](#785)) ([9ed1c08](9ed1c08)), closes [#15](#15)
* compute types of parameters of lambdas that are passed as default value ([#780](#780)) ([01a5c03](01a5c03))
* error if call leads to infinite recursion ([#783](#783)) ([f7eabd8](f7eabd8)), closes [#667](#667)
* error if impure callable is passed to pure parameter ([#792](#792)) ([5536a4a](5536a4a)), closes [#730](#730)
* error if parameter name in impurity reason is invalid ([#772](#772)) ([faa2012](faa2012)), closes [#741](#741)
* error if purity of functions is not specified ([#768](#768)) ([a15b0af](a15b0af)), closes [#731](#731)
* filter statements without effect for code generation ([#786](#786)) ([cd4f2c1](cd4f2c1)), closes [#542](#542)
* improve location of warning about duplicate annotation target ([#771](#771)) ([87d2a48](87d2a48))
* info if `@Pure` annotation is called on parameter of pure function ([#778](#778)) ([c15c70e](c15c70e))
* purity computer ([#784](#784)) ([b09bb3a](b09bb3a))
* remove type parameters from enum variants ([#767](#767)) ([cb6556a](cb6556a)), closes [#766](#766)
* short-circuit `and`, `or`, and `?:` if RHS has no side effects ([#789](#789)) ([9d9f4b7](9d9f4b7)), closes [#15](#15)
* streamline purity information ([#779](#779)) ([75a9e5b](75a9e5b))
* stricter definition of `const` parameters ([#776](#776)) ([73a0d4e](73a0d4e))
* update snippets for functions and methods ([#769](#769)) ([061d3b1](061d3b1))
* validate impurity reasons of overriding methods ([#774](#774)) ([71fc5bd](71fc5bd)), closes [#665](#665)
* warn about duplicate impurity reasons ([#773](#773)) ([8344356](8344356)), closes [#733](#733)
* warn if statement has no effect ([#787](#787)) ([6f45dc4](6f45dc4)), closes [#664](#664)

### Bug Fixes

* signature help for optional parameters ([#793](#793)) ([fd88ce8](fd88ce8)), closes [#791](#791)
* wrong detection of useless statements that call parameters/unknown callables ([#790](#790)) ([a49b4b3](a49b4b3))
* wrong`"assignment/nothing-assigned"` error if RHS calls expression lambda ([#781](#781)) ([b909cb8](b909cb8))
@lars-reimann
Copy link
Member Author

🎉 This issue has been resolved in version 0.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@lars-reimann lars-reimann added the released Included in a release label Nov 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement 💡 New feature or request released Included in a release validation ✔️ Improved or new static checks
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

1 participant