├── .idea
├── .gitignore
├── vcs.xml
├── modules.xml
├── misc.xml
├── libraries
│ └── commons_lang3.xml
└── inspectionProfiles
│ └── Project_Default.xml
├── .gitignore
├── .gitattributes
├── lib
├── commons-lang3-3.12.0.jar
├── doc
│ └── commons-lang3-3.12.0-javadoc.jar
└── src
│ └── commons-lang3-3.12.0-sources.jar
├── src
├── inspectionDescriptions
│ └── Cppcheck.html
└── com
│ └── github
│ └── johnthagen
│ └── cppcheck
│ ├── CppcheckError.java
│ ├── Properties.java
│ ├── CppcheckNotification.java
│ ├── JFilePicker.java
│ ├── ShowOutputAction.java
│ ├── SupportedExtensions.java
│ ├── CppcheckInspection.java
│ ├── Configuration.java
│ └── CppCheckInspectionImpl.java
├── .github
└── ISSUE_TEMPLATE.md
├── clion-cppcheck.iml
├── LICENSE.txt
├── resources
└── META-INF
│ └── plugin.xml
└── README.md
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | /workspace.xml
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/shelf/
2 | /clion-cppcheck.jar
3 | /out
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
--------------------------------------------------------------------------------
/lib/commons-lang3-3.12.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnthagen/clion-cppcheck/HEAD/lib/commons-lang3-3.12.0.jar
--------------------------------------------------------------------------------
/lib/doc/commons-lang3-3.12.0-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnthagen/clion-cppcheck/HEAD/lib/doc/commons-lang3-3.12.0-javadoc.jar
--------------------------------------------------------------------------------
/lib/src/commons-lang3-3.12.0-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnthagen/clion-cppcheck/HEAD/lib/src/commons-lang3-3.12.0-sources.jar
--------------------------------------------------------------------------------
/src/inspectionDescriptions/Cppcheck.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Runs Cppcheck against the current file.
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/CppcheckError.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | class CppcheckError extends Error {
6 | public CppcheckError(@NotNull final String message)
7 | {
8 | super(message);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Environment
2 |
3 | * Operating System (e.g. Ubuntu 16.04 x64):
4 | * IDE Version (e.g. CLion 2016.3.2):
5 | * Cppcheck executable version (`cppcheck --version`):
6 | * Cppcheck plugin version:
7 | * Exact strings used in cppcheck plugin options:
8 | * **cppcheck path**:
9 | * **cppcheck options**:
10 |
11 | ### Expected behaviour
12 |
13 | ### Actual behaviour
14 |
15 | ### Steps to reproduce the behaviour
16 |
--------------------------------------------------------------------------------
/.idea/libraries/commons_lang3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/Properties.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.ide.util.PropertiesComponent;
4 |
5 | class Properties {
6 | private static final PropertiesComponent INSTANCE = PropertiesComponent.getInstance();
7 |
8 | static void set(final String key, final String value) {
9 | INSTANCE.setValue(key, value);
10 | }
11 |
12 | static String get(final String key) {
13 | return INSTANCE.getValue(key);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/CppcheckNotification.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.notification.Notification;
4 | import com.intellij.notification.NotificationType;
5 | import com.intellij.notification.Notifications;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | class CppcheckNotification {
9 | public static void send(@NotNull final String title, @NotNull final String content, final NotificationType type) {
10 | Notifications.Bus.notify(new Notification("Cppcheck",
11 | "Cppcheck " + title,
12 | content,
13 | type));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/clion-cppcheck.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2021 John Hagen
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to
5 | deal in the Software without restriction, including without limitation the
6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 | sell copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 | IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/JFilePicker.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import javax.swing.JPanel;
4 | import javax.swing.JTextField;
5 | import javax.swing.JFileChooser;
6 | import javax.swing.JLabel;
7 | import javax.swing.JButton;
8 | import java.awt.FlowLayout;
9 |
10 | class JFilePicker extends JPanel {
11 | private final JTextField textField;
12 | private final JFileChooser fileChooser;
13 |
14 | JFilePicker(final String textFieldLabel, final String buttonLabel) {
15 | fileChooser = new JFileChooser();
16 |
17 | setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
18 |
19 | final JLabel label = new JLabel(textFieldLabel);
20 |
21 | textField = new JTextField(30);
22 | final JButton button = new JButton(buttonLabel);
23 |
24 | button.addActionListener(evt -> buttonActionPerformed());
25 |
26 | add(label);
27 | add(textField);
28 | add(button);
29 | }
30 |
31 | JTextField getTextField() {
32 | return textField;
33 | }
34 |
35 | private void buttonActionPerformed() {
36 | if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
37 | textField.setText(fileChooser.getSelectedFile().getAbsolutePath());
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/ShowOutputAction.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.openapi.actionSystem.AnAction;
4 | import com.intellij.openapi.actionSystem.AnActionEvent;
5 | import com.intellij.openapi.fileEditor.FileEditorManager;
6 | import com.intellij.openapi.fileEditor.OpenFileDescriptor;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.vfs.LocalFileSystem;
9 | import com.intellij.openapi.vfs.VirtualFile;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import static com.github.johnthagen.cppcheck.CppcheckInspection.LATEST_RESULT_FILE;
13 |
14 | public class ShowOutputAction extends AnAction {
15 | @Override
16 | public void actionPerformed(@NotNull final AnActionEvent anActionEvent) {
17 | final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(LATEST_RESULT_FILE.toFile());
18 | if (vFile == null) {
19 | return;
20 | }
21 |
22 | final Project project = anActionEvent.getProject();
23 | if (project == null) {
24 | return;
25 | }
26 |
27 | // TODO: make it possible to view the output for the currently open file
28 | // TODO: find a way to display this without writing to the file system
29 | FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, vFile), true);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/SupportedExtensions.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.openapi.vfs.VirtualFile;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | class SupportedExtensions {
12 | // TODO: get the list of supported extensions from Cppcheck if it provides that information
13 | // TODO: extend list by extensions configured within CLion
14 | private final static List supportedCExtensions = new ArrayList<>(Arrays.asList(
15 | "c",
16 | "cl"));
17 |
18 | private final static List supportedCPPExtensions = new ArrayList<>(Arrays.asList(
19 | "cc",
20 | "cp",
21 | "cpp",
22 | "c++",
23 | "cxx",
24 | "hh",
25 | "hpp",
26 | "hxx",
27 | "tpp",
28 | "txx"));
29 |
30 | private final static List supportedHeaderExtensions = new ArrayList<>(Collections.singletonList(
31 | "h"));
32 |
33 | public static boolean isCFamilyFile(@NotNull final VirtualFile file) {
34 | return isCFile(file) || isCPPFile(file) || isHeaderFile(file);
35 | }
36 |
37 | private static boolean isFile(@NotNull final VirtualFile file, @NotNull final List supportedExtensions)
38 | {
39 | final String fileExtension = file.getExtension();
40 | if (fileExtension == null) {
41 | return false;
42 | }
43 |
44 | final String lowerFileExtension = fileExtension.toLowerCase();
45 | return supportedExtensions.contains(lowerFileExtension);
46 | }
47 |
48 | public static boolean isCFile(@NotNull final VirtualFile file) {
49 | return isFile(file, supportedCExtensions);
50 | }
51 |
52 | public static boolean isCPPFile(@NotNull final VirtualFile file) {
53 | return isFile(file, supportedCPPExtensions);
54 | }
55 |
56 | public static boolean isHeaderFile(@NotNull final VirtualFile file) {
57 | return isFile(file, supportedHeaderExtensions);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.github.johnthagen.cppcheck
3 | cppcheck
4 | 1.6.7
5 | johnthagen
6 |
7 |
9 |
10 | Features:
11 |
12 | - Runs Cppcheck on the fly while you write code
13 | - Highlights lines and displays Cppcheck error messages
14 | - Supports passing options to Cppcheck
15 |
16 |
17 |
18 | Usage:
19 | Please refer to Installation.
20 |
21 | Known issues:
22 | Please refer to Known Issues.
23 | ]]>
24 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
35 | com.intellij.modules.lang
36 |
37 |
38 |
44 |
46 |
47 |
48 |
49 |
50 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/CppcheckInspection.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.codeInspection.InspectionManager;
4 | import com.intellij.codeInspection.LocalInspectionTool;
5 | import com.intellij.codeInspection.ProblemDescriptor;
6 | import com.intellij.codeInspection.ProblemHighlightType;
7 | import com.intellij.execution.ExecutionException;
8 | import com.intellij.notification.NotificationType;
9 | import com.intellij.openapi.editor.Document;
10 | import com.intellij.openapi.fileEditor.FileDocumentManager;
11 | import com.intellij.openapi.util.TextRange;
12 | import com.intellij.openapi.util.io.FileUtil;
13 | import com.intellij.openapi.vfs.VirtualFile;
14 | import com.intellij.psi.PsiFile;
15 | import org.apache.commons.lang3.RandomStringUtils;
16 | import org.jetbrains.annotations.NotNull;
17 | import org.jetbrains.annotations.Nullable;
18 | import org.xml.sax.SAXException;
19 |
20 | import javax.xml.parsers.ParserConfigurationException;
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.nio.file.Path;
24 | import java.nio.file.Paths;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | class CppcheckInspection extends LocalInspectionTool {
29 | final static Path LATEST_RESULT_FILE = Paths.get(FileUtil.getTempDirectory(), "clion-cppcheck-latest.xml");
30 |
31 | private static ProblemDescriptor createProblemDescriptor(@NotNull final PsiFile file,
32 | @NotNull final InspectionManager manager,
33 | @NotNull final String msg) {
34 | return manager.createProblemDescriptor(
35 | file,
36 | (TextRange)null,
37 | msg,
38 | ProblemHighlightType.GENERIC_ERROR,
39 | true);
40 | }
41 |
42 | @Nullable
43 | @Override
44 | public ProblemDescriptor[] checkFile(@NotNull final PsiFile file,
45 | @NotNull final InspectionManager manager,
46 | final boolean isOnTheFly) {
47 | final VirtualFile vFile = file.getVirtualFile();
48 | if (vFile == null || !vFile.isInLocalFileSystem() || !SupportedExtensions.isCFamilyFile(vFile)) {
49 | return ProblemDescriptor.EMPTY_ARRAY;
50 | }
51 |
52 | final Document document = FileDocumentManager.getInstance().getDocument(vFile);
53 | if (document == null || document.getLineCount() == 0) {
54 | return ProblemDescriptor.EMPTY_ARRAY;
55 | }
56 |
57 | final String cppcheckPath = Properties.get(Configuration.CONFIGURATION_KEY_CPPCHECK_PATH);
58 | if (cppcheckPath == null || cppcheckPath.isEmpty()) {
59 | final ProblemDescriptor problemDescriptor = createProblemDescriptor(file, manager, "Please set 'Cppcheck Path' in 'Cppcheck Configuration'.");
60 | return new ProblemDescriptor[]{problemDescriptor};
61 | }
62 |
63 | final File cppcheckPathFile = new File(cppcheckPath);
64 | if (!cppcheckPathFile.exists()) {
65 | final ProblemDescriptor problemDescriptor = createProblemDescriptor(file, manager, "Configured 'Cppcheck Path' in 'Cppcheck Configuration' does not exist: " + cppcheckPathFile.getAbsolutePath());
66 | return new ProblemDescriptor[]{problemDescriptor};
67 | }
68 |
69 | final ArrayList descriptors = new ArrayList<>();
70 |
71 | String cppcheckOptions = Properties.get(Configuration.CONFIGURATION_KEY_CPPCHECK_OPTIONS);
72 |
73 | final String cppcheckMisraPath = Properties.get(Configuration.CONFIGURATION_KEY_CPPCHECK_MISRA_PATH);
74 | if (cppcheckMisraPath != null && !cppcheckMisraPath.isEmpty()) {
75 | final File cppcheckMisraPathFile = new File(cppcheckMisraPath);
76 | if (!cppcheckMisraPathFile.exists()) {
77 | final ProblemDescriptor problemDescriptor = createProblemDescriptor(file, manager, "Configured 'MISRA Addon JSON' in 'Cppcheck Configuration' does not exist: " + cppcheckMisraPathFile.getAbsolutePath());
78 | descriptors.add(problemDescriptor);
79 | }
80 | else {
81 | cppcheckOptions = String.format("%s --addon=%s", cppcheckOptions, cppcheckMisraPath);
82 | }
83 | }
84 | cppcheckOptions = String.format("%s --xml", cppcheckOptions);
85 |
86 | int verboseLevel = 0;
87 | final String cppcheckVerboseLevel = Properties.get(Configuration.CONFIGURATION_KEY_CPPCHECK_VERBOSE_LEVEL);
88 | if (cppcheckVerboseLevel != null && !cppcheckVerboseLevel.isEmpty()) {
89 | verboseLevel = Integer.parseInt(cppcheckVerboseLevel);
90 | }
91 |
92 | File tempFile = null;
93 | try {
94 | final CppCheckInspectionImpl inspectionImpl = new CppCheckInspectionImpl(verboseLevel);
95 | tempFile = FileUtil.createTempFile(RandomStringUtils.randomAlphanumeric(8) + "_", vFile.getName(), true);
96 | FileUtil.writeToFile(tempFile, document.getText());
97 | final String cppcheckOutput =
98 | inspectionImpl.executeCommandOnFile(vFile, cppcheckPathFile, prependIncludeDir(cppcheckOptions, vFile),
99 | tempFile, cppcheckMisraPath);
100 |
101 | // store the output of the latest analysis
102 | FileUtil.writeToFile(LATEST_RESULT_FILE.toFile(), cppcheckOutput);
103 |
104 | final List descriptorsList = inspectionImpl.parseOutput(file, manager, document, cppcheckOutput,
105 | tempFile.getName());
106 | descriptors.addAll(descriptorsList);
107 | } catch (final ExecutionException | CppcheckError | IOException | SAXException | ParserConfigurationException ex) {
108 | CppcheckNotification.send("execution failed for " + vFile.getCanonicalPath(),
109 | ex.getClass().getSimpleName() + ": " + ex.getMessage(),
110 | NotificationType.ERROR);
111 | final ProblemDescriptor problemDescriptor = createProblemDescriptor(file, manager, "Cppcheck execution failed: " + ex.getClass().getSimpleName() + ": " + ex.getMessage().split("\n", 2)[0]);
112 | descriptors.add(problemDescriptor);
113 | } finally {
114 | if (tempFile != null) {
115 | FileUtil.delete(tempFile);
116 | }
117 | }
118 |
119 | return descriptors.toArray(new ProblemDescriptor[0]);
120 | }
121 |
122 | @NotNull
123 | private static String prependIncludeDir(@NotNull final String cppcheckOptions, @NotNull final VirtualFile vFile) {
124 | final VirtualFile dir = vFile.getParent();
125 | if (dir == null) {
126 | return cppcheckOptions;
127 | }
128 | final String path = dir.getCanonicalPath();
129 | if (path == null) {
130 | return cppcheckOptions;
131 | }
132 | return String.format("-I\"%s\" %s", path, cppcheckOptions);
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/Configuration.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.openapi.options.Configurable;
4 | import com.intellij.ui.components.panels.VerticalLayout;
5 | import org.jetbrains.annotations.Nls;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | import javax.swing.*;
9 | import javax.swing.event.ChangeEvent;
10 | import javax.swing.event.ChangeListener;
11 | import javax.swing.event.DocumentEvent;
12 | import javax.swing.event.DocumentListener;
13 |
14 | class Configuration implements Configurable {
15 | private boolean modified = false;
16 | private JFilePicker cppcheckFilePicker;
17 | private JTextField cppcheckOptionsField;
18 | private JFilePicker cppcheckMisraFilePicker;
19 | private final String[] VERBOSE_LEVEL_SPINNER_MODEL = {"0", "1", "2", "3", "4"};
20 | private JSpinner cppcheckVerboseLevel;
21 | private static final String CPPCHECK_NOTE =
22 | "Note: C++ projects should leave --language=c++ appended to the Cppcheck options to avoid some " +
23 | "false positives in header files due to the fact that Cppcheck implicitly defaults to " +
24 | "setting --language to \"c\" for .h files.";
25 | private static final String CPPCHECK_MISRA_NOTE =
26 | "Using MISRA requires a rule texts file, which can be obtained from MISRA themselves " +
27 | "(Their license prohibits distributing the rules texts)\n\n" +
28 | "Create a .json file near your Cppcheck installation and point to it here\n" +
29 | "Within that file, create something like this:\n" +
30 | "{\n" +
31 | " \"script\": \"misra.py\",\n" +
32 | " \"args\": [\"--rule-texts=\"]\n" +
33 | "}";
34 |
35 | private final CppcheckConfigurationModifiedListener
36 | listener = new CppcheckConfigurationModifiedListener(this);
37 |
38 | static final String CONFIGURATION_KEY_CPPCHECK_PATH = "cppcheck";
39 | static final String CONFIGURATION_KEY_CPPCHECK_OPTIONS = "cppcheckOptions";
40 | static final String CONFIGURATION_KEY_CPPCHECK_VERBOSE_LEVEL = "cppcheckVerboseLevel";
41 | static final String CONFIGURATION_KEY_CPPCHECK_MISRA_PATH = "cppcheckMisraPath";
42 |
43 | private static final String defaultOptions = "--enable=warning,performance,portability,style --language=c++";
44 |
45 | @Nls
46 | @Override
47 | public String getDisplayName() {
48 | return "cppcheck";
49 | }
50 |
51 | @Nullable
52 | @Override
53 | public String getHelpTopic() {
54 | return null;
55 | }
56 |
57 | @Nullable
58 | @Override
59 | public JComponent createComponent() {
60 | cppcheckFilePicker = new JFilePicker("Cppcheck Path:", "...");
61 | final JLabel optionFieldLabel = new JLabel("Cppcheck Options (Default: " + defaultOptions + "):");
62 | cppcheckOptionsField = new JTextField(defaultOptions, 38);
63 | cppcheckMisraFilePicker = new JFilePicker("MISRA Addon JSON:", "...");
64 | final JLabel verboseLevelLabel = new JLabel("Plugin Verbose Level:");
65 | cppcheckVerboseLevel = new JSpinner(new SpinnerListModel(VERBOSE_LEVEL_SPINNER_MODEL));
66 |
67 | // The first time a user installs the plugin, save the default options in their properties.
68 | if (Properties.get(CONFIGURATION_KEY_CPPCHECK_OPTIONS) == null ||
69 | Properties.get(CONFIGURATION_KEY_CPPCHECK_OPTIONS).isEmpty()) {
70 | Properties.set(CONFIGURATION_KEY_CPPCHECK_OPTIONS, cppcheckOptionsField.getText());
71 | }
72 |
73 | if (Properties.get(CONFIGURATION_KEY_CPPCHECK_VERBOSE_LEVEL) == null) {
74 | cppcheckVerboseLevel.setValue("0");
75 | Properties.set(CONFIGURATION_KEY_CPPCHECK_VERBOSE_LEVEL, cppcheckVerboseLevel.getValue().toString());
76 | }
77 |
78 | if (Properties.get(CONFIGURATION_KEY_CPPCHECK_MISRA_PATH) == null) {
79 | cppcheckMisraFilePicker.getTextField().setText("");
80 | Properties.set(CONFIGURATION_KEY_CPPCHECK_MISRA_PATH, cppcheckMisraFilePicker.getTextField().getText());
81 | }
82 |
83 | if (Properties.get(CONFIGURATION_KEY_CPPCHECK_MISRA_PATH) == null) {
84 | cppcheckMisraFilePicker.getTextField().setText("");
85 | Properties.set(CONFIGURATION_KEY_CPPCHECK_MISRA_PATH, cppcheckMisraFilePicker.getTextField().getText());
86 | }
87 |
88 | final JTextArea cppcheckNoteArea = new JTextArea(CPPCHECK_NOTE, 2, 80);
89 | cppcheckNoteArea.setLineWrap(true);
90 | cppcheckNoteArea.setWrapStyleWord(true);
91 |
92 | final JTextArea cppcheckMisraNoteArea = new JTextArea(CPPCHECK_MISRA_NOTE, 2, 80);
93 | cppcheckMisraNoteArea.setLineWrap(true);
94 | cppcheckMisraNoteArea.setWrapStyleWord(true);
95 |
96 | reset();
97 |
98 | cppcheckFilePicker.getTextField().getDocument().addDocumentListener(listener);
99 | cppcheckOptionsField.getDocument().addDocumentListener(listener);
100 | cppcheckMisraFilePicker.getTextField().getDocument().addDocumentListener(listener);
101 | cppcheckVerboseLevel.addChangeListener(listener);
102 |
103 | final JPanel jPanel = new JPanel();
104 |
105 | final VerticalLayout verticalLayout = new VerticalLayout(1, 2);
106 | jPanel.setLayout(verticalLayout);
107 |
108 | jPanel.add(cppcheckFilePicker);
109 | jPanel.add(optionFieldLabel);
110 | jPanel.add(cppcheckOptionsField);
111 | jPanel.add(cppcheckNoteArea);
112 |
113 | jPanel.add(cppcheckMisraFilePicker);
114 | jPanel.add(cppcheckMisraNoteArea);
115 |
116 | final JPanel spinnerPanel = new JPanel();
117 | spinnerPanel.setLayout(new BoxLayout(spinnerPanel, BoxLayout.X_AXIS));
118 | spinnerPanel.add(verboseLevelLabel);
119 | spinnerPanel.add(cppcheckVerboseLevel);
120 | jPanel.add(spinnerPanel);
121 |
122 | return jPanel;
123 | }
124 |
125 | @Override
126 | public boolean isModified() {
127 | return modified;
128 | }
129 |
130 | private void setModified() {
131 | modified = true;
132 | }
133 |
134 | @Override
135 | public void apply() {
136 | Properties.set(CONFIGURATION_KEY_CPPCHECK_PATH, cppcheckFilePicker.getTextField().getText());
137 | Properties.set(CONFIGURATION_KEY_CPPCHECK_OPTIONS, cppcheckOptionsField.getText());
138 | Properties.set(CONFIGURATION_KEY_CPPCHECK_VERBOSE_LEVEL, cppcheckVerboseLevel.getValue().toString());
139 | Properties.set(CONFIGURATION_KEY_CPPCHECK_MISRA_PATH, cppcheckMisraFilePicker.getTextField().getText());
140 | modified = false;
141 | }
142 |
143 | @Override
144 | public void reset() {
145 | final String cppcheckPath = Properties.get(CONFIGURATION_KEY_CPPCHECK_PATH);
146 | cppcheckFilePicker.getTextField().setText(cppcheckPath);
147 |
148 | final String cppcheckOptions = Properties.get(CONFIGURATION_KEY_CPPCHECK_OPTIONS);
149 | cppcheckOptionsField.setText(cppcheckOptions);
150 |
151 | final String cppcheckVerbose = Properties.get(CONFIGURATION_KEY_CPPCHECK_VERBOSE_LEVEL);
152 | cppcheckVerboseLevel.setValue(cppcheckVerbose);
153 |
154 | final String cppcheckMisraPath = Properties.get(CONFIGURATION_KEY_CPPCHECK_MISRA_PATH);
155 | cppcheckMisraFilePicker.getTextField().setText(cppcheckMisraPath);
156 |
157 | modified = false;
158 | }
159 |
160 | @Override
161 | public void disposeUIResources() {
162 | cppcheckFilePicker.getTextField().getDocument().removeDocumentListener(listener);
163 | cppcheckOptionsField.getDocument().removeDocumentListener(listener);
164 | cppcheckVerboseLevel.removeChangeListener(listener);
165 | cppcheckMisraFilePicker.getTextField().getDocument().removeDocumentListener(listener);
166 | }
167 |
168 | private static class CppcheckConfigurationModifiedListener implements DocumentListener, ChangeListener {
169 | private final Configuration option;
170 |
171 | CppcheckConfigurationModifiedListener(final Configuration option) {
172 | this.option = option;
173 | }
174 |
175 | @Override
176 | public void insertUpdate(final DocumentEvent documentEvent) {
177 | option.setModified();
178 | }
179 |
180 | @Override
181 | public void removeUpdate(final DocumentEvent documentEvent) {
182 | option.setModified();
183 | }
184 |
185 | @Override
186 | public void changedUpdate(final DocumentEvent documentEvent) {
187 | option.setModified();
188 | }
189 |
190 | @Override
191 | public void stateChanged(final ChangeEvent e) { option.setModified(); }
192 | }
193 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # clion-cppcheck
2 |
3 | A plugin for JetBrains IDEs to provide inspections for C/C++ files utilizing the static analyzer [Cppcheck](https://cppcheck.sourceforge.io/).
4 |
5 | This project is supported with a free open source license of CLion from
6 | [JetBrains](https://www.jetbrains.com/?from=clion-cppcheck).
7 |
8 | ## Features
9 |
10 | - Runs `Cppcheck` on the fly while you write code.
11 | - Highlights lines and displays `Cppcheck` error messages.
12 | - Supports passing options to `Cppcheck`.
13 |
14 | ## Installation
15 |
16 | - Install the `cppcheck` plugin from the JetBrains Marketplace: https://plugins.jetbrains.com/plugin/8143-cppcheck. See
17 | [Installing, Updating and Uninstalling Repository Plugins](https://www.jetbrains.com/help/clion/managing-plugins.html)
18 | for more details.
19 |
20 | - Install Cppcheck. Please refer to https://github.com/danmar/cppcheck#packages on how to obtain a version of Cppcheck
21 | for your platform.
22 |
23 | - Go to the `Cppcheck Configuration` section in the settings of your respective JetBrains IDE and put the **absolute**
24 | path to the Cppcheck executable in the `Cppcheck Path`.
25 |
26 | - (Windows) The executable is found in the path you specified during the installation. By default this should be
27 | `C:\Program Files\Cppcheck\cppcheck.exe`.
28 | - (Non-Windows) Use `which cppcheck` or `command -v cppcheck` on the command-line to get the location of the executable.
29 | The default depends on your system but should usually be `/usr/bin/cppcheck` or `/usr/local/bin/cppcheck`.
30 |
31 | ## Usage
32 |
33 | ### Provided Actions
34 |
35 | The plugin provides the `Show Cppcheck XML Output` action which will show the raw XML output of the latest finished
36 | analysis.
37 |
38 | ## Plugin Configuration
39 |
40 | ### Verbose Level
41 |
42 | The verbose level of the plugin. The following additional information are provided:
43 |
44 | `0` - no verbose information
45 | `1` - a notification will be shown if the analysis finished
46 | `2` - a notification will be shown if the analysis was invoked (includes all command-line options)
47 | `4` - a notification will be shown for each findings in the result (will not show the internally ignored ones)
48 |
49 | ## Known Issues/Limitations
50 |
51 | See https://github.com/johnthagen/clion-cppcheck/issues for a complete list of tracked issues and enhancements requests.
52 |
53 | ### Analyzing header files
54 |
55 | Cppcheck is not designed to be run on header files (`.h`) directly, as must be done for this
56 | plugin, and as a result may have false positives.
57 |
58 | When run on header files directly, Cppcheck defaults to C as the language, which will generate
59 | false positives for C++ projects. So `--language=c++` is implicitly added as option when analyzing header files.
60 |
61 | It will also provide `unusedFunction` and `unusedStructMember` false positives so these findings are being suppressed.
62 |
63 | Related issues:
64 | https://github.com/johnthagen/clion-cppcheck/issues/22
65 | https://github.com/johnthagen/clion-cppcheck/issues/52
66 |
67 | ### Analyzing multiple configurations
68 |
69 | By default Cppcheck tries to determine all the available configurations for a file (i.e. all combination of the used
70 | preprocessor defines). As the plugin doesn't get the current list of defines this may lead to findings shown in code
71 | which is shown as disabled in the editor. To check just a specific configuration you can either add defines using `-D`
72 | to the options. Or you can limit the configurations to a single one adding `--max-configs=1`.
73 |
74 | By default Limiting the configurations also decreases the time of the analysis.
75 |
76 | By default a maximum of 12 configurations is checked. This may lead to some code which might actually be active not to
77 | show any findings. This can also be controlled by the `--max-configs=` option.
78 |
79 | Related issues:
80 | https://github.com/johnthagen/clion-cppcheck/issues/34
81 | https://github.com/johnthagen/clion-cppcheck/issues/52
82 |
83 | ### Multiple include paths
84 |
85 | No additional includes path are being passed to Cppcheck for the analysis which might result in false positives or not
86 | all findings being shown.
87 |
88 | You can add additional include path using the `-I ` options.
89 |
90 | Related issues:
91 | https://github.com/johnthagen/clion-cppcheck/issues/52
92 | https://github.com/johnthagen/clion-cppcheck/issues/55
93 |
94 | ### Batch analysis
95 |
96 | The batch analysis passes the files individually to Cppcheck just like the highlighting inspections. So if you pass a
97 | folder to the batch analysis it might not show the same findings as when passing a folder to `Cppcheck` itself.
98 |
99 | It will also pass all the contents of the folder to the analysis and not just project files. This might lead to
100 | unexpected findings.
101 |
102 | Also some findings in headers files triggered by the analysis of a source files are not being shown.
103 |
104 | Related issues:
105 | https://github.com/johnthagen/clion-cppcheck/issues/54
106 |
107 | ### Showing raw output
108 |
109 | `Show Cppcheck XML Output` only shows the XML result of the latest analysis. If you need to view the results for a
110 | specific file make sure it was the last one analyzed.
111 |
112 | Related issues:
113 | https://github.com/johnthagen/clion-cppcheck/issues/53
114 |
115 | ### External libraries / System includes
116 |
117 | Cppcheck does not support analyzing of external library or system includes. It provides profiles for several external
118 | libraries which describe the contents and behavior of the includes which allows it to finding issues with usage of them
119 | in the code. To add such a profile to your analysis you need to specify it via the `--library=` option. The
120 | available profile can be found in the `cfg` folder of your `Cppcheck` installation.
121 |
122 | ### Global options
123 |
124 | Currently the configured options are global and not per project.
125 |
126 | Related issues:
127 | https://github.com/johnthagen/clion-cppcheck/issues/52
128 |
129 | ## Development
130 |
131 | To run the plugin from source, open this project in IntelliJ and create a new "Plugin" run configuration. Running or
132 | debugging this run configuration will launch a new IntelliJ process with the plugin loaded into it.
133 |
134 | See this page for details: https://jetbrains.org/intellij/sdk/docs/basics/getting_started/setting_up_environment.html
135 |
136 | To build the plugin for deployment to the https://plugins.jetbrains.com/, select Build | Prepare Plugin Module For
137 | Deployment.
138 |
139 | ## Maintainers
140 |
141 | - @johnthagen
142 | - @firewave
143 |
144 | ## Releases
145 |
146 | ### 1.6.7 - 20XX-XX-XX
147 |
148 | ### 1.6.6 - 2024-01-06
149 |
150 | - Fixed `no location` error notifications for `checkersReport` when `information` is enabled.
151 |
152 | ### 1.6.5 - 2023-12-15
153 |
154 | - Removed `com.intellij.modules.java` dependency.
155 | - Bumped the oldest supported product versions to 2020.1.
156 |
157 | ### 1.6.4 - 2023-12-12
158 |
159 | - Added missing `com.intellij.modules.java` dependency.
160 |
161 | ### 1.6.3 - 2023-12-07
162 |
163 | - Added `Show Cppcheck XML Output` action to show the latest XML output.
164 | - Report execution errors as global inspection errors.
165 | - Display `Cppcheck Path` configuration errors as global inspection errors instead of using a (hard to spot) status bar
166 | message.
167 | - Display global inspection error and omit the option if the configured `MISRA Addon JSON` does not exist.
168 | - Made plugin verbose level configurable via settings.
169 | - Display all available details for findings in tooltip.
170 |
171 | ### 1.6.2 - 2022-01-25
172 |
173 | - Fixed `NullPointerException` with Cppcheck < 1.89 caused by missing `column` attribute in XML result.
174 |
175 | ### 1.6.1 - 2022-01-14
176 |
177 | - Fixed missing `commons-lang3` dependency.
178 | - Fixed `.idea` project provided by repository.
179 |
180 | ### 1.6.0 - 2021-12-26
181 |
182 | - Parse `--xml` output instead of text output. (Contribution by @firewave)
183 | - Fixed scanning of files with whitespaces in name. (Contribution by @firewave)
184 | - Only scan files which actually exist. (Contribution by @firewave)
185 | - Use unique file names for temporary files used for analysis. (Contribution by @firewave)
186 | - Properly handle `debug` messages generated by `--debug-warnings`. (Contribution by @firewave)
187 | - Added `.cl`, `.hxx`, `.tpp` and `.txx` to list of supported file extensions - now matches the ones supported by
188 | Cppcheck internally. (Contribution by @firewave)
189 | - Internally suppress `unusedFunction` and `unusedStructMember` (for header files only) warnings to avoid false
190 | positives. (Contribution by @firewave)
191 | - Fixed `information` messages not being shown at all. (Contribution by @firewave)
192 |
193 | ### 1.5.1 - 2020-11-12
194 |
195 | Improved reporting of execution failures. (Contribution by @firewave)
196 |
197 | ### 1.5.0 - 2020-11-01
198 |
199 | - Correctly specify minimum supported CLion version.
200 | - Support showing inconclusive annotations in the IDE. (Contribution by @firewave)
201 |
202 | ### 1.4.2 - 2020-04-06
203 |
204 | Fix NullPointerException. (Contribution by @firewave)
205 |
206 | ### 1.4.1 - 2020-04-06
207 |
208 | Fix NullPointerException. (Contribution by @firewave)
209 |
210 | ### 1.4.0 - 2020-04-03
211 |
212 | Support Cppcheck MISRA addon. (Contribution by @SJ-Innovation)
213 |
214 | ### 1.3.0 - 2020-03-28
215 |
216 | Support Cppcheck >1.89. (Contribution by @SJ-Innovation)
217 |
218 | ### 1.2.0 - 2018-04-11
219 |
220 | Greatly improve plugin responsiveness to changes by using virtual files to interact with Cppcheck.
221 | (Contribution by @fastasturtle)
222 |
223 | ### 1.1.0 - 2018-04-02
224 |
225 | Use `CapturingProcessHandler` to fix read locking issues and spaces in path to source files.
226 | (Contribution by @fastasturtle)
227 |
228 | ### 1.0.10 - 2018-01-06
229 |
230 | Fix formatting on plugin homepage.
231 |
232 | ### 1.0.9 - 2018-01-02
233 |
234 | Improve the user's guide for installing and using the plugin. Default to using `--language=c++` Cppcheck option.
235 |
236 | ### 1.0.8 - 2017-08-02
237 |
238 | Fix handling Cppcheck errors that span multiple lines.
239 |
240 | ### 1.0.7 - 2017-02-03
241 |
242 | Avoid drawing errors in .cpp and .c files from header files they import.
243 |
244 | ### 1.0.6 - 2016-02-25
245 |
246 | Fix NullPointerException when opening files with no extension.
247 |
248 | ### 1.0.5 - 2016-02-11
249 |
250 | Add warning about header file false positives in C++ projects.
251 |
252 | ### 1.0.4 - 2016-01-28
253 |
254 | Fix highlighting prepended whitespace.
255 |
256 | ### 1.0.3 - 2016-01-22
257 |
258 | Highlight line corresponding to severity.
259 |
260 | ### 1.0.2 - 2016-01-19
261 |
262 | Fix execution on Linux.
263 |
264 | ### 1.0.1 - 2016-01-11
265 |
266 | Fix possible out of bounds line number when Cppcheck gets out of sync with in-memory file.
267 |
268 | ### 1.0.0 - 2016-01-07
269 |
270 | First release.
271 |
--------------------------------------------------------------------------------
/src/com/github/johnthagen/cppcheck/CppCheckInspectionImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.johnthagen.cppcheck;
2 |
3 | import com.intellij.codeInspection.InspectionManager;
4 | import com.intellij.codeInspection.ProblemDescriptor;
5 | import com.intellij.codeInspection.ProblemHighlightType;
6 | import com.intellij.execution.ExecutionException;
7 | import com.intellij.execution.configurations.GeneralCommandLine;
8 | import com.intellij.execution.process.CapturingProcessHandler;
9 | import com.intellij.execution.process.ProcessOutput;
10 | import com.intellij.notification.NotificationType;
11 | import com.intellij.openapi.editor.Document;
12 | import com.intellij.openapi.progress.EmptyProgressIndicator;
13 | import com.intellij.openapi.progress.ProcessCanceledException;
14 | import com.intellij.openapi.progress.ProgressIndicator;
15 | import com.intellij.openapi.progress.ProgressManager;
16 | import com.intellij.openapi.util.TextRange;
17 | import com.intellij.openapi.vfs.VirtualFile;
18 | import com.intellij.psi.PsiFile;
19 | import com.intellij.util.DocumentUtil;
20 | import com.intellij.util.execution.ParametersListUtil;
21 | import org.jetbrains.annotations.NotNull;
22 | import org.w3c.dom.NamedNodeMap;
23 | import org.w3c.dom.Node;
24 | import org.w3c.dom.NodeList;
25 | import org.xml.sax.InputSource;
26 | import org.xml.sax.SAXException;
27 |
28 | import javax.xml.parsers.DocumentBuilder;
29 | import javax.xml.parsers.DocumentBuilderFactory;
30 | import javax.xml.parsers.ParserConfigurationException;
31 | import java.io.File;
32 | import java.io.IOException;
33 | import java.io.StringReader;
34 | import java.util.ArrayList;
35 | import java.util.List;
36 |
37 | class CppCheckInspectionImpl {
38 | CppCheckInspectionImpl(final int verboseLevel) {
39 | this.verboseLevel = verboseLevel;
40 | }
41 |
42 | private static ProblemHighlightType severityToHighlightType(@NotNull final String severity) {
43 | switch (severity) {
44 | case "error":
45 | return ProblemHighlightType.GENERIC_ERROR;
46 | case "warning":
47 | return ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
48 | case "style":
49 | case "performance":
50 | case "portability":
51 | case "debug":
52 | // INFORMATION problems are not shown in the IDE at all so we need to treat them as weak warnings
53 | case "information":
54 | return ProblemHighlightType.WEAK_WARNING;
55 |
56 | // If the severity is not understood (changes in Cppcheck), return ERROR.
57 | default:
58 | return ProblemHighlightType.ERROR;
59 | }
60 | }
61 |
62 | private final int verboseLevel;
63 | private static final String INCONCLUSIVE_TEXT = ":inconclusive";
64 |
65 | static class Location
66 | {
67 | Location(@NotNull final Node location) {
68 | final NamedNodeMap locationAttributes = location.getAttributes();
69 | file = new File(locationAttributes.getNamedItem("file").getNodeValue()).getName();
70 | line = Integer.parseInt(locationAttributes.getNamedItem("line").getNodeValue());
71 | final Node columnAttr = locationAttributes.getNamedItem("column");
72 | // the "column" attribute was added in Cppcheck 1.89
73 | if (columnAttr != null) {
74 | column = Integer.parseInt(columnAttr.getNodeValue());
75 | }
76 | else {
77 | column = -1;
78 | }
79 | final Node infoAttr = locationAttributes.getNamedItem("info");
80 | if (infoAttr != null) {
81 | info = infoAttr.getNodeValue();
82 | } else {
83 | info = null;
84 | }
85 | }
86 |
87 | final String file;
88 | final int line;
89 | final int column;
90 | final String info;
91 | }
92 |
93 | @NotNull
94 | public List parseOutput(@NotNull final PsiFile psiFile,
95 | @NotNull final InspectionManager manager,
96 | @NotNull final Document document,
97 | @NotNull final String cppcheckOutput,
98 | @NotNull final String sourceFileName) throws IOException, SAXException, ParserConfigurationException {
99 |
100 | final VirtualFile vFile = psiFile.getVirtualFile();
101 | if (verboseLevel >= 1) {
102 | // TODO: provide XML output via a "Show Cppcheck output" action - event log messages are truncated
103 | CppcheckNotification.send("finished analysis for " + vFile.getCanonicalPath(),
104 | "",
105 | NotificationType.INFORMATION);
106 | }
107 |
108 | final List descriptors = new ArrayList<>();
109 |
110 | final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
111 | final DocumentBuilder db = dbf.newDocumentBuilder();
112 | final org.w3c.dom.Document doc = db.parse(new InputSource(new StringReader(cppcheckOutput)));
113 |
114 | final NodeList errors = doc.getElementsByTagName("error");
115 | for (int i = 0; i < errors.getLength(); ++i) {
116 | /*
117 |
118 |
119 |
120 | a
121 |
122 | */
123 |
124 | final Node error = errors.item(i);
125 | final NamedNodeMap attributes = error.getAttributes();
126 |
127 | final String id = attributes.getNamedItem("id").getNodeValue();
128 |
129 | // Skip the "toomanyconfigs" error
130 | /*
131 |
132 |
133 |
134 | */
135 | if (id.equals("toomanyconfigs")) {
136 | continue;
137 | }
138 |
139 | // Skip the "missingIncludeSystem" error
140 | /*
141 |
142 | */
143 | if (id.equals("missingIncludeSystem")) {
144 | continue;
145 | }
146 |
147 | // suppress this warning until Cppcheck handles them in a better way
148 | if (id.equals("unusedFunction")) {
149 | continue;
150 | }
151 |
152 | // suppress this warnings for now - will be properly handled in an upcoming patch
153 | if (id.equals("noValidConfiguration") || id.equals("missingInclude")) {
154 | continue;
155 | }
156 |
157 | // we are never interested in these
158 | if (id.equals("unmatchedSuppression") || id.equals("purgedConfiguration")) {
159 | continue;
160 | }
161 |
162 | // Generated since 2.13.0 when "information" is enabled.
163 | /*
164 |
165 | */
166 | if (id.equals("checkersReport")) {
167 | continue;
168 | }
169 |
170 | // suppress this warning for headers until Cppcheck handles them in a better way
171 | if (SupportedExtensions.isHeaderFile(vFile) && id.equals("unusedStructMember")) {
172 | continue;
173 | }
174 |
175 | final String severity = attributes.getNamedItem("severity").getNodeValue();
176 | final String errorMessage = attributes.getNamedItem("msg").getNodeValue();
177 | final Node inconclusiveNode = attributes.getNamedItem("inconclusive");
178 | final boolean inconclusive = inconclusiveNode != null && inconclusiveNode.getNodeValue().equals("true");
179 |
180 | final ArrayList locations = new ArrayList<>();
181 |
182 | // look for the first "location" child name
183 | final NodeList children = error.getChildNodes();
184 | for (int j = 0; j < children.getLength(); ++j) {
185 | final Node child = children.item(j);
186 | if (child.getNodeName().equals("location")) {
187 | locations.add(new Location(child));
188 | }
189 | }
190 |
191 | // ignore entries without location e.g. missingIncludeSystem
192 | if (locations.isEmpty()) {
193 | CppcheckNotification.send("no location for " + vFile.getCanonicalPath(),
194 | id + " " + severity + " " + inconclusive + " " + errorMessage,
195 | NotificationType.ERROR);
196 | continue;
197 | }
198 |
199 | final String fileName = locations.get(0).file;
200 | int lineNumber = locations.get(0).line;
201 | // TODO: use in ProblemDescriptor
202 | final int column = locations.get(0).column;
203 |
204 | if (verboseLevel >= 4) {
205 | CppcheckNotification.send(id + " for " + vFile.getCanonicalPath(),
206 | id + " " + severity + " " + inconclusive + " " + errorMessage + " " + fileName + " " + lineNumber + " " + column,
207 | NotificationType.INFORMATION);
208 | }
209 |
210 | // If a file #include's header files, Cppcheck will also run on the header files and print
211 | // any errors. These errors don't apply to the current file and should not be drawn. They can
212 | // be distinguished by checking the file name.
213 | if (!fileName.equals(sourceFileName)) {
214 | continue;
215 | }
216 |
217 | // Cppcheck error
218 | if (lineNumber <= 0 || lineNumber > document.getLineCount()) {
219 | CppcheckNotification.send("line number out-of-bounds for " + vFile.getCanonicalPath(),
220 | id + " " + severity + " " + inconclusive + " " + errorMessage + " " + fileName + " " + lineNumber + " " + column,
221 | NotificationType.ERROR);
222 | continue;
223 | }
224 |
225 | // Document counts lines starting at 0, rather than 1 like in cppcheck.
226 | lineNumber -= 1;
227 |
228 | final int lineStartOffset = DocumentUtil.getFirstNonSpaceCharOffset(document, lineNumber);
229 | final int lineEndOffset = document.getLineEndOffset(lineNumber);
230 |
231 | String details = "";
232 | if (locations.size() > 1) {
233 | final StringBuilder sb = new StringBuilder();
234 | for (final Location l : locations) {
235 | if (l.info == null)
236 | continue;
237 | final String name;
238 | if (l.file.equals(sourceFileName)) {
239 | name = vFile.getName(); // replace temporary file name with actual name
240 | } else {
241 | name = l.file;
242 | }
243 | sb.append(String.format("\n%s:%d: note: %s", name, l.line, l.info));
244 | }
245 | if (sb.length() > 0) {
246 | details = "\n" + sb;
247 | }
248 | }
249 |
250 | final ProblemDescriptor problemDescriptor = manager.createProblemDescriptor(
251 | psiFile,
252 | TextRange.create(lineStartOffset, lineEndOffset),
253 | "Cppcheck: (" + severity + (inconclusive ? INCONCLUSIVE_TEXT : "") + ") " + id + ": " + errorMessage + details,
254 | severityToHighlightType(severity),
255 | true);
256 | descriptors.add(problemDescriptor);
257 | }
258 | return descriptors;
259 | }
260 |
261 | private static final int TIMEOUT_MS = 60 * 1000;
262 |
263 | public String executeCommandOnFile(@NotNull final VirtualFile vFile,
264 | @NotNull final File command,
265 | @NotNull final String options,
266 | @NotNull final File filePath,
267 | final String cppcheckMisraPath) throws CppcheckError, ExecutionException {
268 | final GeneralCommandLine cmd = new GeneralCommandLine()
269 | .withExePath(command.toString())
270 | .withParameters(ParametersListUtil.parse(options))
271 | .withParameters(ParametersListUtil.parse("\"" + filePath.getAbsolutePath() + "\""));
272 |
273 | // Need to be able to get python from the system env
274 | if (cppcheckMisraPath != null && !cppcheckMisraPath.isEmpty()) {
275 | cmd.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.SYSTEM);
276 | }
277 |
278 | if (verboseLevel >= 2) {
279 | CppcheckNotification.send("options for " + vFile.getCanonicalPath(),
280 | cmd.getCommandLineString(),
281 | NotificationType.INFORMATION);
282 | }
283 |
284 | final CapturingProcessHandler processHandler = new CapturingProcessHandler(cmd);
285 | final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
286 | final ProcessOutput output = processHandler.runProcessWithProgressIndicator(
287 | indicator != null ? indicator : new EmptyProgressIndicator(),
288 | TIMEOUT_MS);
289 |
290 | if (output.isCancelled()) {
291 | throw new ProcessCanceledException();
292 | }
293 |
294 | if (output.isTimeout()) {
295 | throw new CppcheckError("Timeout\n"
296 | + cmd.getCommandLineString());
297 | }
298 |
299 | if (output.getExitCode() != 0) {
300 | throw new CppcheckError("Exit Code " + output.getExitCode() + "\n" +
301 | "stdout: " + output.getStdout() + "\n" +
302 | cmd.getCommandLineString());
303 | }
304 |
305 | if (cppcheckMisraPath != null && !cppcheckMisraPath.isEmpty()) {
306 | if (output.getStdout().contains("Bailing out from checking")) {
307 | // MISRA Mode and something went wrong with the misra addon
308 | throw new CppcheckError("MISRA Bail\n" +
309 | cmd.getCommandLineString() + "\n" +
310 | "stdout: " + output.getStdout() + "\n" +
311 | "stderr: " + output.getStderr());
312 | }
313 | }
314 |
315 | return output.getStderr();
316 | }
317 | }
318 |
--------------------------------------------------------------------------------