Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SARIF output support. #4651

Closed
wants to merge 46 commits into from

Conversation

mario-campos
Copy link

@mario-campos mario-campos commented Dec 16, 2022

This has been on my TODO list for a while! I've been wanting to integrate CppCheck's results into GitHub since Code Scanning came out. To do that, however, the tool needs to spit out SARIF.

To test it out, run cppcheck --output-format=sarif --quiet foo.c

Now, this is only an initial stab, and it's probably a bit ugly. I'm not quite done just yet (hence why it's a draft PR). For one, it's not truly outputting the correct line/column numbers. So, if you have any suggestions or comments, please let me know.

Also, I initially tried to work SARIF output into the original design by adding a ErrorMessage::toSARIF method (ala ErrorMessage::toXML), but I quickly realized that this wouldn't work very well, since the SARIF document has to be "constructed", not "streamed" the way the XML document is. Which meant that I had to rework the way that findings ("errors") were collected and aggregated, since the SARIF document kinda needs to see everything all at once.

cli/cppcheckexecutor.cpp Fixed Show resolved Hide resolved
@mario-campos mario-campos force-pushed the mario-campos/sarif branch 2 times, most recently from 2e352ed to 3395a1b Compare December 17, 2022 18:39
@firewave
Copy link
Collaborator

Thanks for your contribution.

Without actually looking at what it does - please add some unit and/or system tests for it.

@danmar
Copy link
Owner

danmar commented Dec 18, 2022

I haven't looked into your implementation but I really like this. 👍

@mario-campos mario-campos marked this pull request as ready for review December 19, 2022 22:10
@mario-campos
Copy link
Author

@firewave, any idea how to include certain .cpp files into the Visual Studio build? I don't have any experience with Windows development.

cppcheckexecutor.obj : error LNK2019: unresolved external symbol "public: __cdecl XMLAnalysisReport::XMLAnalysisReport(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??0XMLAnalysisReport@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "protected: bool __cdecl CppCheckExecutor::parseFromArgs(class CppCheck *,int,char const * const * const)" (?parseFromArgs@CppCheckExecutor@@IEAA_NPEAVCppCheck@@HQEBQEBD@Z) [D:\a\cppcheck\cppcheck\test\testrunner.vcxproj]

cli/clianalysisreport.cpp Outdated Show resolved Hide resolved
cli/sarifanalysisreport.cpp Outdated Show resolved Hide resolved
lib/settings.h Outdated Show resolved Hide resolved
Currently, for XML output, the XML generation is tightly-coupled with the `ErrorMessage` class, which makes it harder to implement other formats (e.g. SARIF) that may need to see all of the errors/findings at once.

Furthermore, implementing the serialization in the `ErrorMessage` class as individual methods (e.g. `toXML`) does not scale very well when using multiple formats. This approach uses the `AnalysisReport` abstract class to sub-class the different serialization formats.
This treats the CLI output as just another format (default) for the findings/report.
This should look and feel like the rest of the CppCheck code.
This is a good start, but it still needs some work. In particular, I need to fix the startLine/endLine/startColumn/endColumn part. Right now, I am using placeholder values ("1") instead of the real line/column numbers.
This matches the other AnalysisReport classes, and it's what clang-tidy recommends.
SARIF's precision property maps to Cppcheck's certainty, which is essentially the confidence level.
@mario-campos mario-campos requested review from firewave and danmar and removed request for danmar and firewave December 24, 2022 18:06
@firewave
Copy link
Collaborator

Sorry for the late reply but I currently out sick. So still no code review...

@firewave, any idea how to include certain .cpp files into the Visual Studio build? I don't have any experience with Windows development.

cppcheckexecutor.obj : error LNK2019: unresolved external symbol "public: __cdecl XMLAnalysisReport::XMLAnalysisReport(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??0XMLAnalysisReport@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "protected: bool __cdecl CppCheckExecutor::parseFromArgs(class CppCheck *,int,char const * const * const)" (?parseFromArgs@CppCheckExecutor@@IEAA_NPEAVCppCheck@@HQEBQEBD@Z) [D:\a\cppcheck\cppcheck\test\testrunner.vcxproj]

If #4652 is merged (could happen any day), dmake will take care of that. Until then you need to do it manually in the *.vcxproj files.

@firewave
Copy link
Collaborator

Since GitHub actions apparently support SARIF output could also also adjust the selfcheck workflow to use SARIF output instead of relying on the exitcode?

Also if SARIF is used should the exitcode be forced to 0? I have no idea how other tools implement this.

Copy link
Collaborator

@firewave firewave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also run dmake to actually update the Makefile.

#ifndef ANALYSIS_REPORT_H
#define ANALYSIS_REPORT_H

#include "errorlogger.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the include. Just forward declare ErrorMessage.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? I tried forward-declaring ErrorMessage as such:

class ErrorMessage;

But it then failed to build.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes because you pass ErrorMessage by value to addFinding. It should be passed by const reference.. then I believe a forward declaration will be enough.

cli/cmdlineparser.cpp Outdated Show resolved Hide resolved
@@ -19,6 +19,7 @@
#ifndef CPPCHECKEXECUTOR_H
#define CPPCHECKEXECUTOR_H

#include "analysisreport.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always forgot if std::unique_ptr can work with incomplete types...could be forward decl then.

cli/sarifanalysisreport.cpp Outdated Show resolved Hide resolved
cli/sarifanalysisreport.cpp Outdated Show resolved Hide resolved
cli/sarifanalysisreport.cpp Outdated Show resolved Hide resolved
cli/sarifanalysisreport.cpp Outdated Show resolved Hide resolved

// https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317507
run["results"] = picojson::value(results);
runs.emplace_back(run);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::move()? same for the other emplace_back() calls above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

static std::map<std::string, picojson::value> text(const std::string& s) {
std::map<std::string, picojson::value> m = {{ "text", picojson::value(s) }};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could enable PICOJSON_USE_RVALUE_REFERENCE (or is it enabled via compile-time detection?) and pass byval and move into picojson::value().

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That define is based on preprocessor checks. It should be available in all compiler although I am not 100% sure if the Visual Studio check already covers VS2012.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think I've got it, but you should probably double-check my work.

cli/cmdlineparser.cpp Outdated Show resolved Hide resolved
cli/xmlanalysisreport.cpp Show resolved Hide resolved
cli/cppcheckexecutor.cpp Outdated Show resolved Hide resolved
cli/cppcheckexecutor.cpp Show resolved Hide resolved
cli/cppcheckexecutor.cpp Outdated Show resolved Hide resolved
cli/sarifanalysisreport.cpp Outdated Show resolved Hide resolved
@mario-campos
Copy link
Author

mario-campos commented Jan 10, 2023

@firewave, unfortunately dmake does not create the Makefile target with the picojson include -isystem externals/picojson:

cli/sarifanalysisreport.o: cli/sarifanalysisreport.cpp cli/analysisreport.h cli/sarifanalysisreport.h externals/picojson/picojson.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h
	$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/sarifanalysisreport.cpp

See https://github.com/mario-campos/cppcheck/actions/runs/3887481113/jobs/6633822871. Which means that, without tampering with the Makefile, dmake will generate a Makefile that fails to build cppcheck. Any suggestions?

#ifndef ANALYSIS_REPORT_H
#define ANALYSIS_REPORT_H

#include "errorlogger.h"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes because you pass ErrorMessage by value to addFinding. It should be passed by const reference.. then I believe a forward declaration will be enough.

std::string serialize() override;

private:
bool mVerbose;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the feeling these members can be const.

@@ -941,8 +941,10 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
}

// Write results in results.xml
else if (std::strcmp(argv[i], "--xml") == 0)
else if (std::strcmp(argv[i], "--xml") == 0) {
printMessage("option --xml is deprecated; use --output-format=xml instead.");
mSettings->xml = true;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we remove variable xml in the Settings. And create enum outputFormat instead.

/**
* Submit a CppCheck result for inclusion into the report.
*/
virtual void addFinding(ErrorMessage msg) = 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change parameter type to const ErrorMessage& msg

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it used to be const ErrorMessage&, but @firewave suggested passing by value so it could be std::moved.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm.. ok but then I think you can't use the forward declaration.. maybe firewave did not see that. Could you double check if the forward declaration is possible if a const reference is used instead.

* by others classes that will contain the results of a CppCheck analysis, and
* output those results in a particular format.
*/
class AnalysisReport {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the constructor for AnalysisReport could take a output stream argument:

std::ostream *outputStream

Then you pass the ofstream/std::cout/std::cerr as argument to the report.

@danmar
Copy link
Owner

danmar commented Jan 18, 2023

I think this looks really interesting. There are some refactorings I would like to see.. but maybe instead of writing lots of nits it's quicker to just take over the finishing touches..

@danmar
Copy link
Owner

danmar commented Jan 18, 2023

unfortunately dmake does not create the Makefile target with the picojson include -isystem externals/picojson:

I have the feeling that you can adjust dmake.cpp here:
https://github.com/danmar/cppcheck/blob/main/tools/dmake.cpp#L73

I guess it should say something like:

    else if (filename.compare(0, 4, "cli/") == 0) {
        getDeps("lib" + filename.substr(filename.find('/')), depfiles);
        for (const std::string & external : externalfolders)
            getDeps(external + filename.substr(filename.find('/')), depfiles);
    }

@danmar
Copy link
Owner

danmar commented Jan 21, 2023

unfortunately dmake does not create the Makefile target with the picojson include -isystem externals/picojson:

I have made some fixes in this branch:

https://github.com/danmar/cppcheck/tree/4651

The fixes I made are here: https://github.com/danmar/cppcheck/commit/acca98d8f33e8f1f4bb5e06c4fb88277970830a2.diff
Feel free to apply those fixes locally. In particular I think the changes in dmake.cpp will help you.

There are still some tests that fail. You can run those tests with these commands:

 cd cppcheck/test/cli
 python3 -m pytest test-helloworld.py

If the tests are fixed I have the feeling that we can merge this PR.

@mario-campos
Copy link
Author

I think this looks really interesting. There are some refactorings I would like to see.. but maybe instead of writing lots of nits it's quicker to just take over the finishing touches..

That would be helpful. Unfortunately, now that the holiday season is over, I don't have as much time for this PR as I did before. But, I will still continue to address the remaining problems as much as I can.

@firewave
Copy link
Collaborator

FYI there's already an PR which adds that output format command-line switch: #3365.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants