Skip to content

Commit 47a9df5

Browse files
authored
Fix #13832 (cppcheck build dir: do not reuse cached results if there were invalidLicense errors) (danmar#7606)
Analyzer info is scanned for invalid license errors before the cache is used. Fixes https://trac.cppcheck.net/ticket/13832.
1 parent fcb330f commit 47a9df5

File tree

5 files changed

+183
-9
lines changed

5 files changed

+183
-9
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ test/options.o: test/options.cpp test/options.h
710710
test/test64bit.o: test/test64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
711711
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/test64bit.cpp
712712

713-
test/testanalyzerinformation.o: test/testanalyzerinformation.cpp lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h
713+
test/testanalyzerinformation.o: test/testanalyzerinformation.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h lib/xml.h test/fixture.h
714714
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testanalyzerinformation.cpp
715715

716716
test/testassert.o: test/testassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h

lib/analyzerinfo.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,21 +83,26 @@ void AnalyzerInformation::close()
8383
}
8484
}
8585

86-
static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, std::list<ErrorMessage> &errors)
86+
bool AnalyzerInformation::skipAnalysis(const tinyxml2::XMLDocument &analyzerInfoDoc, std::size_t hash, std::list<ErrorMessage> &errors)
8787
{
88-
tinyxml2::XMLDocument doc;
89-
const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str());
90-
if (error != tinyxml2::XML_SUCCESS)
91-
return false;
92-
93-
const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement();
88+
const tinyxml2::XMLElement * const rootNode = analyzerInfoDoc.FirstChildElement();
9489
if (rootNode == nullptr)
9590
return false;
9691

9792
const char *attr = rootNode->Attribute("hash");
9893
if (!attr || attr != std::to_string(hash))
9994
return false;
10095

96+
// Check for invalid license error or internal error, in which case we should retry analysis
97+
for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
98+
if (std::strcmp(e->Name(), "error") == 0 &&
99+
(e->Attribute("id", "premium-invalidLicense") ||
100+
e->Attribute("id", "premium-internalError") ||
101+
e->Attribute("id", "internalError")
102+
))
103+
return false;
104+
}
105+
101106
for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
102107
if (std::strcmp(e->Name(), "error") == 0)
103108
errors.emplace_back(e);
@@ -147,7 +152,9 @@ bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::st
147152

148153
mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg,fileIndex);
149154

150-
if (skipAnalysis(mAnalyzerInfoFile, hash, errors))
155+
tinyxml2::XMLDocument analyzerInfoDoc;
156+
const tinyxml2::XMLError xmlError = analyzerInfoDoc.LoadFile(mAnalyzerInfoFile.c_str());
157+
if (xmlError == tinyxml2::XML_SUCCESS && skipAnalysis(analyzerInfoDoc, hash, errors))
151158
return false;
152159

153160
mOutputStream.open(mAnalyzerInfoFile);

lib/analyzerinfo.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
class ErrorMessage;
3232
struct FileSettings;
3333

34+
namespace tinyxml2 {
35+
class XMLDocument;
36+
};
37+
3438
/// @addtogroup Core
3539
/// @{
3640

@@ -49,6 +53,8 @@ struct FileSettings;
4953
*/
5054
class CPPCHECKLIB AnalyzerInformation {
5155
public:
56+
friend class TestAnalyzerInformation;
57+
5258
~AnalyzerInformation();
5359

5460
static std::string getFilesTxt(const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings);
@@ -75,7 +81,10 @@ class CPPCHECKLIB AnalyzerInformation {
7581

7682
protected:
7783
static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, int fileIndex);
84+
7885
private:
86+
static bool skipAnalysis(const tinyxml2::XMLDocument &analyzerInfoDoc, std::size_t hash, std::list<ErrorMessage> &errors);
87+
7988
std::ofstream mOutputStream;
8089
std::string mAnalyzerInfoFile;
8190
};

test/cli/premium_test.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,29 @@ def test_misra_py(tmpdir):
130130
_, stdout, _ = cppcheck(['--enable=style', '--premium=misra-c-2012', test_file], cppcheck_exe=exe)
131131
assert 'misra.py' not in stdout # Did not find misra.py
132132
assert 'Checking' in stdout
133+
134+
135+
def test_invalid_license_retry(tmpdir):
136+
# Trac 13832 - cppcheck build dir: do not reuse cached results if there were invalidLicense errors
137+
build_dir = os.path.join(tmpdir, 'b')
138+
test_file = os.path.join(tmpdir, 'test.c')
139+
addon_file = os.path.join(tmpdir, 'premiumaddon.py')
140+
141+
os.mkdir(build_dir)
142+
143+
with open(test_file, 'wt') as f:
144+
f.write('void foo();\n')
145+
146+
args = [f"--addon={addon_file}", f"--cppcheck-build-dir={build_dir}", '--xml', '--enable=all', test_file]
147+
148+
with open(addon_file, 'wt') as f:
149+
f.write('print(\'{"addon":"premium","column":0,"errorId":"invalidLicense","extra":"","file":"Cppcheck Premium","linenr":0,"message":"Invalid license: No license file was found, contact sales@cppchecksolutions.com","severity":"error"}\')')
150+
151+
_, _, stderr = cppcheck(args)
152+
assert 'Invalid license' in stderr
153+
154+
with open(addon_file, 'wt') as f:
155+
f.write('')
156+
157+
_, _, stderr = cppcheck(args)
158+
assert 'Invalid license' not in stderr

test/testanalyzerinformation.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
#include "analyzerinfo.h"
2121
#include "filesettings.h"
2222
#include "fixture.h"
23+
#include "xml.h"
2324

2425
#include <sstream>
26+
#include <tinyxml2.h>
2527

2628
class TestAnalyzerInformation : public TestFixture, private AnalyzerInformation {
2729
public:
@@ -34,6 +36,7 @@ class TestAnalyzerInformation : public TestFixture, private AnalyzerInformation
3436
TEST_CASE(duplicateFile);
3537
TEST_CASE(filesTextDuplicateFile);
3638
TEST_CASE(parse);
39+
TEST_CASE(skipAnalysis);
3740
}
3841

3942
void getAnalyzerInfoFile() const {
@@ -95,6 +98,135 @@ class TestAnalyzerInformation : public TestFixture, private AnalyzerInformation
9598
ASSERT_EQUALS(0, info.fileIndex);
9699
ASSERT_EQUALS("C:/dm/cppcheck-fix-13333/test/cli/whole-program/odr1.cpp", info.sourceFile);
97100
}
101+
102+
void skipAnalysis() const {
103+
// Matching hash with license error (don't skip)
104+
{
105+
std::list<ErrorMessage> errorList;
106+
tinyxml2::XMLDocument doc;
107+
108+
const tinyxml2::XMLError xmlError = doc.Parse(
109+
"<?xml version=\"1.0\"?>"
110+
"<analyzerinfo hash=\"100\">"
111+
"<error id=\"premium-invalidLicense\" severity=\"error\" msg=\"Invalid license: No license file was found, contact sales@cppchecksolutions.com\" verbose=\"Invalid license: No license file was found, contact sales@cppchecksolutions.com\" file0=\"test.c\">"
112+
"<location file=\"Cppcheck Premium\" line=\"0\" column=\"0\"/>"
113+
"</error>"
114+
"</analyzerinfo>"
115+
);
116+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError);
117+
118+
ASSERT_EQUALS(false, AnalyzerInformation::skipAnalysis(doc, 100, errorList));
119+
ASSERT_EQUALS(0, errorList.size());
120+
}
121+
122+
// Matching hash with premium internal error (don't skip)
123+
{
124+
std::list<ErrorMessage> errorList;
125+
tinyxml2::XMLDocument doc;
126+
127+
const tinyxml2::XMLError xmlError = doc.Parse(
128+
"<?xml version=\"1.0\"?>"
129+
"<analyzerinfo hash=\"100\">"
130+
"<error id=\"premium-internalError\" severity=\"error\" msg=\"Something went wrong\" verbose=\"Something went wrong\" file0=\"test.c\">"
131+
"<location file=\"Cppcheck\" line=\"0\" column=\"0\"/>"
132+
"</error>"
133+
"</analyzerinfo>"
134+
);
135+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError);
136+
137+
ASSERT_EQUALS(false, AnalyzerInformation::skipAnalysis(doc, 100, errorList));
138+
ASSERT_EQUALS(0, errorList.size());
139+
}
140+
141+
// Matching hash with internal error (don't skip)
142+
{
143+
std::list<ErrorMessage> errorList;
144+
tinyxml2::XMLDocument doc;
145+
146+
const tinyxml2::XMLError xmlError = doc.Parse(
147+
"<?xml version=\"1.0\"?>"
148+
"<analyzerinfo hash=\"100\">"
149+
"<error id=\"internalError\" severity=\"error\" msg=\"Something went wrong\" verbose=\"Something went wrong\" file0=\"test.c\">"
150+
"<location file=\"Cppcheck\" line=\"0\" column=\"0\"/>"
151+
"</error>"
152+
"</analyzerinfo>"
153+
);
154+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError);
155+
156+
ASSERT_EQUALS(false, AnalyzerInformation::skipAnalysis(doc, 100, errorList));
157+
ASSERT_EQUALS(0, errorList.size());
158+
}
159+
160+
// Matching hash with normal error (skip)
161+
{
162+
std::list<ErrorMessage> errorList;
163+
tinyxml2::XMLDocument doc;
164+
165+
const tinyxml2::XMLError xmlError = doc.Parse(
166+
"<?xml version=\"1.0\"?>"
167+
"<analyzerinfo hash=\"100\">"
168+
"<error id=\"nullPointer\" severity=\"error\" msg=\"Null pointer dereference: ptr\" verbose=\"Null pointer dereference: ptr\" cwe=\"476\" file0=\"test.c\">"
169+
"<location file=\"test.c\" line=\"4\" column=\"3\" info=\"Null pointer dereference\"/>"
170+
"<location file=\"test.c\" line=\"3\" column=\"12\" info=\"Assignment &apos;ptr=NULL&apos;, assigned value is 0\"/>"
171+
"<symbol>ptr</symbol>"
172+
"</error>"
173+
"</analyzerinfo>"
174+
);
175+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError);
176+
177+
ASSERT_EQUALS(true, AnalyzerInformation::skipAnalysis(doc, 100, errorList));
178+
ASSERT_EQUALS(1, errorList.size());
179+
}
180+
181+
// Matching hash with no error (skip)
182+
{
183+
std::list<ErrorMessage> errorList;
184+
tinyxml2::XMLDocument doc;
185+
186+
const tinyxml2::XMLError xmlError = doc.Parse(
187+
"<?xml version=\"1.0\"?>"
188+
"<analyzerinfo hash=\"100\">"
189+
"</analyzerinfo>"
190+
);
191+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError);
192+
193+
ASSERT_EQUALS(true, AnalyzerInformation::skipAnalysis(doc, 100, errorList));
194+
ASSERT_EQUALS(0, errorList.size());
195+
}
196+
197+
// Different hash with normal error (don't skip)
198+
{
199+
std::list<ErrorMessage> errorList;
200+
tinyxml2::XMLDocument doc;
201+
202+
const tinyxml2::XMLError xmlError = doc.Parse(
203+
"<?xml version=\"1.0\"?>"
204+
"<analyzerinfo hash=\"100\">"
205+
"<error id=\"nullPointer\" severity=\"error\" msg=\"Null pointer dereference: ptr\" verbose=\"Null pointer dereference: ptr\" cwe=\"476\" file0=\"test.c\">"
206+
"<location file=\"test.c\" line=\"4\" column=\"3\" info=\"Null pointer dereference\"/>"
207+
"<location file=\"test.c\" line=\"3\" column=\"12\" info=\"Assignment &apos;ptr=NULL&apos;, assigned value is 0\"/>"
208+
"<symbol>ptr</symbol>"
209+
"</error>"
210+
"</analyzerinfo>"
211+
);
212+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError);
213+
214+
ASSERT_EQUALS(false, AnalyzerInformation::skipAnalysis(doc, 99, errorList));
215+
ASSERT_EQUALS(0, errorList.size());
216+
}
217+
218+
// Empty document (don't skip)
219+
{
220+
std::list<ErrorMessage> errorList;
221+
tinyxml2::XMLDocument doc;
222+
223+
const tinyxml2::XMLError xmlError = doc.Parse("");
224+
ASSERT_EQUALS(tinyxml2::XML_ERROR_EMPTY_DOCUMENT, xmlError);
225+
226+
ASSERT_EQUALS(false, AnalyzerInformation::skipAnalysis(doc, 100, errorList));
227+
ASSERT_EQUALS(0, errorList.size());
228+
}
229+
}
98230
};
99231

100232
REGISTER_TEST(TestAnalyzerInformation)

0 commit comments

Comments
 (0)