├── .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 | 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 | --------------------------------------------------------------------------------