Skip to content

Commit 3bdd3c8

Browse files
committed
json-parser: warn about unknown nodes in our JSON format
Otherwise they would be silently ignored with even `--mode=json`, which is supposed to preserve as much information as possible.
1 parent 8bbc1e4 commit 3bdd3c8

File tree

5 files changed

+144
-5
lines changed

5 files changed

+144
-5
lines changed

json-parser.cc

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include "csparser.hh" // for KeyEventDigger
2323

24+
#include <set>
25+
2426
#include <boost/foreach.hpp>
2527
#include <boost/property_tree/json_parser.hpp>
2628

@@ -44,10 +46,25 @@ class AbstractTreeDecoder {
4446
/// tree decoder of the native JSON format of csdiff
4547
class SimpleTreeDecoder: public AbstractTreeDecoder {
4648
public:
49+
SimpleTreeDecoder(const std::string &fileName, bool silent);
4750
virtual void readNode(Defect *def, const pt::ptree &node);
4851

4952
private:
50-
KeyEventDigger keDigger;
53+
enum ENodeKind {
54+
NK_DEFECT,
55+
NK_EVENT,
56+
NK_LAST
57+
};
58+
59+
void reportUnknownNodes(ENodeKind, const pt::ptree &) const;
60+
61+
typedef std::set<std::string> TNodeSet;
62+
typedef std::vector<TNodeSet> TNodeStore;
63+
64+
const std::string fileName_;
65+
const bool silent_;
66+
TNodeStore nodeStore_;
67+
KeyEventDigger keDigger_;
5168
};
5269

5370
/// tree decoder of the Coverity JSON format
@@ -145,7 +162,7 @@ JsonParser::JsonParser(
145162

146163
if (findChildOf(&d->defList, d->root, "defects"))
147164
// csdiff-native JSON format
148-
d->decoder = new SimpleTreeDecoder;
165+
d->decoder = new SimpleTreeDecoder(fileName, silent);
149166
else if (findChildOf(&d->defList, d->root, "issues"))
150167
// Coverity JSON format
151168
d->decoder = new CovTreeDecoder;
@@ -214,13 +231,66 @@ bool JsonParser::getNext(Defect *def) {
214231
}
215232
}
216233

234+
SimpleTreeDecoder::SimpleTreeDecoder(const std::string &fileName, bool silent):
235+
fileName_(fileName),
236+
silent_(silent)
237+
{
238+
if (silent_)
239+
// skip initialization of nodeStore_ because no lookup will ever happen
240+
return;
241+
242+
nodeStore_.resize(NK_LAST);
243+
244+
// known per-defect subnodes
245+
nodeStore_[NK_DEFECT] = {
246+
"annotation",
247+
"checker",
248+
"cwe",
249+
"defect_id",
250+
"events",
251+
"function",
252+
"imp",
253+
"key_event_idx",
254+
"language",
255+
};
256+
257+
// known per-event subnodes
258+
nodeStore_[NK_EVENT] = {
259+
"column",
260+
"event",
261+
"file_name",
262+
"line",
263+
"message",
264+
"verbosity_level",
265+
};
266+
}
267+
268+
void SimpleTreeDecoder::reportUnknownNodes(ENodeKind nk, const pt::ptree &node)
269+
const
270+
{
271+
if (silent_)
272+
return;
273+
274+
const TNodeSet &nodeSet = nodeStore_[nk];
275+
276+
BOOST_FOREACH(const pt::ptree::value_type &item, node) {
277+
const std::string &name = item.first;
278+
if (nodeSet.end() == nodeSet.find(name))
279+
std::cerr << fileName_
280+
<< ": warning: unknown JSON node: " << name
281+
<< std::endl;
282+
}
283+
}
284+
217285
void SimpleTreeDecoder::readNode(
218286
Defect *def,
219287
const pt::ptree &defNode)
220288
{
221289
// make sure the Defect structure is properly initialized
222290
(*def) = Defect();
223291

292+
this->reportUnknownNodes(NK_DEFECT, defNode);
293+
224294
// the checker field is mandatory
225295
def->checker = defNode.get<std::string>("checker");
226296

@@ -231,6 +301,7 @@ void SimpleTreeDecoder::readNode(
231301
const pt::ptree &evtListSrc = defNode.get_child("events");
232302
BOOST_FOREACH(const pt::ptree::value_type &evtItem, evtListSrc) {
233303
const pt::ptree &evtNode = evtItem.second;
304+
this->reportUnknownNodes(NK_EVENT, evtNode);
234305

235306
DefEvent evt;
236307
evt.fileName = valueOf<std::string >(evtNode, "file_name" , "");
@@ -254,7 +325,7 @@ void SimpleTreeDecoder::readNode(
254325

255326
if (defNode.not_found() == defNode.find("key_event_idx")) {
256327
// key event not specified, try to guess it
257-
if (!this->keDigger.guessKeyEvent(def))
328+
if (!keDigger_.guessKeyEvent(def))
258329
throw pt::ptree_error("failed to guess key event");
259330
}
260331
else {
@@ -269,7 +340,7 @@ void SimpleTreeDecoder::readNode(
269340

270341
if (verbosityLevelNeedsInit)
271342
// missing or incomplete verbosity_level, initialize it over
272-
this->keDigger.initVerbosity(def);
343+
keDigger_.initVerbosity(def);
273344

274345
// read annotation if available
275346
def->annotation = valueOf<std::string>(defNode, "annotation", "");

tests/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ set(jsfilter "sed")
2828
set(jsfilter "${jsfilter} -e ':a;N;$!ba;s|:\\\\\\\\n *\\\\\\\\[|: [|g'")
2929

3030
macro(add_test_wrap test_name cmd)
31-
add_test("${test_name}" sh -c "${cmd}")
31+
add_test("${test_name}" bash -c "${cmd}")
3232
set_tests_properties(${test_name} PROPERTIES COST ${test_cost})
3333
math(EXPR test_cost "${test_cost} - 1")
3434
endmacro()
@@ -180,6 +180,7 @@ test_csgrep(csgrep "60-gcc-parser-cppcheck-cwe" )
180180
test_csgrep(csgrep "61-json-parser-cov-v7-lang" )
181181
test_csgrep(csgrep "62-csparser-checker-lang" )
182182
test_csgrep(csgrep "63-gcc-parser-checker-lang" )
183+
test_csgrep(csgrep "64-json-parser-unknown-node" )
183184
test_csparser(csparser-5.8 00)
184185
test_csparser(csparser-5.8 01)
185186
test_csparser(csparser-5.8 02)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--mode=json 2> >(sed "s|^.*/||") >/dev/null
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"defects": [
3+
{
4+
"checker": "MISSING_MOVE_ASSIGNMENT",
5+
"function": "null",
6+
"lang": "c/c++",
7+
"key_event_idx": 0,
8+
"events": [
9+
{
10+
"file_name": "/builddir/build/BUILD/systemtap-4.4/bpf-bitset.h",
11+
"line": 209,
12+
"event": "missing_move_assignment",
13+
"message": "Class \"bpf::bitset::set1\" may benefit from adding a move assignment operator. See other events which show the copy assignment operator being applied to rvalue(s), where a move assignment may be faster.",
14+
"verbosity_level": "0"
15+
},
16+
{
17+
"file_name": "/builddir/build/BUILD/systemtap-4.4/bpf-opt.cxx",
18+
"line": 633,
19+
"event": "copy_assignment",
20+
"message": "Example 1: Copy assignment is performed on an object (rvalue) of type \"bpf::bitset::set1\".",
21+
"verbosity_level": "1"
22+
},
23+
{
24+
"file_name": "/builddir/build/BUILD/systemtap-4.4/bpf-opt.cxx",
25+
"line": 528,
26+
"event": "copy_assignment",
27+
"message": "Example 2: Copy assignment is performed on an object (rvalue) of type \"bpf::bitset::set1\".",
28+
"verbosity_level": "1"
29+
},
30+
{
31+
"file_name": "/builddir/build/BUILD/systemtap-4.4/bpf-opt.cxx",
32+
"line": 533,
33+
"event": "copy_assignment",
34+
"message": "Example 3: Copy assignment is performed on an object (rvalue) of type \"bpf::bitset::set1\".",
35+
"verbosity_level": "1",
36+
"comment": "useless event"
37+
}
38+
]
39+
},
40+
{
41+
"checker": "UNINIT_CTOR",
42+
"cwe": 457,
43+
"function": "bpf::life_data::life_data(unsigned long, unsigned long)",
44+
"language": "c/c++",
45+
"key_event_idx": 1,
46+
"events": [
47+
{
48+
"file_name": "/builddir/build/BUILD/systemtap-4.4/bpf-opt.cxx",
49+
"line": 475,
50+
"event": "member_decl",
51+
"message": "Class member declaration for \"npartitions\".",
52+
"verbosity_level": "1"
53+
},
54+
{
55+
"file_name": "/builddir/build/BUILD/systemtap-4.4/bpf-opt.cxx",
56+
"line": 487,
57+
"event": "uninit_member",
58+
"message": "Non-static class member \"npartitions\" is not initialized in this constructor nor in any functions that it calls.",
59+
"verbosity_level": "0"
60+
}
61+
]
62+
}
63+
]
64+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
64-json-parser-unknown-node-stdin.txt: warning: unknown JSON node: lang
2+
64-json-parser-unknown-node-stdin.txt: warning: unknown JSON node: comment

0 commit comments

Comments
 (0)