or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

ast-utils.mdindex.mdpattern-matching.mdreference-tracking.mdstatic-analysis.mdtoken-predicates.md
tile.json

static-analysis.mddocs/

Static Analysis

Advanced static code analysis capabilities including value evaluation and side effect detection. These functions enable ESLint rules to understand code behavior without execution, supporting complex analysis scenarios.

Capabilities

Static Value Evaluation

Evaluate the static value of AST nodes when possible, handling constants, literals, and simple expressions.

/**
 * Get the value of a given node if it's a static value
 * @param node - The node to evaluate
 * @param initialScope - Optional scope to start finding variables for identifier resolution
 * @returns Static value result or null if not evaluable
 */
function getStaticValue(node: Node, initialScope?: Scope): StaticValue | null;

interface StaticValue {
  value: any;
  optional?: boolean;
}

Usage Examples:

import { getStaticValue } from "eslint-utils";

create(context) {
  return {
    CallExpression(node) {
      // Check if first argument is a static string
      if (node.arguments[0]) {
        const staticArg = getStaticValue(node.arguments[0], context.getScope());
        if (staticArg && typeof staticArg.value === 'string') {
          console.log('Static string argument:', staticArg.value);
        }
      }
    },
    
    BinaryExpression(node) {
      // Evaluate static binary expressions
      const result = getStaticValue(node, context.getScope());
      if (result) {
        console.log('Binary expression evaluates to:', result.value);
        
        // Warn about always-true conditions
        if (node.operator === '===' && result.value === true) {
          context.report(node, 'Condition is always true');
        }
      }
    },
    
    Identifier(node) {
      // Resolve constant variable values
      const value = getStaticValue(node, context.getScope());
      if (value) {
        console.log(`Variable '${node.name}' has static value:`, value.value);
      }
    }
  };
}

String Constant Evaluation

Specialized function for extracting string values from literals and template literals.

/**
 * Get the value of a given node if it's a literal or template literal
 * @param node - The node to evaluate
 * @param initialScope - Optional scope for identifier evaluation
 * @returns String value or null if not a string constant
 */
function getStringIfConstant(node: Node, initialScope?: Scope): string | null;

Usage Examples:

import { getStringIfConstant } from "eslint-utils";

create(context) {
  return {
    CallExpression(node) {
      // Check for specific function calls with string arguments
      if (node.callee.name === 'require') {
        const moduleName = getStringIfConstant(node.arguments[0]);
        if (moduleName) {
          console.log('Requiring module:', moduleName);
          
          if (moduleName.startsWith('./')) {
            context.report(node, 'Use absolute imports instead of relative');
          }
        }
      }
    },
    
    Property(node) {
      // Check object property keys
      const keyString = getStringIfConstant(node.key);
      if (keyString && keyString.includes('-')) {
        context.report(node, 'Use camelCase property names');
      }
    },
    
    TemplateLiteral(node) {
      // Evaluate template literals with no expressions
      const stringValue = getStringIfConstant(node);
      if (stringValue) {
        console.log('Template literal evaluates to:', stringValue);
      }
    }
  };
}

Side Effect Detection

Analyze nodes for potential side effects, crucial for optimization and code analysis rules.

/**
 * Check whether a given node has any side effect or not
 * @param node - The node to check
 * @param sourceCode - The source code object
 * @param options - Optional configuration for side effect detection
 * @returns True if the node has potential side effects
 */
function hasSideEffect(node: Node, sourceCode: SourceCode, options?: SideEffectOptions): boolean;

interface SideEffectOptions {
  considerGetters?: boolean;
  considerImplicitTypeConversion?: boolean;
  visitorKeys?: object;
}

Usage Examples:

import { hasSideEffect } from "eslint-utils";

create(context) {
  const sourceCode = context.getSourceCode();
  
  return {
    ExpressionStatement(node) {
      // Check for unused expressions with side effects
      if (!hasSideEffect(node.expression, sourceCode)) {
        context.report(node, 'Unused expression with no side effects');
      }
    },
    
    ConditionalExpression(node) {
      // Warn about side effects in ternary conditions
      if (hasSideEffect(node.test, sourceCode)) {
        context.report(node.test, 'Avoid side effects in ternary conditions');
      }
    },
    
    CallExpression(node) {
      // Advanced side effect checking with options
      const hasEffects = hasSideEffect(node, sourceCode, {
        considerGetters: true,
        considerImplicitTypeConversion: true
      });
      
      if (hasEffects && isInIIFE(node)) {
        context.report(node, 'Side effects detected in IIFE');
      }
    },
    
    MemberExpression(node) {
      // Check property access for getter side effects
      const hasGetterEffects = hasSideEffect(node, sourceCode, {
        considerGetters: true
      });
      
      if (hasGetterEffects) {
        context.report(node, 'Property access may have side effects');
      }
    }
  };
}

Advanced Usage Patterns

Complex scenarios combining static analysis functions:

import { getStaticValue, getStringIfConstant, hasSideEffect } from "eslint-utils";

create(context) {
  const sourceCode = context.getSourceCode();
  
  function analyzeExpression(node) {
    // Try static evaluation first
    const staticValue = getStaticValue(node, context.getScope());
    if (staticValue) {
      return {
        type: 'static',
        value: staticValue.value,
        hasSideEffects: false
      };
    }
    
    // Check for string constants
    const stringValue = getStringIfConstant(node, context.getScope());
    if (stringValue) {
      return {
        type: 'string',
        value: stringValue,
        hasSideEffects: false
      };
    }
    
    // Check for side effects
    const hasEffects = hasSideEffect(node, sourceCode, {
      considerGetters: true,
      considerImplicitTypeConversion: true
    });
    
    return {
      type: 'dynamic',
      value: null,
      hasSideEffects: hasEffects
    };
  }
  
  return {
    VariableDeclarator(node) {
      if (node.init) {
        const analysis = analyzeExpression(node.init);
        
        if (analysis.type === 'static') {
          console.log(`Variable '${node.id.name}' initialized with static value:`, analysis.value);
        } else if (analysis.hasSideEffects) {
          context.report(node, 'Variable initialization has side effects');
        }
      }
    }
  };
}

Supported Static Evaluations

The static value evaluation supports:

  • Literals: Numbers, strings, booleans, null, undefined
  • Arrays: Array expressions with static elements
  • Objects: Object expressions with static properties
  • Binary Operations: Arithmetic, comparison, and logical operators
  • Unary Operations: Negation, typeof, void, etc.
  • Template Literals: With static expressions
  • Conditional Expressions: With static test conditions
  • Sequence Expressions: Evaluates to the last expression
  • Built-in Globals: Math, Object, Array methods (safe subset)
  • Constant Variables: Variables declared with const and static initializers

Side Effect Categories

The side effect detection identifies:

  • Assignment Operations: All assignment and update expressions
  • Function Calls: All function and method calls
  • Constructor Calls: New expressions
  • Property Access: When considerGetters is true
  • Type Conversion: When considerImplicitTypeConversion is true
  • Await Expressions: Asynchronous operations
  • Import Expressions: Dynamic imports
  • Update Expressions: Increment/decrement operators