misra: the user must provide the rule texts in text file.

This commit is contained in:
Daniel Marjamäki 2018-03-24 13:28:40 +01:00
parent e65a5529ad
commit 588ec80122
5 changed files with 74 additions and 41 deletions

View File

@ -5,7 +5,6 @@
# Example usage of this addon (scan a sourcefile main.cpp) # Example usage of this addon (scan a sourcefile main.cpp)
# cppcheck --dump main.cpp # cppcheck --dump main.cpp
# python misra.py --rule-texts=<path-to-rule-texts> 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 # 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 # distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189
@ -36,8 +35,10 @@ def reportError(location, num1, num2):
id = 'misra-c2012-' + str(num1) + '.' + str(num2) id = 'misra-c2012-' + str(num1) + '.' + str(num2)
if num in ruleTexts: if num in ruleTexts:
errmsg = ruleTexts[num] + ' [' + id + ']' errmsg = ruleTexts[num] + ' [' + id + ']'
elif len(ruleTexts) == 0:
errmsg = 'misra violation (use --rule-texts=<file> to get proper output) [' + id + ']'
else: else:
errmsg = 'misra violation (use --misra-pdf=<file> or --rule-texts=<file> to get proper output) [' + id + ']' return
sys.stderr.write('[' + location.file + ':' + str(location.linenr) + '] (style): ' + errmsg + '\n') sys.stderr.write('[' + location.file + ':' + str(location.linenr) + '] (style): ' + errmsg + '\n')
@ -1106,24 +1107,6 @@ def loadRuleTexts(filename):
ruleTexts[num] = ruleTexts[num] + ' ' + line ruleTexts[num] = ruleTexts[num] + ' ' + line
continue continue
def loadRuleTextsFromPdf(filename):
if not os.path.isfile(filename):
print('Fatal error: PDF file is not found: ' + filename)
sys.exit(1)
f = tempfile.NamedTemporaryFile(delete=False)
f.close()
#print('tempfile:' + f.name)
try:
subprocess.call(['pdftotext', filename, f.name])
except OSError as err:
print('Fatal error: Failed to execute pdftotext: ' + str(err))
sys.exit(1)
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: if len(sys.argv) == 1:
print(""" print("""
Syntax: misra.py [OPTIONS] <dumpfiles> Syntax: misra.py [OPTIONS] <dumpfiles>
@ -1132,33 +1115,25 @@ OPTIONS:
--rule-texts=<file> Load rule texts from plain text file. --rule-texts=<file> Load rule texts from plain text file.
If you have the tool 'pdftotext' you can generate If you have the tool 'pdftotext' you might be able
this textfile with such command: to generate this textfile with such command:
$ pdftotext MISRA_C_2012.pdf MISRA_C_2012.txt $ pdftotext MISRA_C_2012.pdf MISRA_C_2012.txt
Otherwise you can more or less copy/paste the chapter Otherwise you can more or less copy/paste the chapter
Appendix A Summary of guidelines Appendix A Summary of guidelines
from the MISRA pdf. from the MISRA pdf. You can buy the MISRA pdf from
http://www.misra.org.uk/
Format: Format:
<..arbitrary text..> <..arbitrary text..>
Appendix A Summary of guidelines Appendix A Summary of guidelines
Dir 1.1 Rule 1.1
Rule text for 1.1 Rule text for 1.1
Dir 1.2 Rule 1.2
Rule text for 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) sys.exit(1)
@ -1171,8 +1146,6 @@ for arg in sys.argv[1:]:
print('Fatal error: file is not found: ' + filename) print('Fatal error: file is not found: ' + filename)
sys.exit(1) sys.exit(1)
loadRuleTexts(filename) loadRuleTexts(filename)
elif arg.startswith('--misra-pdf='):
loadRuleTextsFromPdf(arg[12:])
elif ".dump" in arg: elif ".dump" in arg:
continue continue
else: else:

View File

@ -152,6 +152,7 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent)
connect(mUI.mBtnIncludeDown, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathDown); connect(mUI.mBtnIncludeDown, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathDown);
connect(mUI.mBtnAddSuppression, &QPushButton::clicked, this, &ProjectFileDialog::addSuppression); connect(mUI.mBtnAddSuppression, &QPushButton::clicked, this, &ProjectFileDialog::addSuppression);
connect(mUI.mBtnRemoveSuppression, &QPushButton::clicked, this, &ProjectFileDialog::removeSuppression); connect(mUI.mBtnRemoveSuppression, &QPushButton::clicked, this, &ProjectFileDialog::removeSuppression);
connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile);
loadFromProjectFile(projectFile); loadFromProjectFile(projectFile);
} }
@ -177,7 +178,8 @@ void ProjectFileDialog::saveSettings() const
static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, const QString &dataDir, const QString &addon) static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, const QString &dataDir, const QString &addon)
{ {
cb->setChecked(projectFile->getAddons().contains(addon)); if (projectFile)
cb->setChecked(projectFile->getAddons().contains(addon));
if (CheckThread::getAddonFilePath(dataDir, addon + ".py").isEmpty()) { if (CheckThread::getAddonFilePath(dataDir, addon + ".py").isEmpty()) {
cb->setEnabled(false); cb->setEnabled(false);
cb->setText(cb->text() + QObject::tr(" (Not found)")); cb->setText(cb->text() + QObject::tr(" (Not found)"));
@ -231,6 +233,16 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
updateAddonCheckBox(mUI.mAddonCert, projectFile, dataDir, "cert"); updateAddonCheckBox(mUI.mAddonCert, projectFile, dataDir, "cert");
updateAddonCheckBox(mUI.mAddonMisra, projectFile, dataDir, "misra"); updateAddonCheckBox(mUI.mAddonMisra, projectFile, dataDir, "misra");
const QString &misraFile = settings.value(SETTINGS_MISRA_FILE, QString()).toString();
mUI.mEditMisraFile->setText(misraFile);
if (!mUI.mAddonMisra->isEnabled()) {
mUI.mEditMisraFile->setEnabled(false);
mUI.mBtnBrowseMisraFile->setEnabled(false);
} else if (misraFile.isEmpty()) {
mUI.mAddonMisra->setEnabled(false);
mUI.mAddonMisra->setText(mUI.mAddonMisra->text() + ' ' + tr("(no rule texts file)"));
}
mUI.mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer()); mUI.mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer());
mUI.mToolClangTidy->setChecked(projectFile->getClangTidy()); mUI.mToolClangTidy->setChecked(projectFile->getClangTidy());
if (CheckThread::clangTidyCmd().isEmpty()) { if (CheckThread::clangTidyCmd().isEmpty()) {
@ -658,3 +670,17 @@ void ProjectFileDialog::removeSuppression()
QListWidgetItem *item = mUI.mListSuppressions->takeItem(row); QListWidgetItem *item = mUI.mListSuppressions->takeItem(row);
delete item; delete item;
} }
void ProjectFileDialog::browseMisraFile()
{
const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA rule texts file"), QDir::homePath(), tr("Misra rule texts file (%1)").arg("*.txt"));
if (!fileName.isEmpty()) {
QSettings settings;
mUI.mEditMisraFile->setText(fileName);
settings.setValue(SETTINGS_MISRA_FILE, fileName);
mUI.mAddonMisra->setText("MISRA C 2012");
mUI.mAddonMisra->setEnabled(true);
updateAddonCheckBox(mUI.mAddonMisra, nullptr, settings.value("DATADIR", QString()).toString(), "misra");
}
}

View File

@ -230,6 +230,11 @@ protected slots:
*/ */
void removeSuppression(); void removeSuppression();
/**
* @brief Browse for misra file
*/
void browseMisraFile();
protected: protected:
/** /**

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>642</width> <width>642</width>
<height>507</height> <height>549</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -17,7 +17,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>3</number>
</property> </property>
<widget class="QWidget" name="mTabPathsAndDefines"> <widget class="QWidget" name="mTabPathsAndDefines">
<attribute name="title"> <attribute name="title">
@ -524,6 +524,31 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Misra rule texts</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mEditMisraFile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copy/paste the text from Appendix A &amp;quot;Summary of guidelines&amp;quot; from the MISRA C 2012 pdf to a text file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mBtnBrowseMisraFile">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -317,12 +317,16 @@
<item> <item>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Misra PDF/Text file</string> <string>Misra rule texts file</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="mEditMisraFile"/> <widget class="QLineEdit" name="mEditMisraFile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copy/paste the text from Appendix A &amp;quot;Summary of guidelines&amp;quot; from the MISRA C 2012 pdf to a text file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="mBtnBrowseMisraFile"> <widget class="QPushButton" name="mBtnBrowseMisraFile">