/** Declare a function. TODO: static vs. instance. */
  @Override
  void declareFunction(FunctionNode func) {
    func.parseFunctionBody(classScope.getProblems());

    final FunctionDefinition funcDef = func.getDefinition();

    final boolean is_constructor = func.isConstructor();

    functionSemanticChecks(func);

    //  Save the constructor function until
    //  we've seen all the instance variables
    //  that might need initialization.
    if (is_constructor) {
      if (this.ctorFunction == null) this.ctorFunction = func;
      else {
        // If we already have a ctor, must be multiply defined. Ignore it and generate problem
        String name = this.className.getBaseName();
        classScope.addProblem(new MultipleContructorDefinitionsProblem(func, name));
      }
    } else {
      LexicalScope ls = funcDef.isStatic() ? classStaticScope : classScope;

      MethodInfo mi = classScope.getGenerator().generateFunction(func, ls, null);

      if (mi != null) {
        Name funcName = funcDef.getMName(classScope.getProject());
        ITraitVisitor tv =
            ls.traitsVisitor.visitMethodTrait(
                functionTraitKind(func, TRAIT_Method), funcName, 0, mi);

        if (funcName != null)
          classScope
              .getMethodBodySemanticChecker()
              .checkFunctionForConflictingDefinitions(func, funcDef);

        if (!funcDef.isStatic())
          if (funcDef.getNamespaceReference()
              instanceof NamespaceDefinition.IProtectedNamespaceDefinition)
            this.iinfo.flags |= ABCConstants.CLASS_FLAG_protected;

        ls.processMetadata(tv, getAllMetaTags(funcDef));

        if (func.hasModifier(ASModifier.FINAL)) tv.visitAttribute(Trait.TRAIT_FINAL, Boolean.TRUE);
        if (func.hasModifier(ASModifier.OVERRIDE))
          tv.visitAttribute(Trait.TRAIT_OVERRIDE, Boolean.TRUE);
        tv.visitEnd();
      }
    }
  }
  protected void verifyFunctionModifiers(FunctionNode f) {
    ModifiersSet modifiersSet = f.getModifiers();
    if (modifiersSet == null) return;

    IExpressionNode site = f.getNameExpressionNode();
    if (modifiersSet.hasModifier(ASModifier.STATIC)) {
      if (modifiersSet.hasModifier(ASModifier.FINAL)) {
        classScope.addProblem(new FinalOutsideClassProblem(site));
      }
      if (modifiersSet.hasModifier(ASModifier.OVERRIDE)) {
        classScope.addProblem(new StaticAndOverrideProblem(site));
      }
      if (modifiersSet.hasModifier(ASModifier.DYNAMIC)) {
        classScope.addProblem(new DynamicNotOnClassProblem(site));
      }
      if (modifiersSet.hasModifier(ASModifier.VIRTUAL)) {
        classScope.addProblem(new VirtualOutsideClassProblem(site));
      }
    }
    classScope.getMethodBodySemanticChecker().checkForDuplicateModifiers(f);
    // Functions in a class allow all modifiers
    return;
  }
  /**
   * This method performs the semantic analysis of a function declared in a class.
   *
   * @param node the FunctionNode to semantically analyze
   */
  void functionSemanticChecks(FunctionNode node) {
    verifyFunctionModifiers(node);

    FunctionDefinition func = node.getDefinition();

    Collection<ICompilerProblem> problems = classScope.getProblems();

    // code model has some peculiar ideas about what makes a function a constructor or not
    boolean looks_like_ctor = func.isConstructor();
    looks_like_ctor |=
        func.getBaseName() != null
            && this.className != null
            && func.getBaseName().equals(this.className.getBaseName());

    if (!looks_like_ctor && (func.getBaseName() != null)) {
      SemanticUtils.checkScopedToDefaultNamespaceProblem(
          classScope, node, func, this.classDefinition.getQualifiedName());
    }
    if (looks_like_ctor) {
      // If a constructor has a namespace as part of it's declaration, it must be declared public.
      // It is ok to omit the namespace
      // We must check the AST, as CM treats all ctors as public no matter what the user typed in
      // so the FunctionDefinition will always be in the public namespace
      if (node.getActualNamespaceNode() != null
          && node.getActualNamespaceNode().getName() != IASKeywordConstants.PUBLIC)
        problems.add(new ConstructorMustBePublicProblem(node.getActualNamespaceNode()));

      // A constructor cannot be static
      if (func.isStatic()) problems.add(new ConstructorIsStaticProblem(node));

      // A constructor cannot declare a return type, other than void.
      IExpressionNode returnTypeExpression = node.getReturnTypeNode();
      if (returnTypeExpression != null) {
        // We cannot check whether node.resolveReturnType() returns the definition
        // for the void type, because  the return type of a constructor is considered
        // to be the class of the object being constructed, rather than void.
        // So instead we simply check whether the type annotation was void.
        boolean returnTypeIsVoid = false;
        if (returnTypeExpression instanceof ILanguageIdentifierNode) {
          LanguageIdentifierKind kind = ((ILanguageIdentifierNode) returnTypeExpression).getKind();
          if (kind == LanguageIdentifierKind.VOID) returnTypeIsVoid = true;
        }
        if (!returnTypeIsVoid) {
          ICompilerProblem problem =
              new ConstructorCannotHaveReturnTypeProblem(returnTypeExpression);
          problems.add(problem);
        }
      }

      // Is it a getter or setter that appears to be the constructor?
      if (func instanceof IAccessorDefinition)
        problems.add(new ConstructorIsGetterSetterProblem(node.getNameExpressionNode()));
    } else if (!func.isStatic()) {
      // We have to find the (potentially) overriden function whether we are an override or
      // not/
      FunctionDefinition override = func.resolveOverriddenFunction(classScope.getProject());
      if (func.isOverride()) {
        if (override == null) {
          // Didn't find the function we are supposed to be overriding
          problems.add(new OverrideNotFoundProblem(node.getNameExpressionNode()));
        } else {
          if (!func.hasCompatibleSignature(override, classScope.getProject())) {
            // Signatures didn't match
            problems.add(new IncompatibleOverrideProblem(node.getNameExpressionNode()));
          }
          if (override.isFinal()) {
            // overriding final
            problems.add(new OverrideFinalProblem(node.getNameExpressionNode()));
          }
        }
      } else if (override != null) {
        // found overriden function, but function not marked as override
        problems.add(new FunctionNotMarkedOverrideProblem(node.getNameExpressionNode()));
      }
    }
  }