diff --git a/outputview/outputfilteringstrategies.cpp b/outputview/outputfilteringstrategies.cpp index f2e65e1156..3a50016289 100644 --- a/outputview/outputfilteringstrategies.cpp +++ b/outputview/outputfilteringstrategies.cpp @@ -1,416 +1,395 @@ /* This file is part of KDevelop Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "outputfilteringstrategies.h" #include "outputformats.h" #include "filtereditem.h" #include #include #include #include namespace KDevelop { +FilteredItem FilteringStrategyUtils::match(const QList& errorFormats, const QString& line) +{ + FilteredItem item(line); + foreach( const ErrorFormat& curErrFilter, errorFormats ) { + QRegExp regEx = curErrFilter.expression; + if( regEx.indexIn( line ) != -1 ) + { + item.url = regEx.cap( curErrFilter.fileGroup ); + item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; + if(curErrFilter.columnGroup >= 0) { + item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; + } else { + item.columnNo = 0; + } + + QString txt = regEx.cap(curErrFilter.textGroup); + + item.type = FilteredItem::ErrorItem; + + // Make the item clickable if it comes with the necessary file & line number information + if (curErrFilter.fileGroup > 0 && curErrFilter.lineGroup > 0) { + item.isActivatable = true; + } + break; + } + } + return item; +} + /// --- No filter strategy --- NoFilterStrategy::NoFilterStrategy() { } FilteredItem NoFilterStrategy::actionInLine(const QString& line) { return FilteredItem( line ); } FilteredItem NoFilterStrategy::errorInLine(const QString& line) { return FilteredItem( line ); } /// --- Compiler error filter strategy --- /// Impl. of CompilerFilterStrategy. struct CompilerFilterStrategyPrivate { CompilerFilterStrategyPrivate(const KUrl& buildDir); KUrl urlForFile( const QString& ) const; bool isMultiLineCase(ErrorFormat curErrFilter) const; void putDirAtEnd(const QString& ); QVector m_currentDirs; KUrl m_buildDir; typedef QMap PositionMap; PositionMap m_positionInCurrentDirs; }; namespace { // All the possible string that indicate an error if we via Regex have been able to // extract file and linenumber from a given outputline typedef QPair Indicator; const QVector INDICATORS { // ld Indicator("undefined reference", FilteredItem::ErrorItem), Indicator("undefined symbol", FilteredItem::ErrorItem), Indicator("ld: cannot find", FilteredItem::ErrorItem), Indicator("no such file", FilteredItem::ErrorItem), // gcc Indicator("error", FilteredItem::ErrorItem), // generic Indicator("warning", FilteredItem::WarningItem), Indicator("info", FilteredItem::InformationItem), Indicator("note", FilteredItem::InformationItem), }; // A list of filters for possible compiler, linker, and make errors const QVector ERROR_FILTERS { // GCC - another case, eg. for #include "pixmap.xpm" which does not exists ErrorFormat( "^([^:\t]+):([0-9]+):([0-9]+):([^0-9]+)", 1, 2, 4, 3 ), // GCC ErrorFormat( "^([^:\t]+):([0-9]+):([^0-9]+)", 1, 2, 3 ), // GCC ErrorFormat( "^(In file included from |[ ]+from )([^: \\t]+):([0-9]+)(:|,)(|[0-9]+)", 2, 3, 5 ), // ICC ErrorFormat( "^([^: \\t]+)\\(([0-9]+)\\):([^0-9]+)", 1, 2, 3, "intel" ), //libtool link ErrorFormat( "^(libtool):( link):( warning): ", 0, 0, 0 ), // make ErrorFormat( "No rule to make target", 0, 0, 0 ), // cmake ErrorFormat( "^([^: \\t]+):([0-9]+):", 1, 2, 0, "cmake" ), // cmake ErrorFormat( "CMake (Error|Warning) (|\\([a-zA-Z]+\\) )(in|at) ([^:]+):($|[0-9]+)", 4, 5, 1, "cmake" ), // cmake/automoc // example: AUTOMOC: error: /home/krf/devel/src/foo/src/quick/quickpathitem.cpp The file includes (...), ErrorFormat( "^AUTOMOC: error: (.*) (The file includes .*)$", 1, 0, 0 ), // Fortran ErrorFormat( "\"(.*)\", line ([0-9]+):(.*)", 1, 2, 3 ), // GFortran ErrorFormat( "^(.*):([0-9]+)\\.([0-9]+):(.*)", 1, 2, 4, "gfortran", 3 ), // Jade ErrorFormat( "^[a-zA-Z]+:([^: \t]+):([0-9]+):[0-9]+:[a-zA-Z]:(.*)", 1, 2, 3 ), // ifort ErrorFormat( "^fortcom: (.*): (.*), line ([0-9]+):(.*)", 2, 3, 1, "intel" ), // PGI ErrorFormat( "PGF9(.*)-(.*)-(.*)-(.*) \\((.*): ([0-9]+)\\)", 5, 6, 4, "pgi" ), // PGI (2) ErrorFormat( "PGF9(.*)-(.*)-(.*)-Symbol, (.*) \\((.*)\\)", 5, 5, 4, "pgi" ), }; // A list of filters for possible compiler, linker, and make actions const QVector ACTION_FILTERS { ActionFormat( I18N_NOOP2_NOSTRIP("", "compiling"), 1, 2, "(?:^|[^=])\\b(gcc|CC|cc|distcc|c\\+\\+|" "g\\+\\+|clang|clang\\+\\+|mpicc|icc|icpc)\\s+.*-c.*[/ '\\\\]+(\\w+\\.(?:cpp|CPP|c|C|cxx|CXX|cs|" "java|hpf|f|F|f90|F90|f95|F95))"), //moc and uic ActionFormat( I18N_NOOP2_NOSTRIP("", "generating"), 1, 2, "/(moc|uic)\\b.*\\s-o\\s([^\\s;]+)"), //libtool linking ActionFormat( I18N_NOOP2_NOSTRIP("Linking object files into a library or executable", "linking"), "libtool", "/bin/sh\\s.*libtool.*--mode=link\\s.*\\s-o\\s([^\\s;]+)", 1 ), //unsermake ActionFormat( I18N_NOOP2_NOSTRIP("", "compiling"), 1, 1, "^compiling (.*)" ), ActionFormat( I18N_NOOP2_NOSTRIP("", "generating"), 1, 2, "^generating (.*)" ), ActionFormat( I18N_NOOP2_NOSTRIP("Linking object files into a library or executable", "linking"), 1, 2, "(gcc|cc|c\\+\\+|g\\+\\+|clang|clang\\+\\+|mpicc|icc|icpc)\\S* (?:\\S* )*-o ([^\\s;]+)"), ActionFormat( I18N_NOOP2_NOSTRIP("Linking object files into a library or executable", "linking"), 1, 2, "^linking (.*)" ), //cmake ActionFormat( I18N_NOOP2_NOSTRIP("", "built"), -1, 1, "\\[.+%\\] Built target (.*)" ), ActionFormat( I18N_NOOP2_NOSTRIP("", "compiling"), "cmake", "\\[.+%\\] Building .* object (.*)CMakeFiles/", 1 ), ActionFormat( I18N_NOOP2_NOSTRIP("", "generating"), -1, 1, "\\[.+%\\] Generating (.*)" ), ActionFormat( I18N_NOOP2_NOSTRIP("Linking object files into a library or executable", "linking"), -1, 1, "^Linking (.*)" ), ActionFormat( I18N_NOOP2_NOSTRIP("", "configuring"), "cmake", "(-- Configuring (done|incomplete)|-- Found|-- Adding|-- Enabling)", -1 ), ActionFormat( I18N_NOOP2_NOSTRIP("", "installing"), -1, 1, "-- Installing (.*)" ), //libtool install ActionFormat( I18N_NOOP2_NOSTRIP("", "creating"), "", "/(?:bin/sh\\s.*mkinstalldirs).*\\s([^\\s;]+)", 1 ), ActionFormat( I18N_NOOP2_NOSTRIP("", "installing"), "", "/(?:usr/bin/install|bin/sh\\s.*mkinstalldirs" "|bin/sh\\s.*libtool.*--mode=install).*\\s([^\\s;]+)", 1 ), //dcop ActionFormat( I18N_NOOP2_NOSTRIP("", "generating"), "dcopidl", "dcopidl .* > ([^\\s;]+)", 1 ), ActionFormat( I18N_NOOP2_NOSTRIP("", "compiling"), "dcopidl2cpp", "dcopidl2cpp (?:\\S* )*([^\\s;]+)", 1 ), // match against Entering directory to update current build dir ActionFormat( "", "cd", "", "make\\[\\d+\\]: Entering directory (\\`|\\')(.+)'", 2), }; } CompilerFilterStrategyPrivate::CompilerFilterStrategyPrivate(const KUrl& buildDir) : m_buildDir(buildDir) { } KUrl CompilerFilterStrategyPrivate::urlForFile(const QString& filename) const { QFileInfo fi( filename ); KUrl currentUrl; if( fi.isRelative() ) { if( m_currentDirs.isEmpty() ) { currentUrl = m_buildDir; currentUrl.addPath( filename ); return currentUrl; } QVector::const_iterator it = m_currentDirs.constEnd() - 1; do { currentUrl = KUrl( *it ); currentUrl.addPath( filename ); } while( (it-- != m_currentDirs.constBegin()) && !QFileInfo(currentUrl.toLocalFile()).exists() ); return currentUrl; } else { currentUrl = KUrl( filename ); } return currentUrl; } bool CompilerFilterStrategyPrivate::isMultiLineCase(KDevelop::ErrorFormat curErrFilter) const { if(curErrFilter.compiler == "gfortran" || curErrFilter.compiler == "cmake") { return true; } return false; } void CompilerFilterStrategyPrivate::putDirAtEnd(const QString& dirNameToInsert) { CompilerFilterStrategyPrivate::PositionMap::iterator it = m_positionInCurrentDirs.find( dirNameToInsert ); // Encountered new build directory? if (it == m_positionInCurrentDirs.end()) { m_currentDirs.push_back( dirNameToInsert ); m_positionInCurrentDirs.insert( dirNameToInsert, m_currentDirs.size() - 1 ); } else { // Build dir already in currentDirs, but move it to back of currentDirs list // (this gives us most-recently-used semantics in urlForFile) std::rotate(m_currentDirs.begin() + it.value(), m_currentDirs.begin() + it.value() + 1, m_currentDirs.end() ); it.value() = m_currentDirs.size() - 1; } } CompilerFilterStrategy::CompilerFilterStrategy(const KUrl& buildDir) : d(new CompilerFilterStrategyPrivate( buildDir )) { } CompilerFilterStrategy::~CompilerFilterStrategy() { delete d; } QVector< QString > CompilerFilterStrategy::getCurrentDirs() { return d->m_currentDirs; } FilteredItem CompilerFilterStrategy::actionInLine(const QString& line) { const QByteArray cd = "cd"; const QByteArray compiling = "compiling"; FilteredItem item(line); foreach( const ActionFormat& curActFilter, ACTION_FILTERS ) { QRegExp regEx = curActFilter.expression; if( regEx.indexIn( line ) != -1 ) { item.type = FilteredItem::ActionItem; if( curActFilter.fileGroup != -1 && curActFilter.toolGroup != -1 ) { item.shortenedText = QString( "%1 %2 (%3)").arg(i18nc(curActFilter.context, curActFilter.action)).arg( regEx.cap( curActFilter.fileGroup ) ).arg( regEx.cap( curActFilter.toolGroup ) ); } if( curActFilter.action == cd ) { d->m_currentDirs.push_back( regEx.cap( curActFilter.fileGroup ) ); d->m_positionInCurrentDirs.insert( regEx.cap( curActFilter.fileGroup ) , d->m_currentDirs.size() - 1 ); } // Special case for cmake: we parse the "Compiling " expression // and use it to find out about the build paths encountered during a build. // They are later searched by urlForFile to find source files corresponding to // compiler errors. if ( curActFilter.action == compiling && curActFilter.tool == "cmake") { KUrl url = d->m_buildDir; url.addPath(regEx.cap( curActFilter.fileGroup )); d->putDirAtEnd(url.toLocalFile()); } break; } } return item; } FilteredItem CompilerFilterStrategy::errorInLine(const QString& line) { FilteredItem item(line); foreach( const ErrorFormat& curErrFilter, ERROR_FILTERS ) { QRegExp regEx = curErrFilter.expression; if( regEx.indexIn( line ) != -1 && !( line.contains( "Each undeclared identifier is reported only once" ) || line.contains( "for each function it appears in." ) ) ) { if(curErrFilter.fileGroup > 0) { if( curErrFilter.compiler == "cmake" ) { // Unfortunately we cannot know if an error or an action comes first in cmake, and therefore we need to do this if( d->m_currentDirs.empty() ) { d->putDirAtEnd( d->m_buildDir.upUrl().toLocalFile() ); } } item.url = d->urlForFile( regEx.cap( curErrFilter.fileGroup ) ); } item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; if(curErrFilter.columnGroup >= 0) { item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; } else { item.columnNo = 0; } QString txt = regEx.cap(curErrFilter.textGroup); // Find the indicator which happens most early. int earliestIndicatorIdx = txt.length(); foreach( const Indicator& curIndicator, INDICATORS ) { int curIndicatorIdx = txt.indexOf(curIndicator.first, 0, Qt::CaseInsensitive); if((curIndicatorIdx >= 0) && (earliestIndicatorIdx > curIndicatorIdx)) { earliestIndicatorIdx = curIndicatorIdx; item.type = curIndicator.second; } } // Make the item clickable if it comes with the necessary file information if (item.url.isValid()) { item.isActivatable = true; if(item.type == FilteredItem::InvalidItem) { // If there are no error indicators in the line // maybe this is a multiline case if(d->isMultiLineCase(curErrFilter)) { item.type = FilteredItem::ErrorItem; } else { // Okay so we couldn't find anything to indicate an error, but we have file and lineGroup // Lets keep this item clickable and indicate this to the user. item.type = FilteredItem::InformationItem; } } } break; } } return item; } /// --- Script error filter strategy --- // A list of filters for possible Python and PHP errors const QList SCRIPT_ERROR_FILTERS = QList() //QRegExp python("^ File \"(.*)\", line (\\d*), in(.*)$"); << ErrorFormat( "^ File \"(.*)\", line ([0-9]+)(.*$|, in(.*)$)", 1, 2, -1 ) //QRegExp phpstacktrace("^.*(/.*):(\\d*).*$"); << ErrorFormat( "^.*(/.*):([0-9]+).*$", 1, 2, -1 ) //QRegExp phperror("^.* in (/.*) on line (\\d*).*$"); << ErrorFormat( "^.* in (/.*) on line ([0-9]+).*$", 1, 2, -1 ); ScriptErrorFilterStrategy::ScriptErrorFilterStrategy() { } FilteredItem ScriptErrorFilterStrategy::actionInLine(const QString& line) { return FilteredItem(line); } FilteredItem ScriptErrorFilterStrategy::errorInLine(const QString& line) { - FilteredItem item(line); - foreach( const ErrorFormat& curErrFilter, SCRIPT_ERROR_FILTERS ) { - QRegExp regEx = curErrFilter.expression; - if( regEx.indexIn( line ) != -1 ) - { - item.url = regEx.cap( curErrFilter.fileGroup ); - item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; - if(curErrFilter.columnGroup >= 0) { - item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; - } else { - item.columnNo = 0; - } - - QString txt = regEx.cap(curErrFilter.textGroup); - - item.type = FilteredItem::ErrorItem; - - // Make the item clickable if it comes with the necessary file & line number information - if (curErrFilter.fileGroup > 0 && curErrFilter.lineGroup > 0) - item.isActivatable = true; - - break; - } - } - return item; + return FilteringStrategyUtils::match(SCRIPT_ERROR_FILTERS, line); } /// --- Static Analysis filter strategy --- // A list of filters for static analysis tools (krazy2, cppcheck) const QList STATIC_ANALYSIS_FILTERS = QList() // CppCheck << ErrorFormat( "^\\[(.*):([0-9]+)\\]:(.*)", 1, 2, 3 ) // krazy2 << ErrorFormat( "^\\t([^:]+).*line#([0-9]+).*", 1, 2, -1 ) // krazy2 without line info << ErrorFormat( "^\\t(.*): missing license", 1, -1, -1 ); StaticAnalysisFilterStrategy::StaticAnalysisFilterStrategy() { } FilteredItem StaticAnalysisFilterStrategy::actionInLine(const QString& line) { return FilteredItem(line); } FilteredItem StaticAnalysisFilterStrategy::errorInLine(const QString& line) { - FilteredItem item(line); - foreach( const ErrorFormat& curErrFilter, STATIC_ANALYSIS_FILTERS ) { - QRegExp regEx = curErrFilter.expression; - if( regEx.indexIn( line ) != -1 ) - { - item.url = regEx.cap( curErrFilter.fileGroup ); - item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; - if(curErrFilter.columnGroup >= 0) { - item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; - } else { - item.columnNo = 0; - } - - QString txt = regEx.cap(curErrFilter.textGroup); - - item.type = FilteredItem::ErrorItem; - - // Make the item clickable if it comes with the necessary file & line number information - if (curErrFilter.fileGroup > 0 && curErrFilter.lineGroup > 0) { - item.isActivatable = true; - } - break; - } - } - return item; + return FilteringStrategyUtils::match(STATIC_ANALYSIS_FILTERS, line); } - -} // namespace KDevelop - +} diff --git a/outputview/outputfilteringstrategies.h b/outputview/outputfilteringstrategies.h index 43c63bbf43..48847dafa0 100644 --- a/outputview/outputfilteringstrategies.h +++ b/outputview/outputfilteringstrategies.h @@ -1,110 +1,121 @@ /* This file is part of KDevelop Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KDEVPLATFORM_OUTPUTFILTERINGSTRATEGIES_H #define KDEVPLATFORM_OUTPUTFILTERINGSTRATEGIES_H /** * @file This file contains Concrete strategies for filtering output * in KDevelop output model. I.e. classes that inherit from ifilterstrategy. * New filtering strategies should be added here */ #include "ifilterstrategy.h" +#include "outputformats.h" #include - -#include - +#include #include +class KUrl; + namespace KDevelop { +namespace FilteringStrategyUtils +{ + /** + * Looks through @p errorFormats and matches each item against @p line + * + * @return A FilteredItem object for the first match encountered + */ + FilteredItem match(const QList& errorFormats, const QString& line); +} + struct CompilerFilterStrategyPrivate; /** * This filter strategy is for not applying any filtering at all. Implementation of the * interface methods are basically noops **/ class KDEVPLATFORMOUTPUTVIEW_EXPORT NoFilterStrategy : public IFilterStrategy { public: NoFilterStrategy(); virtual FilteredItem errorInLine(QString const& line); virtual FilteredItem actionInLine(QString const& line); }; /** * This filter stategy checks if a given line contains output * that is defined as an error (or an action) from a compiler. **/ class KDEVPLATFORMOUTPUTVIEW_EXPORT CompilerFilterStrategy : public IFilterStrategy { public: CompilerFilterStrategy(KUrl const& buildDir); virtual ~CompilerFilterStrategy(); virtual FilteredItem errorInLine(QString const& line); virtual FilteredItem actionInLine(QString const& line); QVector getCurrentDirs(); private: CompilerFilterStrategyPrivate* const d; }; /** * This filter stategy filters out errors (no actions) from Python and PHP scripts. **/ class KDEVPLATFORMOUTPUTVIEW_EXPORT ScriptErrorFilterStrategy : public IFilterStrategy { public: ScriptErrorFilterStrategy(); virtual FilteredItem errorInLine(QString const& line); virtual FilteredItem actionInLine(QString const& line); }; /** * This filter stategy filters out errors (no actions) from Static code analysis tools (Cppcheck,) **/ class KDEVPLATFORMOUTPUTVIEW_EXPORT StaticAnalysisFilterStrategy : public IFilterStrategy { public: StaticAnalysisFilterStrategy(); virtual FilteredItem errorInLine(QString const& line); virtual FilteredItem actionInLine(QString const& line); }; } // namespace KDevelop #endif // KDEVPLATFORM_OUTPUTFILTERINGSTRATEGIES_H