def CheckForNonStandardConstructs()

in cpplint.py [0:0]


def CheckForNonStandardConstructs(filename, clean_lines, linenum,
                                  nesting_state, error):
    r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.

  Complain about several constructs which gcc-2 accepts, but which are
  not standard C++.  Warning about these in lint is one way to ease the
  transition to new compilers.
  - put storage class first (e.g. "static const" instead of "const static").
  - "%lld" instead of %qd" in printf-type functions.
  - "%1$d" is non-standard in printf-type functions.
  - "\%" is an undefined character escape sequence.
  - text after #endif is not allowed.
  - invalid inner-style forward declaration.
  - >? and <? operators, and their >?= and <?= cousins.

  Additionally, check for constructor/destructor style violations and reference
  members, as it is very convenient to do so while checking for
  gcc-2 compliance.

  Args:
    filename: The name of the current file.
    clean_lines: A CleansedLines instance containing the file.
    linenum: The number of the line to check.
    nesting_state: A NestingState instance which maintains information about
                   the current stack of nested blocks being parsed.
    error: A callable to which errors are reported, which takes 4 arguments:
           filename, line number, error level, and message
  """

    # Remove comments from the line, but leave in strings for now.
    line = clean_lines.lines[linenum]

    if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
        error(filename, linenum, 'runtime/printf_format', 3,
              '%q in format strings is deprecated.  Use %ll instead.')

    if Search(r'printf\s*\(.*".*%\d+\$', line):
        error(filename, linenum, 'runtime/printf_format', 2,
              '%N$ formats are unconventional.  Try rewriting to avoid them.')

    # Remove escaped backslashes before looking for undefined escapes.
    line = line.replace('\\\\', '')

    if Search(r'("|\').*\\(%|\[|\(|{)', line):
        error(filename, linenum, 'build/printf_format', 3,
              '%, [, (, and { are undefined character escapes.  Unescape them.')

    # For the rest, work with both comments and strings removed.
    line = clean_lines.elided[linenum]

    if Search(r'\b(const|volatile|void|char|short|int|long'
              r'|float|double|signed|unsigned'
              r'|schar|u?int8|u?int16|u?int32|u?int64)'
              r'\s+(register|static|extern|typedef)\b',
              line):
        error(filename, linenum, 'build/storage_class', 5,
              'Storage-class specifier (static, extern, typedef, etc) should be '
              'at the beginning of the declaration.')

    if Match(r'\s*#\s*endif\s*[^/\s]+', line):
        error(filename, linenum, 'build/endif_comment', 5,
              'Uncommented text after #endif is non-standard.  Use a comment.')

    if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
        error(filename, linenum, 'build/forward_decl', 5,
              'Inner-style forward declarations are invalid.  Remove this line.')

    if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',
              line):
        error(filename, linenum, 'build/deprecated', 3,
              '>? and <? (max and min) operators are non-standard and deprecated.')

    if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):
        # TODO(unknown): Could it be expanded safely to arbitrary references,
        # without triggering too many false positives? The first
        # attempt triggered 5 warnings for mostly benign code in the regtest, hence
        # the restriction.
        # Here's the original regexp, for the reference:
        # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?'
        # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;'
        error(filename, linenum, 'runtime/member_string_references', 2,
              'const string& members are dangerous. It is much better to use '
              'alternatives, such as pointers or simple constants.')

    # Everything else in this function operates on class declarations.
    # Return early if the top of the nesting stack is not a class, or if
    # the class head is not completed yet.
    classinfo = nesting_state.InnermostClass()
    if not classinfo or not classinfo.seen_open_brace:
        return

    # The class may have been declared with namespace or classname qualifiers.
    # The constructor and destructor will not have those qualifiers.
    base_classname = classinfo.name.split('::')[-1]

    # Look for single-argument constructors that aren't marked explicit.
    # Technically a valid construct, but against style.
    explicit_constructor_match = Match(
        r'\s+(?:(?:inline|constexpr)\s+)*(explicit\s+)?'
        r'(?:(?:inline|constexpr)\s+)*%s\s*'
        r'\(((?:[^()]|\([^()]*\))*)\)'
        % re.escape(base_classname),
        line)

    if explicit_constructor_match:
        is_marked_explicit = explicit_constructor_match.group(1)

        if not explicit_constructor_match.group(2):
            constructor_args = []
        else:
            constructor_args = explicit_constructor_match.group(2).split(',')

        # collapse arguments so that commas in template parameter lists and function
        # argument parameter lists don't split arguments in two
        i = 0
        while i < len(constructor_args):
            constructor_arg = constructor_args[i]
            while (constructor_arg.count('<') > constructor_arg.count('>') or
                   constructor_arg.count('(') > constructor_arg.count(')')):
                constructor_arg += ',' + constructor_args[i + 1]
                del constructor_args[i + 1]
            constructor_args[i] = constructor_arg
            i += 1

        defaulted_args = [arg for arg in constructor_args if '=' in arg]
        noarg_constructor = (not constructor_args or  # empty arg list
                             # 'void' arg specifier
                             (len(constructor_args) == 1 and
                              constructor_args[0].strip() == 'void'))
        onearg_constructor = ((len(constructor_args) == 1 and  # exactly one arg
                               not noarg_constructor) or
                              # all but at most one arg defaulted
                              (len(constructor_args) >= 1 and
                               not noarg_constructor and
                               len(defaulted_args) >= len(constructor_args) - 1))
        initializer_list_constructor = bool(
            onearg_constructor and
            Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0]))
        copy_constructor = bool(
            onearg_constructor and
            Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&'
                  % re.escape(base_classname), constructor_args[0].strip()))

        if (not is_marked_explicit and
                onearg_constructor and
                not initializer_list_constructor and
                not copy_constructor):
            if defaulted_args:
                error(filename, linenum, 'runtime/explicit', 5,
                      'Constructors callable with one argument '
                      'should be marked explicit.')
            else:
                error(filename, linenum, 'runtime/explicit', 5,
                      'Single-parameter constructors should be marked explicit.')
        elif is_marked_explicit and not onearg_constructor:
            if noarg_constructor:
                error(filename, linenum, 'runtime/explicit', 5,
                      'Zero-parameter constructors should not be marked explicit.')