<commit_before>// Copyright 2022, University of Colorado Boulder
/**
 * @fileoverview Rule that checks for missing return types on method definitions for TypeScript files.
 *
 * This file was required, and we couldn't use the rule from the typescript-eslint plugin because we only want this
 * rule checked when defining a method, and not on anonymous functions or other function declarations.
 *
 * @author <NAME> (PhET Interactive Simulations)
 * @copyright 2022 University of Colorado Boulder
 */

const { ESLintUtils } = require( '@typescript-eslint/utils/' ); // eslint-disable-line

// these are still MethodDefinition nodes, but don't require an annotation
const exemptMethods = [ 'get', 'set', 'constructor' ];

module.exports = {
  meta: {
    type: 'problem',
    fixable: 'code'
  },
  create: context => {
    return {
      MethodDefinition: node => {
        if ( !exemptMethods.includes( node.kind ) && node.value && !node.value.returnType ) {

          context.report( {
            message: 'Missing return type.',
            node: node

            // Comment out for next time we need this fixer, but it requires type info, where the rule doesn't, so don't always include it.
            // fix: fixer => {
            //
            //   // Get the type checker.
            //   const parserServices = ESLintUtils.getParserServices( context );
            //   const checker = parserServices.program.getTypeChecker();
            //
            //   // Had help from an example here to get the return type from a method declaration as a string:
            //   // https://stackoverflow.com/questions/47215069/how-to-use-typescript-compiler-api-to-get-normal-function-info-eg-returntype-p
            //   const tsNode = parserServices.esTreeNodeToTSNodeMap.get( node );
            //   const signature = checker.getSignatureFromDeclaration( tsNode );
            //   const returnType = checker.getReturnTypeOfSignature( signature );
            //   const returnTypeString = checker.typeToString( returnType );
            //
            //   // start location of the function body, insert the return type just before this and add an extra space
            //   const bodyStart = node.value.body.range[ 0 ];
            //
            //   // TODO: do Range/Node etc as a second fix to easily find spots where we are using the DOM type.
            //   // TODO: any should be filled in themselves.
            //   // TODO: look into some derivedProperty or other Property returns. Perhaps do those manually.
            //   if ( returnTypeString !== 'any' && ![ 'Image', 'Range', 'Text', 'Node', 'Event' ].includes( returnTypeString ) &&
            //        !returnTypeString.includes( 'Property' ) ) {
            //     return fixer.insertTextBeforeRange( [ bodyStart - 1, bodyStart ], `: ${returnTypeString} ` );
            //   }
            // }
          } );
        }
      }
    };
  }
};<commit_msg>prepare explicit-method-return-type for requiring return type annotations on function declarations, see https://github.com/phetsims/chipper/issues/1288<commit_after>// Copyright 2022, University of Colorado Boulder
/**
 * @fileoverview Rule that checks for missing return types on method definitions and function declarations for
 * TypeScript files.
 *
 * We could not use the built-in rule explicit-function-return-type because it does not support a way to skip functions
 * defined in a variable declaration. Functions defined like this typically have a local usage scope, and we decided
 * that we don't need them to have a return type.
 *
 * @author <NAME> (PhET Interactive Simulations)
 * @copyright 2022 University of Colorado Boulder
 */

const { ESLintUtils } = require( '@typescript-eslint/utils/' ); // eslint-disable-line

// these are still MethodDefinition nodes, but don't require an annotation
const exemptMethods = [ 'get', 'set', 'constructor' ];

/**
 * Use the type checker to get the return type as a string from the eslint context and AST Node.
 * @param {Context} context - https://eslint.org/docs/latest/developer-guide/working-with-rules#the-context-object
 * @param node - The AST Node
 */
const getReturnTypeString = ( context, node ) => {

  // Get the type checker.
  const parserServices = ESLintUtils.getParserServices( context );
  const checker = parserServices.program.getTypeChecker();

  // Had help from an example here to get the return type from a method declaration as a string:
  // https://stackoverflow.com/questions/47215069/how-to-use-typescript-compiler-api-to-get-normal-function-info-eg-returntype-p
  const tsNode = parserServices.esTreeNodeToTSNodeMap.get( node );
  const signature = checker.getSignatureFromDeclaration( tsNode );
  const returnType = checker.getReturnTypeOfSignature( signature );

  return checker.typeToString( returnType );
};

/**
 * Inserts the returnTypeString at the provided location.
 * @param bodyStartLocation
 * @param returnTypeString
 * @param fixer
 * @returns {boolean|*}
 */
const insertReturnType = ( bodyStartLocation, returnTypeString, fixer ) => {

  // TODO: do Range/Node etc as a second fix to easily find spots where we are using the DOM type.
  // TODO: any should be filled in themselves.
  // TODO: look into some derivedProperty or other Property returns. Perhaps do those manually.
  if ( returnTypeString !== 'any' && ![ 'Image', 'Range', 'Text', 'Node', 'Event' ].includes( returnTypeString ) &&
       !returnTypeString.includes( 'Property' ) ) {

    // location - 1 adds an extra space after the return type name
    return fixer.insertTextBeforeRange( [ bodyStartLocation - 1, bodyStartLocation ], `: ${returnTypeString} ` );
  }

  return false;
};

module.exports = {
  meta: {
    type: 'problem',
    fixable: 'code'
  },
  create: context => {
    return {
      // FunctionDeclaration: node => {
      //   if ( !node.returnType ) {
      //
      //     context.report( {
      //       message: 'Missing return type.',
      //       node: node,
      //
      //       // Comment out for next time we need this fixer, but it requires type info, where the rule doesn't, so don't always include it.
      //       fix: fixer => { // eslint-disable-line
      //
      //         const returnTypeString = getReturnTypeString( context, node );
      //         const bodyStart = node.body.range[ 0 ];
      //         return insertReturnType( bodyStart, returnTypeString, fixer );
      //       }
      //     } );
      //   }
      // },
      MethodDefinition: node => {
        if ( !exemptMethods.includes( node.kind ) && node.value && !node.value.returnType ) {

          context.report( {
            message: 'Missing return type.',
            node: node,

            // Comment out for next time we need this fixer, but it requires type info, where the rule doesn't, so don't always include it.
            fix: fixer => {

              const returnTypeString = getReturnTypeString( context, node );
              const bodyStart = node.value.body.range[ 0 ];
              return insertReturnType( bodyStart, returnTypeString, fixer );
            }
          } );
        }
      }
    };
  }
};
{'fraction_non_alphanumeric': 0.10873813512004467, 'fraction_numerical': 0.005862646566164154, 'mean_word_length': 2.4647001934235977, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5990433', 'n_tokens_mistral': 1954, 'n_tokens_neox': 1874, 'n_words': 651}