//  assert_macro_check implementation  ------------------------------------------------//

//  Copyright Eric Niebler 2010.
//  Based on the assert_macro_check checker by Marshall Clow
//
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#include "assert_macro_check.hpp"
#include <functional>
#include "boost/regex.hpp"
#include "boost/lexical_cast.hpp"
#include "boost/filesystem/operations.hpp"

namespace fs = boost::filesystem;

namespace
{
  boost::regex assert_macro_regex(
    "("
    "^\\s*#\\s*undef\\s*" // # undef
    "\\b(assert)\\b"      // followed by assert macro, whole word
    ")"
    "|"                   // or (ignored)
    "("
    "//[^\\n]*"           // single line comments (//)
    "|"
    "/\\*.*?\\*/"         // multi line comments (/**/)
    "|"
    "\"(?:\\\\\\\\|\\\\\"|[^\"])*\"" // string literals
    ")"
    "|"                   // or
    "("
    "\\b(assert)\\b"  // assert macro, whole word
    "\\s*\\("         // followed by 0 or more spaces and an opening paren
    ")"
    , boost::regex::normal);

} // unnamed namespace


namespace boost
{
  namespace inspect
  {
   assert_macro_check::assert_macro_check()
     : m_files_with_errors(0)
     , m_from_boost_root(
         fs::exists(search_root_path() / "boost") &&
         fs::exists(search_root_path() / "libs"))
   {
     register_signature( ".c" );
     register_signature( ".cpp" );
     register_signature( ".cxx" );
     register_signature( ".h" );
     register_signature( ".hpp" );
     register_signature( ".hxx" );
     register_signature( ".ipp" );
   }

   void assert_macro_check::inspect(
      const string & library_name,
      const path & full_path,   // example: c:/foo/boost/filesystem/path.hpp
      const string & contents )     // contents of file to be inspected
    {
      if (contents.find( "boostinspect:" "naassert_macro" ) != string::npos)
        return;

      // Check files iff (a) they are in the boost directory, or (b) they
      // are in the src directory under libs.
      if (m_from_boost_root) {
        path relative( relative_to( full_path, search_root_path() ) );
        path::const_iterator pbeg = relative.begin(), pend = relative.end();
        if (pbeg != std::find(pbeg, pend, "boost") &&
          !(pbeg == std::find(pbeg, pend, "libs") && pend != std::find(pbeg, pend, "src")))
          return;
      }

      long errors = 0;
      boost::sregex_iterator cur(contents.begin(), contents.end(), assert_macro_regex), end;
      for( ; cur != end; ++cur )
      {
        if(!(*cur)[3].matched)
        {
          string::const_iterator it = contents.begin();
          string::const_iterator match_it = (*cur)[0].first;

          string::const_iterator line_start = it;

          string::size_type line_number = 1;
          for ( ; it != match_it; ++it) {
              if (string::traits_type::eq(*it, '\n')) {
                  ++line_number;
                  line_start = it + 1; // could be end()
              }
          }

          ++errors;
          error( library_name, full_path, "C-style assert macro on line "
            + boost::lexical_cast<string>( line_number ) );
        }
      }
      if(errors > 0)
        ++m_files_with_errors;
    }
  } // namespace inspect
} // namespace boost


