# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

""" Lint for IDL """

import os
import sys

from idl_log import ErrOut, InfoOut, WarnOut
from idl_node import IDLAttribute, IDLNode
from idl_ast import IDLAst
from idl_option import GetOption, Option, ParseOptions
from idl_outfile import IDLOutFile
from idl_visitor import IDLVisitor


Option('wcomment', 'Disable warning for missing comment.')
Option('wenum', 'Disable warning for missing enum value.')
Option('winline', 'Disable warning for inline blocks.')
Option('wname', 'Disable warning for inconsistent interface name.')
Option('wnone', 'Disable all warnings.')
Option('wparam', 'Disable warning for missing [in|out|inout] on param.')
Option('wpass', 'Disable warning for mixed passByValue and returnByValue.')

#
# IDLLinter
#
# Once the AST is build, we need to resolve the namespace and version
# information.
#
class IDLLinter(IDLVisitor):
  def VisitFilter(self, node, data):
    __pychecker__ = 'unusednames=node,data'
    return not node.IsA('Comment', 'Copyright')

  def Arrive(self, node, errors):
    __pychecker__ = 'unusednames=node,errors'
    warnings = 0
    if node.IsA('Interface', 'Member', 'Struct', 'Enum', 'EnumItem', 'Typedef'):
      comments = node.GetListOf('Comment')
      if not comments and not node.GetProperty('wcomment'):
        node.Warning('Expecting a comment.')
        warnings += 1

    if node.IsA('File'):
      labels = node.GetListOf('Label')
      interfaces = node.GetListOf('Interface')
      if interfaces and not labels:
        node.Warning('Expecting a label in a file containing interfaces.')

    if node.IsA('Struct', 'Typedef') and not node.GetProperty('wpass'):
      if node.GetProperty('passByValue'):
        pbv = 'is'
      else:
        pbv = 'is not'
      if node.GetProperty('returnByValue'):
        ret = 'is'
      else:
        ret = 'is not'
      if pbv != ret:
        node.Warning('%s passByValue but %s returnByValue.' % (pbv, ret))
        warnings += 1

    if node.IsA('EnumItem'):
      if not node.GetProperty('VALUE') and not node.GetProperty('wenum'):
        node.Warning('Expecting value for enumeration.')
        warnings += 1

    if node.IsA('Interface'):
      macro = node.GetProperty('macro')
      if macro and not node.GetProperty('wname'):
        node.Warning('Interface name inconsistent: %s' % macro)
        warnings += 1

    if node.IsA('Inline') and not node.GetProperty('winline'):
      inline_type = node.GetProperty('NAME')
      node.parent.Warning('Requires an inline %s block.' % inline_type)
      warnings += 1

    if node.IsA('Callspec') and not node.GetProperty('wparam'):
      out = False
      for arg in node.GetListOf('Param'):
        if arg.GetProperty('out'):
          out = True
        if arg.GetProperty('in') and out:
          arg.Warning('[in] parameter after [out] parameter')
          warnings += 1

    if node.IsA('Param') and not node.GetProperty('wparam'):
      found = False;
      for form in ['in', 'inout', 'out']:
        if node.GetProperty(form): found = True
      if not found:
        node.Warning('Missing argument type: [in|out|inout]')
        warnings += 1

    return warnings

  def Depart(self, node, warnings, childdata):
    __pychecker__ = 'unusednames=node'
    for child in childdata:
      warnings += child
    return warnings

def Lint(ast):
  options = ['wcomment', 'wenum', 'winline', 'wparam', 'wpass', 'wname']
  wnone = GetOption('wnone')
  for opt in options:
    if wnone or GetOption(opt): ast.SetProperty(opt, True)

  skipList = []
  for filenode in ast.GetListOf('File'):
    name = filenode.GetProperty('NAME')
    if filenode.GetProperty('ERRORS') > 0:
      ErrOut.Log('%s : Skipped due to errors.' % name)
      skipList.append(filenode)
      continue
    warnings = IDLLinter().Visit(filenode, 0)
    if warnings:
      WarnOut.Log('%s warning(s) for %s\n' % (warnings, name))
  return skipList
