5959
6060#include < algorithm>
6161#include < cstddef>
62+ #include < fstream>
6263#include < iterator>
6364#include < list>
6465#include < memory>
9697#include < QtNetwork/QNetworkRequest>
9798#include < QPushButton>
9899#include < QRegularExpression>
100+ #include < QSet>
99101#include < QSettings>
100102#include < QSize>
101103#include < QTimer>
@@ -118,6 +120,18 @@ static QString fromNativePath(const QString& p) {
118120#endif
119121}
120122
123+ // The files selected in the results view are not necessarily spelled the same way as the
124+ // project or files.txt spells them, compare them as absolute paths
125+ static QString comparablePath (const QString& path) {
126+ return QDir::cleanPath (QFileInfo (path).absoluteFilePath ());
127+ }
128+
129+ static QSet<QString> comparablePaths (const QStringList& paths) {
130+ QSet<QString> ret;
131+ std::transform (paths.cbegin (), paths.cend (), std::inserter (ret, ret.end ()), comparablePath);
132+ return ret;
133+ }
134+
121135template <typename T = std::vector<std::string>>
122136static T toStdStringList (const QStringList& stringList) {
123137 T ret;
@@ -568,21 +582,31 @@ void MainWindow::saveSettings() const
568582 mUI ->mResults ->saveSettings (mSettings );
569583}
570584
571- void MainWindow::doAnalyzeProject (ImportProject p, const bool checkLib, const bool checkConfig, const QStringList& recheckFiles )
585+ void MainWindow::doAnalyzeProject (ImportProject p, const bool checkLib, const bool checkConfig, const QStringList& selectedFiles, const Recheck recheck )
572586{
573587 Settings checkSettings;
574588 auto supprs = std::make_shared<Suppressions>();
575589 if (!getCppcheckSettings (checkSettings, *supprs))
576590 return ;
577591
578- // filter requested files
579- if (!recheckFiles.isEmpty ()) {
592+ // Filter the selected files
593+ if (recheck == Recheck::Selected) {
594+ const QSet<QString> selected = comparablePaths (selectedFiles);
580595 p.fileSettings .remove_if ([&](const FileSettings& fs) {
581- return !recheckFiles .contains (QString::fromStdString (fs.filename ()));
596+ return !selected .contains (comparablePath ( QString::fromStdString (fs.filename () )));
582597 });
598+ if (p.fileSettings .empty ()) {
599+ QMessageBox msg (QMessageBox::Information,
600+ " Cppcheck" ,
601+ tr (" None of the selected files is part of the project, nothing to analyze." ),
602+ QMessageBox::Ok,
603+ this );
604+ msg.exec ();
605+ return ;
606+ }
583607 }
584608
585- clearResults ( );
609+ clearResultsForAnalysis (checkSettings. buildDir , selectedFiles, recheck );
586610
587611 mIsLogfileLoaded = false ;
588612 if (mProjectFile ) {
@@ -647,7 +671,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bo
647671 mUI ->mResults ->setCheckSettings (checkSettings);
648672}
649673
650- void MainWindow::doAnalyzeFiles (const QStringList &files, const bool checkLib, const bool checkConfig)
674+ void MainWindow::doAnalyzeFiles (const QStringList &files, const bool checkLib, const bool checkConfig, const QStringList& selectedFiles, const Recheck recheck )
651675{
652676 if (files.isEmpty ())
653677 return ;
@@ -657,7 +681,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, c
657681 if (!getCppcheckSettings (checkSettings, *supprs))
658682 return ;
659683
660- clearResults ( );
684+ clearResultsForAnalysis (checkSettings. buildDir , selectedFiles, recheck );
661685
662686 mIsLogfileLoaded = false ;
663687 FileList pathList;
@@ -669,7 +693,6 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, c
669693 }
670694 QStringList fileNames = pathList.getFileList ();
671695
672- mUI ->mResults ->clear (true );
673696 mUI ->mResults ->setResultsSource (ResultsTree::ResultsSource::Analysis);
674697 mThread ->clearFiles ();
675698
@@ -1379,11 +1402,6 @@ void MainWindow::reAnalyzeSelected(const QStringList& files)
13791402 return ;
13801403
13811404 if (mProjectFile ) {
1382- // Clear details, statistics and progress
1383- mUI ->mResults ->clear (false );
1384- for (int i = 0 ; i < files.size (); ++i)
1385- mUI ->mResults ->clearRecheckFile (files[i]);
1386-
13871405 analyzeProject (mProjectFile , files);
13881406 return ;
13891407 }
@@ -1393,10 +1411,7 @@ void MainWindow::reAnalyzeSelected(const QStringList& files)
13931411 if (!getCppcheckSettings (checkSettings, *supprs))
13941412 return ;
13951413
1396- // Clear details, statistics and progress
1397- mUI ->mResults ->clear (false );
1398- for (int i = 0 ; i < files.size (); ++i)
1399- mUI ->mResults ->clearRecheckFile (files[i]);
1414+ clearResultsForAnalysis (checkSettings.buildDir , files, Recheck::Selected);
14001415
14011416 mCurrentDirectory = mUI ->mResults ->getCheckDirectory ();
14021417 FileList pathList;
@@ -1451,7 +1466,7 @@ void MainWindow::reAnalyze(bool all)
14511466void MainWindow::clearResults ()
14521467{
14531468 if (mProjectFile && !mProjectFile ->getBuildDir ().isEmpty ()) {
1454- QDir dir (QFileInfo (mProjectFile ->getFilename ()).absolutePath () + ' / ' + mProjectFile ->getBuildDir ());
1469+ QDir dir (QDir ( QFileInfo (mProjectFile ->getFilename ()).absolutePath ()). filePath ( mProjectFile ->getBuildDir () ));
14551470 for (const QString& f: dir.entryList (QDir::Files)) {
14561471 if (!f.endsWith (" files.txt" )) {
14571472 static const QRegularExpression rx (" ^.*.s[0-9]+$" );
@@ -1465,6 +1480,42 @@ void MainWindow::clearResults()
14651480 enableResultsButtons ();
14661481}
14671482
1483+ void MainWindow::clearResultsForAnalysis (const std::string& buildDir, const QStringList& selectedFiles, const Recheck recheck)
1484+ {
1485+ switch (recheck) {
1486+ case Recheck::None:
1487+ clearResults ();
1488+ break ;
1489+ case Recheck::Selected:
1490+ // Only the selected files are analyzed, the results of the other files are kept
1491+ mUI ->mResults ->clear (false );
1492+ for (const QString& f : selectedFiles)
1493+ mUI ->mResults ->clearRecheckFile (f);
1494+ enableResultsButtons ();
1495+ break ;
1496+ case Recheck::Cached: {
1497+ // Every file reports its results again, either analyzed or replayed from the analyzer
1498+ // information. Remove the analyzer information of the selected files so that they really
1499+ // are analyzed again, also by the clang tools that keep their results next to it.
1500+ const QSet<QString> selected = comparablePaths (selectedFiles);
1501+ QDir dir (QString::fromStdString (buildDir));
1502+ std::ifstream filesTxt (buildDir + " /files.txt" );
1503+ std::string line;
1504+ while (std::getline (filesTxt, line)) {
1505+ AnalyzerInformation::Info info;
1506+ if (!info.parse (line) || !selected.contains (comparablePath (QString::fromStdString (info.sourceFile ))))
1507+ continue ;
1508+ const QString afile = QString::fromStdString (info.afile );
1509+ for (const QString& f : dir.entryList ({afile, afile + " .*" }, QDir::Files))
1510+ dir.remove (f);
1511+ }
1512+ mUI ->mResults ->clear (true );
1513+ enableResultsButtons ();
1514+ break ;
1515+ }
1516+ }
1517+ }
1518+
14681519void MainWindow::openResults ()
14691520{
14701521 if (mUI ->mResults ->hasResults ()) {
@@ -1863,10 +1914,17 @@ bool MainWindow::loadLastResults()
18631914 return true ;
18641915}
18651916
1866- void MainWindow::analyzeProject (const ProjectFile *projectFile, const QStringList& recheckFiles , const bool checkLib, const bool checkConfig)
1917+ void MainWindow::analyzeProject (const ProjectFile *projectFile, const QStringList& selectedFiles , const bool checkLib, const bool checkConfig)
18671918{
18681919 Settings::terminate (false );
18691920
1921+ // With a build directory the whole project is analyzed: the analyzer information cache skips
1922+ // the unchanged files, and files.txt stays complete so whole program analysis sees every file.
1923+ // Without a build directory there is no cache, so only the selected files are analyzed.
1924+ Recheck recheck = Recheck::None;
1925+ if (!selectedFiles.isEmpty ())
1926+ recheck = projectFile->getBuildDir ().isEmpty () ? Recheck::Selected : Recheck::Cached;
1927+
18701928 QFileInfo inf (projectFile->getFilename ());
18711929 const QString& rootpath = projectFile->getRootPath ();
18721930
@@ -1968,11 +2026,11 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringLis
19682026 msg.exec ();
19692027 return ;
19702028 }
1971- doAnalyzeProject (p, checkLib, checkConfig, recheckFiles ); // TODO: avoid copy
2029+ doAnalyzeProject (p, checkLib, checkConfig, selectedFiles, recheck ); // TODO: avoid copy
19722030 return ;
19732031 }
19742032
1975- QStringList paths = recheckFiles. isEmpty () ? projectFile->getCheckPaths () : recheckFiles ;
2033+ QStringList paths = recheck == Recheck::Selected ? selectedFiles : projectFile->getCheckPaths ();
19762034
19772035 // If paths not given then check the root path (which may be the project
19782036 // file's location, see above). This is to keep the compatibility with
@@ -1981,7 +2039,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringLis
19812039 if (paths.isEmpty ()) {
19822040 paths << mCurrentDirectory ;
19832041 }
1984- doAnalyzeFiles (paths, checkLib, checkConfig);
2042+ doAnalyzeFiles (paths, checkLib, checkConfig, selectedFiles, recheck );
19852043}
19862044
19872045void MainWindow::newProjectFile ()
0 commit comments