Misra: Integration of MISRA in the GUI. The misra addon can now extract the rule texts from the PDF.
This commit is contained in:
parent
0a70b8794c
commit
6f2d4361df
|
@ -4,7 +4,8 @@
|
|||
#
|
||||
# Example usage of this addon (scan a sourcefile main.cpp)
|
||||
# cppcheck --dump main.cpp
|
||||
# python misra.py main.cpp.dump
|
||||
# python misra.py --rule-texts=<path-to-rule-texts> main.cpp.dump
|
||||
# python misra.py --misra-pdf=<path-to-misra.pdf> main.cpp.dump
|
||||
#
|
||||
# Limitations: This addon is released as open source. Rule texts can't be freely
|
||||
# distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189
|
||||
|
@ -14,6 +15,9 @@
|
|||
import cppcheckdata
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
|
||||
ruleTexts = {}
|
||||
|
||||
|
@ -31,8 +35,8 @@ def reportError(location, num1, num2):
|
|||
if num in ruleTexts:
|
||||
errmsg = ruleTexts[num] + ' [' + id + ']'
|
||||
else:
|
||||
errmsg = 'misra violation (use --rule-texts=<file> to get proper output) [' + id + ']'
|
||||
sys.stderr.write('[' + location.file + ':' + str(location.linenr) + '] ' + errmsg + '\n')
|
||||
errmsg = 'misra violation (use --misra-pdf=<file> or --rule-texts=<file> to get proper output) [' + id + ']'
|
||||
sys.stderr.write('[' + location.file + ':' + str(location.linenr) + '] (style): ' + errmsg + '\n')
|
||||
|
||||
|
||||
def simpleMatch(token, pattern):
|
||||
|
@ -1024,8 +1028,15 @@ def misra_21_11(data):
|
|||
def loadRuleTexts(filename):
|
||||
num1 = 0
|
||||
num2 = 0
|
||||
appendixA = False
|
||||
for line in open(filename, 'rt'):
|
||||
line = line.replace('\r', '').replace('\n', '')
|
||||
if not appendixA:
|
||||
if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10:
|
||||
appendixA = True
|
||||
continue
|
||||
if line.find('Appendix B') >= 0:
|
||||
break
|
||||
res = re.match(r'^Rule ([0-9]+).([0-9]+)', line)
|
||||
if res:
|
||||
num1 = int(res.group(1))
|
||||
|
@ -1041,12 +1052,57 @@ def loadRuleTexts(filename):
|
|||
num2 = num2 + 1
|
||||
continue
|
||||
|
||||
def loadRuleTextsFromPdf(filename):
|
||||
f = tempfile.NamedTemporaryFile(delete=False)
|
||||
f.close()
|
||||
print('tempfile:' + f.name)
|
||||
subprocess.call(['pdftotext', filename, f.name])
|
||||
loadRuleTexts(f.name)
|
||||
try:
|
||||
os.remove(f.name)
|
||||
except OSError as err:
|
||||
print('Failed to remove temporary file ' + f.name)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
print("""
|
||||
Syntax: misra.py [OPTIONS] <dumpfiles>
|
||||
|
||||
OPTIONS:
|
||||
|
||||
--rule-texts=<file> Load rule texts from plain text file.
|
||||
|
||||
You can more or less copy/paste the chapter:
|
||||
Appendix A Summary of guidelines
|
||||
from the MISRA pdf.
|
||||
|
||||
Format:
|
||||
|
||||
<..arbitrary text..>
|
||||
Appendix A Summary of guidelines
|
||||
Dir 1.1
|
||||
Rule text for 1.1
|
||||
Dir 1.2
|
||||
Rule text for 1.2
|
||||
<...>
|
||||
|
||||
--misra-pdf=<file> Misra PDF file that rule texts will be extracted from.
|
||||
|
||||
The tool 'pdftotext' from xpdf is used and must be installed.
|
||||
Debian: sudo apt-get install xpdf
|
||||
Windows: http://gnuwin32.sourceforge.net/packages/xpdf.htm
|
||||
|
||||
If you don't have 'pdftotext' and don't want to install it then
|
||||
you can use --rule-texts=<file>.
|
||||
""")
|
||||
sys.exit(1)
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == '-verify':
|
||||
VERIFY = True
|
||||
elif arg.startswith('--rule-texts='):
|
||||
loadRuleTexts(arg[13:])
|
||||
elif arg.startswith('--misra-pdf='):
|
||||
loadRuleTextsFromPdf(arg[12:])
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if not arg.endswith('.dump'):
|
||||
|
|
|
@ -287,6 +287,12 @@ void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSetti
|
|||
|
||||
QStringList args;
|
||||
args << addonFilePath << dumpFile;
|
||||
if (addon == "misra" && !mMisraFile.isEmpty() && QFileInfo(mMisraFile).exists()) {
|
||||
if (mMisraFile.endsWith('.pdf', Qt::CaseInsensitive))
|
||||
args << "--misra-pdf=" + mMisraFile;
|
||||
else
|
||||
args << "--rule-texts=" + mMisraFile;
|
||||
}
|
||||
qDebug() << python << args;
|
||||
|
||||
QProcess process;
|
||||
|
|
|
@ -56,6 +56,10 @@ public:
|
|||
mAddonsAndTools = addonsAndTools;
|
||||
}
|
||||
|
||||
void setMisraFile(const QString &misraFile) {
|
||||
mMisraFile = misraFile;
|
||||
}
|
||||
|
||||
void setDataDir(const QString &dataDir) {
|
||||
mDataDir = dataDir;
|
||||
}
|
||||
|
@ -148,6 +152,7 @@ private:
|
|||
QString mDataDir;
|
||||
QStringList mClangIncludePaths;
|
||||
QStringList mSuppressions;
|
||||
QString mMisraFile;
|
||||
};
|
||||
/// @}
|
||||
#endif // CHECKTHREAD_H
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#define SETTINGS_LANGUAGE "Application language"
|
||||
#define SETTINGS_GLOBAL_INCLUDE_PATHS "Global include paths"
|
||||
#define SETTINGS_PYTHON_PATH "Python path"
|
||||
#define SETTINGS_MISRA_FILE "MISRA C 2012 file"
|
||||
#define SETTINGS_CLANG_PATH "Clang path"
|
||||
#define SETTINGS_VS_INCLUDE_PATHS "VS include paths"
|
||||
#define SETTINGS_INLINE_SUPPRESSIONS "Inline suppressions"
|
||||
|
|
|
@ -456,7 +456,7 @@ void MainWindow::doAnalyzeProject(ImportProject p)
|
|||
|
||||
//mThread->SetanalyzeProject(true);
|
||||
if (mProjectFile) {
|
||||
mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools());
|
||||
mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools(), mSettings->value(SETTINGS_MISRA_FILE).toString());
|
||||
QString clangHeaders = mSettings->value(SETTINGS_VS_INCLUDE_PATHS).toString();
|
||||
mThread->setClangIncludePaths(clangHeaders.split(";"));
|
||||
mThread->setSuppressions(mProjectFile->getSuppressions());
|
||||
|
@ -498,6 +498,8 @@ void MainWindow::doAnalyzeFiles(const QStringList &files)
|
|||
mUI.mResults->checkingStarted(fileNames.count());
|
||||
|
||||
mThread->setFiles(fileNames);
|
||||
if (mProjectFile)
|
||||
mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools(), mSettings->value(SETTINGS_MISRA_FILE).toString());
|
||||
QDir inf(mCurrentDirectory);
|
||||
const QString checkPath = inf.canonicalPath();
|
||||
setPath(SETTINGS_LAST_CHECK_PATH, checkPath);
|
||||
|
@ -1428,7 +1430,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile)
|
|||
QFileInfo inf(projectFile->getFilename());
|
||||
const QString rootpath = projectFile->getRootPath();
|
||||
|
||||
mThread->setAddonsAndTools(projectFile->getAddonsAndTools());
|
||||
mThread->setAddonsAndTools(projectFile->getAddonsAndTools(), mSettings->value(SETTINGS_MISRA_FILE).toString());
|
||||
mUI.mResults->setTags(projectFile->getTags());
|
||||
|
||||
// If the root path is not given or is not "current dir", use project
|
||||
|
|
|
@ -163,9 +163,11 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
updateAddonCheckBox(mUI.mAddonThreadSafety, projectFile, dataDir, "threadsafety");
|
||||
updateAddonCheckBox(mUI.mAddonY2038, projectFile, dataDir, "y2038");
|
||||
updateAddonCheckBox(mUI.mAddonCert, projectFile, dataDir, "cert");
|
||||
updateAddonCheckBox(mUI.mAddonMisra, projectFile, dataDir, "misra");
|
||||
|
||||
mUI.mAddonY2038->setChecked(projectFile->getAddons().contains("y2038"));
|
||||
mUI.mAddonCert->setChecked(projectFile->getAddons().contains("cert"));
|
||||
mUI.mAddonMisra->setChecked(projectFile->getAddons().contains("misra"));
|
||||
mUI.mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer());
|
||||
mUI.mToolClangTidy->setChecked(projectFile->getClangTidy());
|
||||
if (CheckThread::clangTidyCmd().isEmpty()) {
|
||||
|
@ -202,6 +204,8 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
list << "y2038";
|
||||
if (mUI.mAddonCert->isChecked())
|
||||
list << "cert";
|
||||
if (mUI.mAddonMisra->isChecked())
|
||||
list << "misra";
|
||||
projectFile->setAddons(list);
|
||||
projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked());
|
||||
projectFile->setClangTidy(mUI.mToolClangTidy->isChecked());
|
||||
|
|
|
@ -493,6 +493,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="mAddonMisra">
|
||||
<property name="text">
|
||||
<string>MISRA C 2012</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -306,6 +306,46 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Misra addon: rule texts</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>The Misra addon can read the rule texts from a file; either a Misra PDF or a plaintext file.</p><p>PDF: To extract text from the MISRA PDF, the misra addon uses the tool 'pdftotext' available in xpdf (crossplatform open source tool). That tool must be installed.</p><p>Text: Copy/paste the text in the chapter &quot;Appendix A Summary of guidelines&quot; from the MISRA pdf into a text file.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Misra PDF/Text file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mEditMisraFile"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="mBtnBrowseMisraFile">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -54,6 +54,7 @@ SettingsDialog::SettingsDialog(ApplicationList *list,
|
|||
mUI.mShowStatistics->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_STATISTICS, false).toBool()));
|
||||
mUI.mShowErrorId->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_ERROR_ID, false).toBool()));
|
||||
mUI.mEditPythonPath->setText(settings.value(SETTINGS_PYTHON_PATH, QString()).toString());
|
||||
mUI.mEditMisraFile->setText(settings.value(SETTINGS_MISRA_FILE, QString()).toString());
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
//mUI.mTabClang->setVisible(true);
|
||||
|
@ -77,6 +78,7 @@ SettingsDialog::SettingsDialog(ApplicationList *list,
|
|||
this, SLOT(editApplication()));
|
||||
|
||||
connect(mUI.mBtnBrowsePythonPath, &QPushButton::clicked, this, &SettingsDialog::browsePythonPath);
|
||||
connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &SettingsDialog::browseMisraFile);
|
||||
|
||||
mUI.mListWidget->setSortingEnabled(false);
|
||||
populateApplicationList();
|
||||
|
@ -161,6 +163,7 @@ void SettingsDialog::saveSettingValues() const
|
|||
saveCheckboxValue(&settings, mUI.mShowStatistics, SETTINGS_SHOW_STATISTICS);
|
||||
saveCheckboxValue(&settings, mUI.mShowErrorId, SETTINGS_SHOW_ERROR_ID);
|
||||
settings.setValue(SETTINGS_PYTHON_PATH, mUI.mEditPythonPath->text());
|
||||
settings.setValue(SETTINGS_MISRA_FILE, mUI.mEditMisraFile->text());
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
settings.setValue(SETTINGS_CLANG_PATH, mUI.mEditClangPath->text());
|
||||
|
@ -306,6 +309,13 @@ void SettingsDialog::browsePythonPath()
|
|||
mUI.mEditPythonPath->setText(fileName);
|
||||
}
|
||||
|
||||
void SettingsDialog::browseMisraFile()
|
||||
{
|
||||
const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA File"), QDir::homePath(), "Misra File (*.pdf *.txt)");
|
||||
if (!fileName.isEmpty())
|
||||
mUI.mEditMisraFile->setText(fileName);
|
||||
}
|
||||
|
||||
void SettingsDialog::browseClangPath()
|
||||
{
|
||||
QString selectedDir = QFileDialog::getExistingDirectory(this,
|
||||
|
|
|
@ -129,6 +129,11 @@ protected slots:
|
|||
|
||||
/** @brief Slot for browsing for the clang binary */
|
||||
void browseClangPath();
|
||||
|
||||
/**
|
||||
* @brief Browse for MISRA file
|
||||
*/
|
||||
void browseMisraFile();
|
||||
protected:
|
||||
/**
|
||||
* @brief Clear all applications from the list and re insert them from mTempApplications
|
||||
|
|
|
@ -94,6 +94,7 @@ void ThreadHandler::check(const Settings &settings)
|
|||
|
||||
for (int i = 0; i < mRunningThreadCount; i++) {
|
||||
mThreads[i]->setAddonsAndTools(mAddonsAndTools);
|
||||
mThreads[i]->setMisraFile(mMisraFile);
|
||||
mThreads[i]->setSuppressions(mSuppressions);
|
||||
mThreads[i]->setClangIncludePaths(mClangIncludePaths);
|
||||
mThreads[i]->setDataDir(mDataDir);
|
||||
|
|
|
@ -71,8 +71,9 @@ public:
|
|||
*/
|
||||
void saveSettings(QSettings &settings) const;
|
||||
|
||||
void setAddonsAndTools(const QStringList &addonsAndTools) {
|
||||
void setAddonsAndTools(const QStringList &addonsAndTools, const QString misraFile) {
|
||||
mAddonsAndTools = addonsAndTools;
|
||||
mMisraFile = misraFile;
|
||||
}
|
||||
|
||||
void setSuppressions(const QStringList &s) {
|
||||
|
@ -258,6 +259,7 @@ protected:
|
|||
QStringList mClangIncludePaths;
|
||||
|
||||
QString mDataDir;
|
||||
QString mMisraFile;
|
||||
private:
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue