File: src/com/google/javascript/jscomp/InlineVariables.java
Issue: goog.addSingletonGetter prevents unused class removal
What steps will reproduce the problem?

// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name default.js
// @use_closure_library true
// @formatting pretty_print,print_input_delimiter
// @warning_level VERBOSE
// @debug true
// ==/ClosureCompiler==

goog.provide('foo');

var foo = function() { this.values = []; };
goog.addSingletonGetter(foo);

foo.prototype.add = function(value) {this.values.push(value)};

What is the expected output? What do you see instead?

Expect: The code is completely removed.

Instead:

(function($ctor$$) {
  $ctor$$.$getInstance$ = function $$ctor$$$$getInstance$$() {
    return $ctor$$.$instance_$ || ($ctor$$.$instance_$ = new $ctor$$)
  }
})(function() {
});

What version of the product are you using? On what operating system?

http://closure-compiler.appspot.com on Feb 28, 2012

Please provide any additional information below.

Buggy code:
private boolean canInline(
    Reference declaration,
    Reference initialization,
    Reference reference) {
  if (!isValidDeclaration(declaration)
      || !isValidInitialization(initialization)
      || !isValidReference(reference)) {
    return false;
  }

  // If the value is read more than once, skip it.
  // VAR declarations and EXPR_RESULT don't need the value, but other
  // ASSIGN expressions parents do.
  if (declaration != initialization &&
      !initialization.getGrandparent().isExprResult()) {
    return false;
  }

  // Be very conservative and do no cross control structures or
  // scope boundaries
  if (declaration.getBasicBlock() != initialization.getBasicBlock()
      || declaration.getBasicBlock() != reference.getBasicBlock()) {
    return false;
  }

  // Do not inline into a call node. This would change
  // the context in which it was being called. For example,
  //   var a = b.c;
  //   a();
  // should not be inlined, because it calls a in the context of b
  // rather than the context of the window.
  //   var a = b.c;
  //   f(a)
  // is ok.
  Node value = initialization.getAssignedValue();
  Preconditions.checkState(value != null);
  if (value.isGetProp()
      && reference.getParent().isCall()
      && reference.getParent().getFirstChild() == reference.getNode()) {
    return false;
  }

  if (value.isFunction()) {
    Node callNode = reference.getParent();
    if (reference.getParent().isCall()) {
      CodingConvention convention = compiler.getCodingConvention();
      // Bug 2388531: Don't inline subclass definitions into class defining
      // calls as this confused class removing logic.
      SubclassRelationship relationship =
          convention.getClassesDefinedByCall(callNode);
      if (relationship != null) {
        return false;
      }

      // issue 668: Don't inline singleton getter methods
      // calls as this confused class removing logic.
    }
  }

  return canMoveAggressively(value) ||
      canMoveModerately(initialization, reference);
}

Fixed code:
private boolean canInline(
    Reference declaration,
    Reference initialization,
    Reference reference) {
  if (!isValidDeclaration(declaration)
      || !isValidInitialization(initialization)
      || !isValidReference(reference)) {
    return false;
  }

  // If the value is read more than once, skip it.
  // VAR declarations and EXPR_RESULT don't need the value, but other
  // ASSIGN expressions parents do.
  if (declaration != initialization &&
      !initialization.getGrandparent().isExprResult()) {
    return false;
  }

  // Be very conservative and do no cross control structures or
  // scope boundaries
  if (declaration.getBasicBlock() != initialization.getBasicBlock()
      || declaration.getBasicBlock() != reference.getBasicBlock()) {
    return false;
  }

  // Do not inline into a call node. This would change
  // the context in which it was being called. For example,
  //   var a = b.c;
  //   a();
  // should not be inlined, because it calls a in the context of b
  // rather than the context of the window.
  //   var a = b.c;
  //   f(a)
  // is ok.
  Node value = initialization.getAssignedValue();
  Preconditions.checkState(value != null);
  if (value.isGetProp()
      && reference.getParent().isCall()
      && reference.getParent().getFirstChild() == reference.getNode()) {
    return false;
  }

  if (value.isFunction()) {
    Node callNode = reference.getParent();
    if (reference.getParent().isCall()) {
      CodingConvention convention = compiler.getCodingConvention();
      // Bug 2388531: Don't inline subclass definitions into class defining
      // calls as this confused class removing logic.
      SubclassRelationship relationship =
          convention.getClassesDefinedByCall(callNode);
      if (relationship != null) {
        return false;
      }

      // issue 668: Don't inline singleton getter methods
      // calls as this confused class removing logic.
      if (convention.getSingletonGetterClassName(callNode) != null) {
        return false;
      }
    }
  }

  return canMoveAggressively(value) ||
      canMoveModerately(initialization, reference);
}