├── .github └── workflows │ └── CI.yml ├── .gitignore ├── CPPCheckPlugin.sln ├── CPPCheckPlugin ├── AnalyzerCppcheck.cs ├── AtomicBool.cs ├── CPPCheckPlugin.csproj ├── CPPCheckPlugin.vsct ├── CPPCheckPluginPackage.cs ├── ChecksPanel.cs ├── CppcheckMessagesList.xaml ├── CppcheckMessagesList.xaml.cs ├── CppcheckSettings.xaml ├── CppcheckSettings.xaml.cs ├── DebugTracer.cs ├── GlobalSuppressions.cs ├── Guids.cs ├── ICodeAnalyzer.cs ├── Key.snk ├── MainToolWindow.cs ├── MainToolWindowUI.xaml ├── MainToolWindowUI.xaml.cs ├── PackageInstallerLicense.txt ├── PkgCmdID.cs ├── Problem.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources.Designer.cs ├── Resources.resx ├── Resources │ ├── Images.png │ └── Package.ico ├── Settings.cs ├── SourceFile.cs ├── SuppressionsInfo.cs ├── SuppressionsSettingsUI │ ├── ItemsListEditor.xaml │ ├── ItemsListEditor.xaml.cs │ ├── SuppressionsSettings.xaml │ └── SuppressionsSettings.xaml.cs ├── VSPackage.resx ├── app.config └── source.extension.vsixmanifest ├── LICENSE ├── README.md └── appveyor.yml /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | # For more information on GitHub Actions, refer to https://github.com/features/actions 2 | # For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications, 3 | # refer to https://github.com/microsoft/github-actions-for-desktop-apps 4 | 5 | name: CI Build 6 | 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | jobs: 14 | 15 | build: 16 | 17 | strategy: 18 | matrix: 19 | configuration: [Debug, Release] 20 | 21 | runs-on: windows-latest # For a list of available runner types, refer to 22 | # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on 23 | 24 | env: 25 | Solution_Name: CPPCheckPlugin.sln # Replace with your solution name, i.e. MyWpfApp.sln. 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 0 32 | 33 | # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild 34 | - name: Setup MSBuild.exe 35 | uses: microsoft/setup-msbuild@v1.0.2 36 | 37 | - name: Nuget 38 | run: nuget restore 39 | 40 | - name: Build the application 41 | run: msbuild $env:Solution_Name /t:Build /p:Configuration=$env:Configuration 42 | env: 43 | Configuration: ${{ matrix.configuration }} 44 | 45 | - uses: actions/upload-artifact@v2 46 | with: 47 | name: cppcheck-vs-addin 48 | path: .\CPPCheckPlugin\bin\**\*.vsix 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | UpgradeLog*.htm* 2 | *.suo 3 | Backup* 4 | CPPCheckPlugin/bin 5 | CPPCheckPlugin/obj 6 | CPPCheckPlugin.csproj.user 7 | storage.ide 8 | packages 9 | .vs/* -------------------------------------------------------------------------------- /CPPCheckPlugin.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPPCheckPlugin", "CPPCheckPlugin\CPPCheckPlugin.csproj", "{47946420-6C85-44FA-9E95-8FB46ADAC152}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{50A8FDAE-6798-4956-9FF5-9137BC98EB66}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | appveyor.yml = appveyor.yml 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {47946420-6C85-44FA-9E95-8FB46ADAC152}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {47946420-6C85-44FA-9E95-8FB46ADAC152}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {47946420-6C85-44FA-9E95-8FB46ADAC152}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {47946420-6C85-44FA-9E95-8FB46ADAC152}.Release|Any CPU.Build.0 = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(SolutionProperties) = preSolution 27 | HideSolutionNode = FALSE 28 | EndGlobalSection 29 | GlobalSection(ExtensibilityGlobals) = postSolution 30 | SolutionGuid = {9006B35E-D161-4206-8DBE-33F21C5B373E} 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /CPPCheckPlugin/AnalyzerCppcheck.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using EnvDTE; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Windows.Forms; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace VSPackage.CPPCheckPlugin 11 | { 12 | class AnalyzerCppcheck : ICodeAnalyzer 13 | { 14 | private const string tempFilePrefix = "CPPCheckPlugin"; 15 | 16 | public AnalyzerCppcheck() 17 | { 18 | // Perform some cleanup of old temporary files 19 | string tempPath = Path.GetTempPath(); 20 | 21 | try 22 | { 23 | // Get all files that have our unique prefix 24 | string[] oldFiles = Directory.GetFiles(tempPath, tempFilePrefix + "*"); 25 | 26 | foreach (string file in oldFiles) 27 | { 28 | DateTime fileModifiedDate = File.GetLastWriteTime(file); 29 | 30 | if (fileModifiedDate.AddMinutes(120) < DateTime.Now) 31 | { 32 | // File hasn't been written to in the last 120 minutes, so it must be 33 | // from an earlier instance which didn't exit gracefully. 34 | File.Delete(file); 35 | } 36 | } 37 | } 38 | catch (System.Exception) { } 39 | } 40 | 41 | ~AnalyzerCppcheck() 42 | { 43 | cleanupTempFiles(); 44 | } 45 | 46 | public static string cppcheckExePath() 47 | { 48 | System.Collections.Specialized.StringCollection analyzerPathCandidates = Properties.Settings.Default.CPPcheckPath; 49 | string analyzerPath = ""; 50 | 51 | foreach (string candidatePath in analyzerPathCandidates) 52 | { 53 | if (File.Exists(candidatePath)) 54 | { 55 | analyzerPath = candidatePath; 56 | break; 57 | } 58 | } 59 | 60 | if (String.IsNullOrEmpty(analyzerPath)) 61 | { 62 | OpenFileDialog dialog = new OpenFileDialog(); 63 | dialog.Filter = "cppcheck executable|cppcheck.exe"; 64 | if (dialog.ShowDialog() != DialogResult.OK) 65 | return String.Empty; 66 | 67 | analyzerPath = dialog.FileName; 68 | if (File.Exists(analyzerPath)) 69 | { 70 | Properties.Settings.Default.CPPcheckPath.Add(analyzerPath); 71 | Properties.Settings.Default.Save(); 72 | } 73 | } 74 | 75 | return analyzerPath; 76 | } 77 | 78 | private string getCPPCheckArgs(SourceFilesWithConfiguration configuredFiles, bool analysisOnSavedFile, string tempFileName) 79 | { 80 | if (!configuredFiles.Any()) 81 | { 82 | Debug.Fail("Empty files list!"); 83 | return ""; 84 | } 85 | 86 | Debug.Assert(_numCores > 0); 87 | String cppheckargs = Properties.Settings.Default.DefaultArguments; 88 | 89 | if (Properties.Settings.Default.SeveritiesString.Length != 0) 90 | cppheckargs += " --enable=" + Properties.Settings.Default.SeveritiesString; 91 | 92 | HashSet suppressions = new HashSet(Properties.Settings.Default.SuppressionsString.Split(',')); 93 | suppressions.Add("unmatchedSuppression"); 94 | 95 | HashSet skippedFilesMask = new HashSet(); 96 | HashSet skippedIncludeMask = new HashSet(); 97 | 98 | SuppressionsInfo unitedSuppressionsInfo = readSuppressions(ICodeAnalyzer.SuppressionStorage.Global); 99 | unitedSuppressionsInfo.UnionWith(readSuppressions(ICodeAnalyzer.SuppressionStorage.Solution)); 100 | 101 | var filesToAnalyze = configuredFiles.Files; 102 | // Creating the list of all different project locations (no duplicates) 103 | HashSet projectPaths = new HashSet(); // enforce uniqueness on the list of project paths 104 | foreach (var file in filesToAnalyze) 105 | { 106 | projectPaths.Add(file.BaseProjectPath); 107 | } 108 | 109 | Debug.Assert(projectPaths.Count == 1); 110 | _projectBasePath = projectPaths.First(); 111 | _projectName = filesToAnalyze.First().ProjectName; 112 | 113 | // Creating the list of all different suppressions (no duplicates) 114 | foreach (var path in projectPaths) 115 | { 116 | unitedSuppressionsInfo.UnionWith(readSuppressions(SuppressionStorage.Project, path, filesToAnalyze.First().ProjectName)); 117 | } 118 | 119 | cppheckargs += (" --relative-paths=\"" + _projectBasePath + "\""); 120 | cppheckargs += (" -j " + _numCores.ToString()); 121 | if (Properties.Settings.Default.InconclusiveChecksEnabled) 122 | cppheckargs += " --inconclusive "; 123 | 124 | suppressions.UnionWith(unitedSuppressionsInfo.SuppressionLines); 125 | foreach (string suppression in suppressions) 126 | { 127 | if (!String.IsNullOrWhiteSpace(suppression)) 128 | cppheckargs += (" --suppress=" + suppression); 129 | } 130 | 131 | if (!(analysisOnSavedFile && Properties.Settings.Default.IgnoreIncludePaths)) 132 | { 133 | // We only add include paths once, and then specify a set of files to check 134 | HashSet includePaths = new HashSet(); 135 | foreach (var file in filesToAnalyze) 136 | { 137 | if (!matchMasksList(file.FilePath, unitedSuppressionsInfo.SkippedFilesMask)) 138 | includePaths.UnionWith(file.IncludePaths); 139 | } 140 | 141 | includePaths.Add(filesToAnalyze.First().BaseProjectPath); // Fix for #60 142 | 143 | foreach (string path in includePaths) 144 | { 145 | if (!matchMasksList(path, unitedSuppressionsInfo.SkippedIncludesMask)) 146 | { 147 | String includeArgument = " -I\"" + path + "\""; 148 | cppheckargs = cppheckargs + " " + includeArgument; 149 | } 150 | } 151 | } 152 | 153 | using (StreamWriter tempFile = new StreamWriter(tempFileName)) 154 | { 155 | foreach (SourceFile file in filesToAnalyze) 156 | { 157 | if (!matchMasksList(file.FilePath, unitedSuppressionsInfo.SkippedFilesMask)) 158 | tempFile.WriteLine(file.FilePath); 159 | } 160 | } 161 | 162 | cppheckargs += " --file-list=\"" + tempFileName + "\""; 163 | 164 | if ((analysisOnSavedFile && Properties.Settings.Default.FileOnlyCheckCurrentConfig) || 165 | (!analysisOnSavedFile && Properties.Settings.Default.ProjectOnlyCheckCurrentConfig)) // Only checking current macros configuration (for speed) 166 | { 167 | cppheckargs = cppheckargs.Replace("--force", ""); 168 | // Creating the list of all different macros (no duplicates) 169 | HashSet macros = new HashSet(); 170 | // TODO: handle /Zc:__cplusplus 171 | // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ 172 | macros.Add("__cplusplus=199711L"); // At least in VS2012, this is still 199711L 173 | // Assuming all files passed here are from the same project / same toolset, which should be true, so peeking the first file for global settings 174 | switch (filesToAnalyze.First().vcCompilerVersion) 175 | { 176 | case SourceFile.VCCompilerVersion.vc2003: 177 | macros.Add("_MSC_VER=1310"); 178 | break; 179 | case SourceFile.VCCompilerVersion.vc2005: 180 | macros.Add("_MSC_VER=1400"); 181 | break; 182 | case SourceFile.VCCompilerVersion.vc2008: 183 | macros.Add("_MSC_VER=1500"); 184 | break; 185 | case SourceFile.VCCompilerVersion.vc2010: 186 | macros.Add("_MSC_VER=1600"); 187 | break; 188 | case SourceFile.VCCompilerVersion.vc2012: 189 | macros.Add("_MSC_VER=1700"); 190 | break; 191 | case SourceFile.VCCompilerVersion.vc2013: 192 | macros.Add("_MSC_VER=1800"); 193 | break; 194 | case SourceFile.VCCompilerVersion.vc2015: 195 | macros.Add("_MSC_VER=1900"); 196 | break; 197 | case SourceFile.VCCompilerVersion.vc2017: 198 | macros.Add("_MSC_VER=1916"); 199 | break; 200 | case SourceFile.VCCompilerVersion.vc2019: 201 | macros.Add("_MSC_VER=1926"); 202 | macros.Add("_MSC_FULL_VER=192628808"); 203 | break; 204 | } 205 | 206 | foreach (var file in filesToAnalyze) 207 | { 208 | macros.UnionWith(file.Macros); 209 | } 210 | macros.Add("WIN32"); 211 | macros.Add("_WIN32"); 212 | 213 | CPPCheckPluginPackage.Instance.JoinableTaskFactory.Run(async () => 214 | { 215 | if (await configuredFiles.is64bitConfigurationAsync()) 216 | { 217 | macros.Add("_M_X64"); 218 | macros.Add("_WIN64"); 219 | } 220 | else 221 | { 222 | macros.Add("_M_IX86"); 223 | } 224 | 225 | if (await configuredFiles.isDebugConfigurationAsync()) 226 | macros.Add("_DEBUG"); 227 | }); 228 | 229 | foreach (string macro in macros) 230 | { 231 | if (!String.IsNullOrEmpty(macro) && !macro.Contains(" ") /* macros with spaces are invalid in VS */) 232 | { 233 | String macroArgument = " -D" + macro; 234 | cppheckargs += macroArgument; 235 | } 236 | } 237 | 238 | HashSet macrosToUndefine = new HashSet(); 239 | foreach (var file in filesToAnalyze) 240 | { 241 | macrosToUndefine.UnionWith(file.MacrosToUndefine); 242 | } 243 | 244 | foreach (string macro in macrosToUndefine) 245 | { 246 | if (!String.IsNullOrEmpty(macro) && !macro.Contains(" ") /* macros with spaces are invalid in VS */) 247 | { 248 | String macroUndefArgument = " -U" + macro; 249 | cppheckargs += macroUndefArgument; 250 | } 251 | } 252 | } 253 | else if (!cppheckargs.Contains("--force")) 254 | cppheckargs += " --force"; 255 | 256 | return cppheckargs; 257 | } 258 | 259 | public override void analyze(List allConfiguredFiles, bool analysisOnSavedFile) 260 | { 261 | if (!allConfiguredFiles.Any()) 262 | return; 263 | else 264 | { 265 | bool validFilesQueuedForCheck = false; 266 | foreach (SourceFilesWithConfiguration files in allConfiguredFiles) 267 | if (files.Files.Any()) 268 | { 269 | validFilesQueuedForCheck = true; 270 | break; 271 | } 272 | 273 | if (!validFilesQueuedForCheck) 274 | return; 275 | } 276 | 277 | 278 | List cppheckargs = new List(); 279 | foreach (var configuredFiles in allConfiguredFiles) 280 | cppheckargs.Add(getCPPCheckArgs(configuredFiles, analysisOnSavedFile, createNewTempFileName())); 281 | 282 | run(cppcheckExePath(), cppheckargs); 283 | } 284 | 285 | public override void suppressProblem(Problem p, SuppressionScope scope) 286 | { 287 | if (p == null) 288 | return; 289 | 290 | String simpleFileName = p.FileName; 291 | 292 | String suppressionLine = null; 293 | switch (scope) 294 | { 295 | case SuppressionScope.suppressAllMessagesThisFileGlobally: 296 | case SuppressionScope.suppressAllMessagesThisFileSolutionWide: 297 | case SuppressionScope.suppressAllMessagesThisFileProjectWide: 298 | suppressionLine = "*:" + simpleFileName; 299 | break; 300 | case SuppressionScope.suppressThisTypeOfMessageFileWide: 301 | suppressionLine = p.MessageId + ":" + simpleFileName; 302 | break; 303 | case SuppressionScope.suppressThisTypeOfMessagesGlobally: 304 | case SuppressionScope.suppressThisTypeOfMessageProjectWide: 305 | case SuppressionScope.suppressThisTypeOfMessagesSolutionWide: 306 | suppressionLine = p.MessageId; 307 | break; 308 | case SuppressionScope.suppressThisMessage: 309 | case SuppressionScope.suppressThisMessageSolutionWide: 310 | case SuppressionScope.suppressThisMessageGlobally: 311 | suppressionLine = p.MessageId + ":" + simpleFileName + ":" + p.Line; 312 | break; 313 | default: 314 | throw new InvalidOperationException("Unsupported value: " + scope.ToString()); 315 | } 316 | 317 | String suppressionsFilePath = suppressionsFilePathByScope(scope, p.BaseProjectPath, p.ProjectName); 318 | Debug.Assert(suppressionsFilePath != null); 319 | 320 | SuppressionsInfo suppressionsInfo = new SuppressionsInfo(); 321 | suppressionsInfo.LoadFromFile(suppressionsFilePath); 322 | 323 | suppressionsInfo.AddSuppressionLine(suppressionLine); 324 | 325 | suppressionsInfo.SaveToFile(suppressionsFilePath); 326 | } 327 | 328 | protected override SuppressionsInfo readSuppressions(SuppressionStorage storage, string projectBasePath = null, string projectName = null) 329 | { 330 | SuppressionsInfo suppressionsInfo = new SuppressionsInfo(); 331 | 332 | String suppressionsFilePath = suppressionsFilePathByStorage(storage, projectBasePath, projectName); 333 | suppressionsInfo.LoadFromFile(suppressionsFilePath); 334 | 335 | return suppressionsInfo; 336 | } 337 | 338 | protected override List parseOutput(String output) 339 | { 340 | // template={file}|{line}|{severity}|{id}|{message} 341 | 342 | if (String.IsNullOrWhiteSpace(output)) 343 | return null; 344 | 345 | try 346 | { 347 | Match progressValueMatch = Regex.Match(output, @"([0-9]+)% done"); 348 | if (progressValueMatch.Success) 349 | { 350 | // This is a progress update 351 | int progress = Convert.ToInt32(progressValueMatch.Groups[1].Value.Replace("% done", "")); 352 | int filesChecked = 0, totalFiles = 0; 353 | Match filesProgressMatch = Regex.Match(output, @"([0-9]+)/([0-9]+) files checked"); 354 | if (filesProgressMatch.Success) 355 | { 356 | filesChecked = Convert.ToInt32(filesProgressMatch.Groups[1].ToString()); 357 | totalFiles = Convert.ToInt32(filesProgressMatch.Groups[2].ToString()); 358 | } 359 | onProgressUpdated(progress, filesChecked, totalFiles); 360 | 361 | if (_unfinishedProblem == null) 362 | { 363 | return null; 364 | } 365 | List list = new List(); 366 | list.Add(_unfinishedProblem); // Done with the current message 367 | _unfinishedProblem = null; 368 | return list; 369 | } 370 | } 371 | catch (System.Exception) { } 372 | 373 | if (output.StartsWith("Checking ")) 374 | { 375 | if (_unfinishedProblem == null) 376 | { 377 | return null; 378 | } 379 | List list = new List(); 380 | list.Add(_unfinishedProblem); // Done with the current message 381 | _unfinishedProblem = null; 382 | return list; 383 | } 384 | else if (!output.Contains("|")) // This line does not represent a new defect found by cppcheck; could be continuation of a multi-line issue description 385 | { 386 | if (_unfinishedProblem != null) 387 | _unfinishedProblem.Message += "\n" + output; 388 | 389 | return null; // Not done with the current message yet 390 | } 391 | 392 | String[] parsed = output.Split('|'); 393 | if (parsed.Length != 5) 394 | return null; 395 | 396 | // New issue found - finalize the previous one 397 | List result = new List(); 398 | if (_unfinishedProblem != null) 399 | result.Add(_unfinishedProblem); 400 | 401 | Problem.SeverityLevel severity = Problem.SeverityLevel.info; 402 | if (parsed[2] == "error") 403 | severity = Problem.SeverityLevel.error; 404 | else if (parsed[2] == "warning") 405 | severity = Problem.SeverityLevel.warning; 406 | 407 | _unfinishedProblem = new Problem(this, severity, parsed[3], parsed[4], parsed[0], String.IsNullOrWhiteSpace(parsed[1]) ? 0 : Int32.Parse(parsed[1]), _projectBasePath, _projectName); 408 | 409 | MainToolWindow.Instance.bringToFront(); 410 | 411 | return result; 412 | } 413 | 414 | protected override void analysisFinished(string arguments) 415 | { 416 | if (_unfinishedProblem != null) 417 | addProblemToToolwindow(_unfinishedProblem); 418 | 419 | const string fileListPattern = "--file-list=\""; 420 | int filenamePos = arguments.IndexOf(fileListPattern) + fileListPattern.Length; 421 | int filenameLength = arguments.IndexOf('\"', filenamePos) - filenamePos; 422 | string tempFileName = arguments.Substring(filenamePos, filenameLength); 423 | 424 | File.Delete(tempFileName); 425 | } 426 | 427 | private void cleanupTempFiles() 428 | { 429 | // Delete the temp files. Doesn't throw an exception if the file was never 430 | // created, so we don't need to worry about that. 431 | foreach (string name in _tempFileNamesInUse) 432 | File.Delete(name); 433 | 434 | _tempFileNamesInUse.Clear(); 435 | } 436 | 437 | private string createNewTempFileName() 438 | { 439 | string name = Path.GetTempPath() + tempFilePrefix + "_" + Path.GetRandomFileName(); 440 | _tempFileNamesInUse.Add(name); 441 | return name; 442 | } 443 | 444 | private Problem _unfinishedProblem = null; 445 | private List _tempFileNamesInUse = new List(); 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /CPPCheckPlugin/AtomicBool.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace VSPackage.CPPCheckPlugin 4 | { 5 | public class AtomicBool 6 | { 7 | public AtomicBool() 8 | { 9 | } 10 | 11 | [MethodImpl(MethodImplOptions.Synchronized)] 12 | public AtomicBool(bool value) 13 | { 14 | _value = value; 15 | } 16 | 17 | [MethodImpl(MethodImplOptions.Synchronized)] 18 | public static implicit operator bool(AtomicBool a) 19 | { 20 | return a._value; 21 | } 22 | 23 | public bool Value 24 | { 25 | [MethodImpl(MethodImplOptions.Synchronized)] 26 | get => _value; 27 | [MethodImpl(MethodImplOptions.Synchronized)] 28 | set { _value = value; } 29 | } 30 | 31 | private bool _value = false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CPPCheckPlugin/CPPCheckPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | 11.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | 8 | 9 | 10 | 11 | 12.0 12 | false 13 | 14 | publish\ 15 | true 16 | Disk 17 | false 18 | Foreground 19 | 7 20 | Days 21 | false 22 | false 23 | true 24 | 0 25 | 1.0.0.%2a 26 | false 27 | true 28 | 29 | 30 | 31 | 32 | Resources\Package.ico 33 | 34 | 35 | 36 | Debug 37 | AnyCPU 38 | 2.0 39 | {47946420-6C85-44FA-9E95-8FB46ADAC152} 40 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 41 | Library 42 | Properties 43 | VSPackage.CPPCheckPlugin 44 | CppcheckPlugin 45 | True 46 | Key.snk 47 | v4.7.2 48 | 49 | 50 | true 51 | full 52 | false 53 | bin\Debug\ 54 | DEBUG;TRACE 55 | prompt 56 | 4 57 | 58 | 59 | pdbonly 60 | true 61 | bin\Release\ 62 | TRACE 63 | prompt 64 | 4 65 | true 66 | 67 | 68 | 69 | 70 | False 71 | ..\packages\VSSDK.DTE.7.0.4\lib\net20\stdole.dll 72 | True 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | CppcheckMessagesList.xaml 95 | 96 | 97 | CppcheckSettings.xaml 98 | 99 | 100 | 101 | 102 | 103 | 104 | MainToolWindowUI.xaml 105 | 106 | 107 | 108 | True 109 | True 110 | Settings.settings 111 | 112 | 113 | True 114 | True 115 | Resources.resx 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | ItemsListEditor.xaml 126 | 127 | 128 | SuppressionsSettings.xaml 129 | 130 | 131 | 132 | 133 | ResXFileCodeGenerator 134 | Resources.Designer.cs 135 | Designer 136 | 137 | 138 | true 139 | VSPackage 140 | 141 | 142 | 143 | 144 | Designer 145 | 146 | 147 | SettingsSingleFileGenerator 148 | Settings.Designer.cs 149 | 150 | 151 | Designer 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | Menus.ctmenu 160 | Designer 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | Always 169 | true 170 | 171 | 172 | 173 | 174 | 175 | Designer 176 | MSBuild:Compile 177 | MSBuild:Compile 178 | Designer 179 | 180 | 181 | Designer 182 | MSBuild:Compile 183 | MSBuild:Compile 184 | Designer 185 | MSBuild:Compile 186 | Designer 187 | 188 | 189 | Designer 190 | MSBuild:Compile 191 | MSBuild:Compile 192 | Designer 193 | 194 | 195 | Designer 196 | MSBuild:Compile 197 | MSBuild:Compile 198 | Designer 199 | 200 | 201 | Designer 202 | MSBuild:Compile 203 | MSBuild:Compile 204 | Designer 205 | 206 | 207 | 208 | 209 | False 210 | Microsoft .NET Framework 4.5 %28x86 and x64%29 211 | true 212 | 213 | 214 | False 215 | .NET Framework 3.5 SP1 216 | false 217 | 218 | 219 | 220 | 221 | 222 | 223 | 16.10.10 224 | runtime; build; native; contentfiles; analyzers; buildtransitive 225 | all 226 | 227 | 228 | 17.3.32804.24 229 | 230 | 231 | 17.3.44 232 | 233 | 234 | 17.3.44 235 | runtime; build; native; contentfiles; analyzers; buildtransitive 236 | all 237 | 238 | 239 | 17.3.32803.143 240 | 241 | 242 | 13.0.1 243 | 244 | 245 | 4.5.4 246 | 247 | 248 | 249 | true 250 | 251 | 252 | 253 | 260 | -------------------------------------------------------------------------------- /CPPCheckPlugin/CPPCheckPlugin.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 77 | 78 | 85 | 86 | 93 | 94 | 101 | 102 | 109 | 110 | 117 | 118 | 119 | 126 | 127 | 134 | 135 | 142 | 143 | 144 | 145 | 146 | 147 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /CPPCheckPlugin/CPPCheckPluginPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Runtime.InteropServices; 5 | using System.ComponentModel.Design; 6 | using Microsoft; 7 | using Microsoft.VisualStudio; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using Microsoft.VisualStudio.Shell; 10 | using EnvDTE; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using System.IO; 16 | using VSPackage.CPPCheckPlugin.Properties; 17 | using Microsoft.VisualStudio.VCProjectEngine; 18 | 19 | using Task = System.Threading.Tasks.Task; 20 | using System.Windows.Forms; 21 | 22 | 23 | namespace VSPackage.CPPCheckPlugin 24 | { 25 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 26 | [DefaultRegistryRoot(@"Software\Microsoft\VisualStudio\11.0")] 27 | [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string, PackageAutoLoadFlags.BackgroundLoad)] 28 | // This attribute is used to register the information needed to show this package 29 | // in the Help/About dialog of Visual Studio. 30 | [InstalledProductRegistration("#110", "#112", "1.2.0", IconResourceID = 400)] 31 | // This attribute is needed to let the shell know that this package exposes some menus. 32 | [ProvideMenuResource("Menus.ctmenu", 1)] 33 | [ProvideToolWindow(typeof(MainToolWindow), Style = VsDockStyle.Tabbed, Window = ToolWindowGuids.Outputwindow, MultiInstances = false, Transient = false)] 34 | [Guid(GuidList.guidCPPCheckPluginPkgString)] 35 | public sealed class CPPCheckPluginPackage : AsyncPackage 36 | { 37 | private int completedFileCount = 0, lastAnalyzerTotalFiles = 0; 38 | 39 | public CPPCheckPluginPackage() 40 | { 41 | _instance = this; 42 | 43 | CreateDefaultGlobalSuppressions(); 44 | } 45 | 46 | public static CPPCheckPluginPackage Instance 47 | { 48 | get { return _instance; } 49 | } 50 | 51 | public static async Task AddTextToOutputWindowAsync(string text) 52 | { 53 | try 54 | { 55 | await Instance.JoinableTaskFactory.SwitchToMainThreadAsync(); 56 | 57 | Assumes.NotNull(Instance._outputPane); 58 | Instance._outputPane.OutputString(text); 59 | } 60 | catch (Exception e) 61 | { 62 | Debug.WriteLine("Exception in addTextToOutputWindow(): " + e.Message); 63 | } 64 | } 65 | 66 | public static string solutionName() 67 | { 68 | return _instance.JoinableTaskFactory.Run(async () => 69 | { 70 | await _instance.JoinableTaskFactory.SwitchToMainThreadAsync(); 71 | try { return Path.GetFileNameWithoutExtension(_dte.Solution.FullName); } 72 | catch (Exception) { return ""; } 73 | }); 74 | } 75 | 76 | public static string solutionPath() 77 | { 78 | return _instance.JoinableTaskFactory.Run(async () => 79 | { 80 | await _instance.JoinableTaskFactory.SwitchToMainThreadAsync(); 81 | try { return Path.GetDirectoryName(_dte.Solution.FullName); } 82 | catch (Exception) { return ""; } 83 | }); 84 | } 85 | 86 | public static async Task activeProjectNameAsync() 87 | { 88 | var projects = await _instance.findSelectedCppProjectsAsync(); 89 | Assumes.NotNull(projects); 90 | return projects.Any() ? (projects.First() as dynamic).Name : ""; 91 | } 92 | 93 | public static async Task activeProjectPathAsync() 94 | { 95 | var projects = await _instance.findSelectedCppProjectsAsync(); 96 | Assumes.NotNull(projects); 97 | 98 | if (projects.Any()) 99 | { 100 | string projectDirectory = (projects.First() as dynamic).ProjectDirectory; 101 | return projectDirectory.Replace("\"", ""); 102 | } 103 | 104 | return ""; 105 | } 106 | 107 | #region Package Members 108 | 109 | MenuCommand menuCheckCurrentProject, menuCheckCurrentProjectContext, menuCheckCurrentProjectsContext; 110 | MenuCommand menuShowSettingsWindow; 111 | MenuCommand menuCancelCheck; 112 | MenuCommand menuCheckSelections, checkMultiSelections; 113 | 114 | private void setMenuState(bool bBusy) 115 | { 116 | menuCheckCurrentProject.Enabled = !bBusy; 117 | menuCheckCurrentProjectContext.Enabled = !bBusy; 118 | menuCheckCurrentProjectsContext.Enabled = !bBusy; 119 | menuShowSettingsWindow.Enabled = !bBusy; 120 | menuCancelCheck.Enabled = bBusy; 121 | menuCheckSelections.Enabled = !bBusy; 122 | checkMultiSelections.Enabled = !bBusy; 123 | } 124 | 125 | private void CommandEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) 126 | { 127 | if (ID == commandEventIdSave || ID == commandEventIdSaveAll) 128 | { 129 | if (Settings.Default.CheckSavedFilesHasValue && Settings.Default.CheckSavedFiles == true) 130 | { 131 | // Stop running analysis to prevent a save dialog pop-up 132 | stopAnalysis(); 133 | } 134 | } 135 | } 136 | 137 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 138 | { 139 | // Switches to the UI thread in order to consume some services used in command initialization 140 | await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 141 | 142 | Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); 143 | 144 | _dte = await GetServiceAsync(typeof(DTE)) as DTE; 145 | Assumes.Present(_dte); 146 | if (_dte == null) 147 | return; 148 | 149 | _eventsHandlers = _dte.Events.DocumentEvents; 150 | _eventsHandlers.DocumentSaved += documentSavedSync; 151 | 152 | _commandEventsHandlers = _dte.Events.CommandEvents; 153 | _commandEventsHandlers.BeforeExecute += new _dispCommandEvents_BeforeExecuteEventHandler(CommandEvents_BeforeExecute); 154 | 155 | var outputWindow = (OutputWindow)_dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; 156 | _outputPane = outputWindow.OutputWindowPanes.Add("cppcheck analysis output"); 157 | 158 | AnalyzerCppcheck cppcheckAnalayzer = new AnalyzerCppcheck(); 159 | cppcheckAnalayzer.ProgressUpdated += checkProgressUpdated; 160 | _analyzers.Add(cppcheckAnalayzer); 161 | 162 | if (string.IsNullOrEmpty(Settings.Default.DefaultArguments)) 163 | Settings.Default.DefaultArguments = CppcheckSettings.DefaultArguments; 164 | 165 | // Add our command handlers for menu (commands must exist in the .vsct file) 166 | OleMenuCommandService mcs = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 167 | if (mcs == null) 168 | { 169 | Debug.Assert(mcs != null); 170 | return; 171 | } 172 | 173 | 174 | // Create the command for the menu item. 175 | { 176 | CommandID menuCommandID = new CommandID(GuidList.guidCPPCheckPluginCmdSet, (int)PkgCmdIDList.cmdidCheckProjectCppcheck); 177 | menuCheckCurrentProject = new MenuCommand(onCheckCurrentProjectRequested, menuCommandID); 178 | mcs.AddCommand(menuCheckCurrentProject); 179 | } 180 | 181 | { 182 | // Create the command for the settings window 183 | CommandID settingsWndCmdId = new CommandID(GuidList.guidCPPCheckPluginCmdSet, (int)PkgCmdIDList.cmdidSettings); 184 | menuShowSettingsWindow = new MenuCommand(onSettingsWindowRequested, settingsWndCmdId); 185 | mcs.AddCommand(menuShowSettingsWindow); 186 | } 187 | 188 | { 189 | CommandID stopCheckMenuCommandID = new CommandID(GuidList.guidCPPCheckPluginCmdSet, (int)PkgCmdIDList.cmdidStopCppcheck); 190 | menuCancelCheck = new MenuCommand(onStopCheckRequested, stopCheckMenuCommandID); 191 | mcs.AddCommand(menuCancelCheck); 192 | } 193 | 194 | { 195 | CommandID selectionsMenuCommandID = new CommandID(GuidList.guidCPPCheckPluginCmdSet, (int)PkgCmdIDList.cmdidCheckMultiItemCppcheck); 196 | menuCheckSelections = new MenuCommand(onCheckSelectedProjects, selectionsMenuCommandID); 197 | mcs.AddCommand(menuCheckSelections); 198 | } 199 | 200 | { 201 | CommandID projectMenuCommandID = new CommandID(GuidList.guidCPPCheckPluginProjectCmdSet, (int)PkgCmdIDList.cmdidCheckProjectCppcheck1); 202 | menuCheckCurrentProjectContext = new MenuCommand(onCheckCurrentProjectRequested, projectMenuCommandID); 203 | mcs.AddCommand(menuCheckCurrentProjectContext); 204 | } 205 | 206 | { 207 | CommandID projectsMenuCommandID = new CommandID(GuidList.guidCPPCheckPluginMultiProjectCmdSet, (int)PkgCmdIDList.cmdidCheckProjectsCppcheck); 208 | menuCheckCurrentProjectsContext = new MenuCommand(onCheckAllProjectsRequested, projectsMenuCommandID); 209 | mcs.AddCommand(menuCheckCurrentProjectsContext); 210 | } 211 | 212 | { 213 | CommandID selectionsMenuCommandID = new CommandID(GuidList.guidCPPCheckPluginMultiItemProjectCmdSet, (int)PkgCmdIDList.cmdidCheckMultiItemCppcheck1); 214 | checkMultiSelections = new MenuCommand(onCheckSelectedProjects, selectionsMenuCommandID); 215 | mcs.AddCommand(checkMultiSelections); 216 | } 217 | 218 | setMenuState(false); 219 | } 220 | 221 | protected override void Dispose(bool disposing) 222 | { 223 | cleanup(); 224 | base.Dispose(disposing); 225 | } 226 | 227 | protected override int QueryClose(out bool canClose) 228 | { 229 | int result = base.QueryClose(out canClose); 230 | if (canClose) 231 | { 232 | cleanup(); 233 | } 234 | return result; 235 | } 236 | #endregion 237 | 238 | private void cleanup() 239 | { 240 | foreach (var item in _analyzers) 241 | { 242 | item.Dispose(); 243 | } 244 | } 245 | 246 | private void onCheckCurrentProjectRequested(object sender, EventArgs e) 247 | { 248 | JoinableTaskFactory.Run(async () => 249 | { 250 | await checkFirstActiveProjectAsync(); 251 | }); 252 | } 253 | 254 | private void onCheckAllProjectsRequested(object sender, EventArgs e) 255 | { 256 | JoinableTaskFactory.Run(async () => 257 | { 258 | var cppProjects = await findAllCppProjectsAsync(); 259 | Assumes.NotNull(cppProjects); 260 | 261 | if (cppProjects.Any()) 262 | _ = checkProjectsAsync(cppProjects); 263 | else 264 | { 265 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 266 | MessageBox.Show("No C++ projects found in the solution - nothing to check."); 267 | } 268 | }); 269 | } 270 | 271 | private void onCheckSelectedProjects(object sender, EventArgs e) 272 | { 273 | JoinableTaskFactory.Run(async () => 274 | { 275 | var cppProjects = await findSelectedCppProjectsAsync(); 276 | Assumes.NotNull(cppProjects); 277 | 278 | if (cppProjects.Any()) 279 | _ = checkProjectsAsync(cppProjects); 280 | else 281 | { 282 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 283 | MessageBox.Show("No C++ projects selected - nothing to check."); 284 | } 285 | }); 286 | } 287 | 288 | private void onStopCheckRequested(object sender, EventArgs e) 289 | { 290 | stopAnalysis(); 291 | } 292 | 293 | private void onSettingsWindowRequested(object sender, EventArgs e) 294 | { 295 | var settings = new CppcheckSettings(); 296 | settings.ShowDialog(); 297 | } 298 | 299 | private void documentSavedSync(Document document) 300 | { 301 | JoinableTaskFactory.Run(async () => 302 | { 303 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 304 | 305 | if (document == null || document.Language != "C/C++") 306 | return; 307 | 308 | if (Settings.Default.CheckSavedFilesHasValue && Settings.Default.CheckSavedFiles == false) 309 | return; 310 | 311 | if (document.ActiveWindow == null) 312 | { 313 | // We get here when new files are being created and added to the project and 314 | // then trying to obtain document.ProjectItem yields an exception. Will just skip this. 315 | return; 316 | } 317 | try 318 | { 319 | var kind = document.ProjectItem.ContainingProject.Kind; 320 | if (!isVisualCppProjectKind(document.ProjectItem.ContainingProject.Kind)) 321 | { 322 | return; 323 | } 324 | 325 | Configuration currentConfig = null; 326 | try { currentConfig = document.ProjectItem.ConfigurationManager.ActiveConfiguration; } 327 | catch (Exception) { currentConfig = null; } 328 | if (currentConfig == null) 329 | { 330 | MessageBox.Show("Cannot perform check - no valid configuration selected", "Cppcheck error"); 331 | return; 332 | } 333 | 334 | //dynamic project = document.ProjectItem.ContainingProject.Object; 335 | Project project = document.ProjectItem.ContainingProject; 336 | SourceFile sourceForAnalysis = await createSourceFileAsync(document.ProjectItem); 337 | if (sourceForAnalysis == null) 338 | return; 339 | 340 | if (!Settings.Default.CheckSavedFilesHasValue) 341 | { 342 | askCheckSavedFiles(); 343 | 344 | if (!Settings.Default.CheckSavedFiles) 345 | return; 346 | } 347 | 348 | MainToolWindow.Instance.showIfWindowNotCreated(); 349 | MainToolWindow.Instance.ContentsType = ICodeAnalyzer.AnalysisType.DocumentSavedAnalysis; 350 | runSavedFileAnalysis(sourceForAnalysis, currentConfig); 351 | } 352 | catch (Exception ex) 353 | { 354 | if (_outputPane != null) 355 | { 356 | _outputPane.Clear(); 357 | _ = AddTextToOutputWindowAsync("Exception occurred in cppcheck add-in: " + ex.Message); 358 | } 359 | DebugTracer.Trace(ex); 360 | } 361 | }); 362 | } 363 | 364 | public static void askCheckSavedFiles() 365 | { 366 | DialogResult reply = MessageBox.Show("Do you want to start analysis any time a file is saved? It will clear previous analysis results.\nYou can change this behavior in cppcheck settings.", "Cppcheck: start analysis when file is saved?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); 367 | Settings.Default.CheckSavedFiles = (reply == DialogResult.Yes); 368 | Settings.Default.Save(); 369 | } 370 | 371 | private async Task> findSelectedCppProjectsAsync() 372 | { 373 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 374 | 375 | var cppProjects = new List(); 376 | foreach (dynamic project in _dte.ActiveSolutionProjects as Object[]) 377 | { 378 | if (isVisualCppProjectKind(project.Kind)) 379 | cppProjects.Add(project); 380 | } 381 | 382 | //System.Windows.MessageBox.Show("No project selected in Solution Explorer - nothing to check."); 383 | return cppProjects; 384 | } 385 | 386 | private async Task> findAllCppProjectsAsync() 387 | { 388 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 389 | 390 | var cppProjects = new List(); 391 | foreach (Project project in _dte.Solution.Projects) 392 | { 393 | if (isVisualCppProjectKind(project.Kind)) 394 | cppProjects.Add(project); 395 | } 396 | 397 | return cppProjects; 398 | } 399 | 400 | // Looks at the project item. If it's a supported file type it's added to the list, and if it's a filter it keeps digging into it. 401 | private async Task scanProjectItemForSourceFilesAsync(ProjectItem item, SourceFilesWithConfiguration configuredFiles, Configuration configuration, Project project) 402 | { 403 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 404 | 405 | var itemType = await getTypeOfProjectItemAsync(item); 406 | 407 | if (itemType == ProjectItemType.folder) 408 | { 409 | foreach (ProjectItem subItem in item.ProjectItems) 410 | { 411 | await scanProjectItemForSourceFilesAsync(subItem, configuredFiles, configuration, project); 412 | } 413 | } 414 | else if (itemType == ProjectItemType.headerFile || itemType == ProjectItemType.cFile || itemType == ProjectItemType.cppFile) 415 | { 416 | var itemName = item.Name; 417 | 418 | if (item.FileCount == 0) 419 | { 420 | Debug.Fail("isCppFileAsync(item) is true, but item.FileCount is null!"); 421 | return; 422 | } 423 | 424 | var itemFileName = item.FileNames[1]; 425 | if (!configuredFiles.Exists(itemFileName)) 426 | { 427 | // Don't bother rebuilding the entire definition if it already exists. 428 | SourceFile sourceFile = await createSourceFileAsync(item); 429 | if (sourceFile != null) 430 | { 431 | configuredFiles.addOrUpdateFile(sourceFile); 432 | scanProgressUpdated(configuredFiles.Count()); 433 | } 434 | } 435 | } 436 | } 437 | 438 | private async Task getAllSupportedFilesFromProjectAsync(Project project) 439 | { 440 | var sourceFiles = new SourceFilesWithConfiguration(); 441 | sourceFiles.Configuration = await getConfigurationAsync(project); 442 | 443 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 444 | 445 | foreach (ProjectItem item in project.ProjectItems) 446 | { 447 | ProjectItemType itemType = await getTypeOfProjectItemAsync(item); 448 | if (itemType == ProjectItemType.cFile || itemType == ProjectItemType.cppFile || itemType == ProjectItemType.headerFile) 449 | { 450 | 451 | //List projectSourceFileList = await getProjectFilesAsync(project, configuration); 452 | //foreach (SourceFile projectSourceFile in projectSourceFileList) 453 | // addEntry(currentConfiguredFiles, projectSourceFileList, project); 454 | } 455 | } 456 | 457 | return sourceFiles; 458 | } 459 | 460 | private async Task checkFirstActiveProjectAsync() 461 | { 462 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 463 | 464 | 465 | var activeProjects = await findSelectedCppProjectsAsync(); 466 | Assumes.NotNull(activeProjects); 467 | 468 | if (activeProjects.Any()) 469 | _ = checkProjectsAsync(new List { activeProjects.First() }); 470 | else 471 | MessageBox.Show("No project selected in Solution Explorer - nothing to check."); 472 | } 473 | 474 | private async Task getConfigurationAsync(Project project) 475 | { 476 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 477 | 478 | try 479 | { 480 | return project.ConfigurationManager.ActiveConfiguration; 481 | } 482 | catch (Exception) 483 | { 484 | return null; 485 | } 486 | } 487 | 488 | private async Task checkProjectsAsync(List projects) 489 | { 490 | Debug.Assert(projects.Any()); 491 | setMenuState(true); 492 | 493 | List allConfiguredFiles = new List(); 494 | foreach (var project in projects) 495 | { 496 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 497 | 498 | Configuration config = await getConfigurationAsync(project); 499 | if (config == null) 500 | { 501 | MessageBox.Show("No valid configuration in project " + project.Name); 502 | continue; 503 | } 504 | 505 | SourceFilesWithConfiguration sourceFiles = new SourceFilesWithConfiguration(); 506 | sourceFiles.Configuration = config; 507 | 508 | foreach (ProjectItem projectItem in project.ProjectItems) 509 | { 510 | Stopwatch sw = Stopwatch.StartNew(); 511 | await scanProjectItemForSourceFilesAsync(projectItem, sourceFiles, config, project); 512 | sw.Stop(); 513 | await AddTextToOutputWindowAsync("scanProjectItemForSourceFilesAsync for " + projectItem.Name + " took " + sw.ElapsedMilliseconds + " ms\n"); 514 | } 515 | 516 | // Although we're using the same base configuration, it's possible for each file to override that. 517 | // Group files into separate configs based on actual parameters. We'll be iterating in reverse order so 518 | // reverse the list first to keep the final order the same. 519 | List allSourceFiles = sourceFiles.Files.ToList(); 520 | allSourceFiles.Reverse(); 521 | 522 | while (allSourceFiles.Any()) 523 | { 524 | SourceFilesWithConfiguration newConfig = new SourceFilesWithConfiguration(); 525 | newConfig.Configuration = config; 526 | 527 | SourceFile templateFile = allSourceFiles.Last(); 528 | newConfig.addOrUpdateFile(templateFile); 529 | allSourceFiles.RemoveAt(allSourceFiles.Count - 1); 530 | 531 | for (int i = allSourceFiles.Count - 1; i >= 0; i--) 532 | { 533 | SourceFile otherFile = allSourceFiles[i]; 534 | 535 | if (otherFile.Macros.All(templateFile.Macros.Contains) && templateFile.Macros.All(otherFile.Macros.Contains) && 536 | otherFile.MacrosToUndefine.All(templateFile.MacrosToUndefine.Contains) && templateFile.MacrosToUndefine.All(otherFile.MacrosToUndefine.Contains) && 537 | otherFile.IncludePaths.All(templateFile.IncludePaths.Contains) && templateFile.IncludePaths.All(otherFile.IncludePaths.Contains) && 538 | otherFile.ProjectName == templateFile.ProjectName 539 | ) 540 | { 541 | newConfig.addOrUpdateFile(otherFile); 542 | allSourceFiles.RemoveAt(i); 543 | } 544 | } 545 | 546 | if (newConfig.Any()) 547 | { 548 | allConfiguredFiles.Add(newConfig); 549 | } 550 | } 551 | } 552 | 553 | _ = JoinableTaskFactory.RunAsync(async () => 554 | { 555 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 556 | 557 | MainToolWindow.Instance.ContentsType = ICodeAnalyzer.AnalysisType.ProjectAnalysis; 558 | MainToolWindow.Instance.showIfWindowNotCreated(); 559 | }); 560 | 561 | scanProgressUpdated(-1); 562 | runAnalysis(allConfiguredFiles, false); 563 | } 564 | 565 | private async Task getTypeOfProjectItemAsync(ProjectItem item) 566 | { 567 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 568 | var itemKind = item.Kind; 569 | var itemName = item.Name; 570 | 571 | switch (itemKind) 572 | { 573 | case VSConstants.ItemTypeGuid.PhysicalFolder_string: 574 | case VSConstants.ItemTypeGuid.VirtualFolder_string: 575 | return ProjectItemType.folder; 576 | 577 | case "{8E7B96A8-E33D-11D0-A6D5-00C04FB67F6A}": 578 | case VSConstants.ItemTypeGuid.PhysicalFile_string: 579 | if (item.ConfigurationManager != null) 580 | { 581 | try 582 | { 583 | VCFile vcFile = item.Object as VCFile; 584 | VCProject vcProject = item.ContainingProject.Object as VCProject; 585 | dynamic vcFileFileConfigurations = vcFile.FileConfigurations; 586 | VCFileConfiguration fileConfig = vcFileFileConfigurations.Item(vcProject.ActiveConfiguration.Name); 587 | 588 | if (!fileConfig.ExcludedFromBuild) 589 | { 590 | if (item.FileCodeModel != null && item.FileCodeModel.Language == CodeModelLanguageConstants.vsCMLanguageVC) 591 | { 592 | switch (vcFile.ItemType) 593 | { 594 | case "ClInclude": 595 | return ProjectItemType.headerFile; 596 | 597 | case "ClCompile": 598 | return ProjectItemType.cppFile; 599 | } 600 | } 601 | } 602 | } 603 | catch (Exception) 604 | { 605 | } 606 | } 607 | 608 | return ProjectItemType.other; 609 | 610 | default: 611 | return ProjectItemType.other; 612 | } 613 | } 614 | 615 | private void runSavedFileAnalysis(SourceFile file, Configuration currentConfig) 616 | { 617 | Debug.Assert(currentConfig != null); 618 | 619 | var configuredFiles = new SourceFilesWithConfiguration(); 620 | configuredFiles.addOrUpdateFile(file); 621 | configuredFiles.Configuration = currentConfig; 622 | 623 | _ = System.Threading.Tasks.Task.Run(async delegate 624 | { 625 | await System.Threading.Tasks.Task.Delay(750); 626 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 627 | runAnalysis(new List { configuredFiles }, true); 628 | }); 629 | } 630 | 631 | public void stopAnalysis() 632 | { 633 | foreach (var analyzer in _analyzers) 634 | { 635 | analyzer.abortThreadIfAny(); 636 | } 637 | } 638 | 639 | private void runAnalysis(List configuredFiles, bool analysisOnSavedFile) 640 | { 641 | completedFileCount = 0; 642 | 643 | foreach (var analyzer in _analyzers) 644 | { 645 | lastAnalyzerTotalFiles = 0; 646 | analyzer.analyze(configuredFiles, analysisOnSavedFile); 647 | } 648 | } 649 | 650 | private static void recursiveAddToolDetails(SourceFile sourceForAnalysis, VCConfiguration projectConfig, VCCLCompilerTool tool, dynamic propertySheets, ref bool bInheritDefs, ref bool bInheritUndefs) 651 | { 652 | // TODO: If the special keyword "\\\"$(INHERIT)\\\"" appears, we should inherit at that specific point. 653 | if (bInheritDefs) 654 | { 655 | string[] macrosToDefine = tool.PreprocessorDefinitions.Split(';'); 656 | bInheritDefs = !macrosToDefine.Contains("\\\"$(NOINHERIT)\\\""); 657 | for (int i = 0; i < macrosToDefine.Length; ++i) 658 | { 659 | macrosToDefine[i] = Environment.ExpandEnvironmentVariables(projectConfig.Evaluate(macrosToDefine[i])); 660 | } 661 | 662 | sourceForAnalysis.addMacros(macrosToDefine); 663 | } 664 | 665 | if (bInheritUndefs) 666 | { 667 | string[] macrosToUndefine = tool.UndefinePreprocessorDefinitions.Split(';'); 668 | bInheritUndefs = !macrosToUndefine.Contains("\\\"$(NOINHERIT)\\\""); 669 | for (int i = 0; i < macrosToUndefine.Length; ++i) 670 | { 671 | macrosToUndefine[i] = Environment.ExpandEnvironmentVariables(projectConfig.Evaluate(macrosToUndefine[i])); 672 | } 673 | 674 | sourceForAnalysis.addMacrosToUndefine(macrosToUndefine); 675 | } 676 | 677 | if (propertySheets != null && (bInheritDefs || bInheritUndefs)) 678 | { 679 | // Scan any inherited property sheets. 680 | foreach (var propSheet in propertySheets) 681 | { 682 | VCCLCompilerTool propSheetTool = (VCCLCompilerTool)propSheet.Tools.Item("VCCLCompilerTool"); 683 | if (propSheetTool != null) 684 | { 685 | // When looping over the inherited property sheets, don't allow rules to filter back up the way. 686 | bool bInheritDefs1 = bInheritDefs, bInheritUndefs1 = bInheritUndefs; 687 | recursiveAddToolDetails(sourceForAnalysis, projectConfig, propSheetTool, propSheet.PropertySheets, ref bInheritDefs1, ref bInheritUndefs1); 688 | } 689 | } 690 | } 691 | } 692 | 693 | private static void recursiveAddToolDetails(SourceFile sourceForAnalysis, VCConfiguration projectConfig, VCNMakeTool tool, dynamic propertySheets, ref bool bInheritDefs, ref bool bInheritUndefs) 694 | { 695 | // TODO: If the special keyword "\\\"$(INHERIT)\\\"" appears, we should inherit at that specific point. 696 | if (bInheritDefs) 697 | { 698 | string[] macrosToDefine = tool.PreprocessorDefinitions.Split(';'); 699 | bInheritDefs = !macrosToDefine.Contains("\\\"$(NOINHERIT)\\\""); 700 | for (int i = 0; i < macrosToDefine.Length; ++i) 701 | { 702 | macrosToDefine[i] = Environment.ExpandEnvironmentVariables(projectConfig.Evaluate(macrosToDefine[i])); 703 | } 704 | 705 | sourceForAnalysis.addMacros(macrosToDefine); 706 | } 707 | 708 | if (propertySheets != null && (bInheritDefs || bInheritUndefs)) 709 | { 710 | // Scan any inherited property sheets. 711 | foreach (var propSheet in propertySheets) 712 | { 713 | VCNMakeTool propSheetTool = (VCNMakeTool)propSheet.Tools.Item("VCNMakeTool"); 714 | if (propSheetTool != null) 715 | { 716 | // When looping over the inherited property sheets, don't allow rules to filter back up the way. 717 | bool bInheritDefs1 = bInheritDefs, bInheritUndefs1 = bInheritUndefs; 718 | recursiveAddToolDetails(sourceForAnalysis, projectConfig, propSheetTool, propSheet.PropertySheets, ref bInheritDefs1, ref bInheritUndefs1); 719 | } 720 | } 721 | } 722 | } 723 | 724 | private static async Task createSourceFileAsync(ProjectItem item) 725 | { 726 | try 727 | { 728 | await Instance.JoinableTaskFactory.SwitchToMainThreadAsync(); 729 | 730 | Debug.Assert(isVisualCppProjectKind(item.ContainingProject.Kind)); 731 | 732 | VCFile vcFile = item.Object as VCFile; 733 | VCProject vcProject = item.ContainingProject.Object as VCProject; 734 | VCConfiguration vcconfig = vcProject.ActiveConfiguration; 735 | dynamic vcFileFileConfigurations = vcFile.FileConfigurations; 736 | VCFileConfiguration fileConfig = vcFileFileConfigurations.Item(vcconfig.Name); 737 | 738 | string toolSetName = ((dynamic)vcconfig).PlatformToolsetFriendlyName; 739 | 740 | string projectDirectory = vcProject.ProjectDirectory; 741 | string projectName = vcProject.Name; 742 | SourceFile sourceForAnalysis = null; 743 | 744 | if (item.FileCount > 0) 745 | { 746 | bool bInheritDefs = true, bInheritUndefs = true; 747 | string[] includePaths = { }; 748 | 749 | // Possible exception thrown for nmake based project 750 | try 751 | { 752 | // Do the file-level first in case it disables inheritance. Include files don't have file-level config. 753 | if (implementsInterface(fileConfig.Tool, "Microsoft.VisualStudio.VCProjectEngine.VCCLCompilerTool")) 754 | { 755 | VCCLCompilerTool vcTool = (VCCLCompilerTool)fileConfig.Tool; 756 | sourceForAnalysis = new SourceFile(item.FileNames[1], projectDirectory, projectName, toolSetName); 757 | includePaths = vcTool.FullIncludePath.Split(';'); 758 | string macros = vcTool.PreprocessorDefinitions; 759 | // Other details may be gathered from the file, project or any inherited property sheets. 760 | recursiveAddToolDetails(sourceForAnalysis, vcconfig, vcTool, null, ref bInheritDefs, ref bInheritUndefs); 761 | } 762 | } 763 | catch (Exception ex) 764 | { 765 | DebugTracer.Trace(ex); 766 | } 767 | 768 | // Now get the full include path 769 | dynamic vcconfigTools = vcconfig.Tools; 770 | VCCLCompilerTool projectTool = (VCCLCompilerTool)vcconfigTools.Item("VCCLCompilerTool"); 771 | if (projectTool != null && implementsInterface(projectTool, "Microsoft.VisualStudio.VCProjectEngine.VCCLCompilerTool")) 772 | { 773 | if (sourceForAnalysis == null) 774 | { 775 | sourceForAnalysis = new SourceFile(item.FileNames[1], projectDirectory, projectName, toolSetName); 776 | includePaths = projectTool.FullIncludePath.Split(';'); 777 | } 778 | 779 | // Take the full include path from file level, which is already fully resolved. 780 | for (int i = 0; i < includePaths.Length; ++i) 781 | { 782 | includePaths[i] = Environment.ExpandEnvironmentVariables(vcconfig.Evaluate(includePaths[i])); 783 | } 784 | sourceForAnalysis.addIncludePaths(includePaths); 785 | 786 | recursiveAddToolDetails(sourceForAnalysis, vcconfig, projectTool, vcconfig.PropertySheets, ref bInheritDefs, ref bInheritUndefs); 787 | } 788 | 789 | VCNMakeTool nmakeTool = (VCNMakeTool)vcconfigTools.Item("VCNMakeTool"); 790 | if (null != nmakeTool && implementsInterface(nmakeTool, "Microsoft.VisualStudio.VCProjectEngine.VCNMakeTool")) 791 | { 792 | if (sourceForAnalysis == null) 793 | { 794 | sourceForAnalysis = new SourceFile(item.FileNames[1], projectDirectory, projectName, toolSetName); 795 | includePaths = nmakeTool.IncludeSearchPath.Split(';'); 796 | } 797 | 798 | // Take the full include path from file level, which is already fully resolved. 799 | for (int i = 0; i < includePaths.Length; ++i) 800 | { 801 | includePaths[i] = Environment.ExpandEnvironmentVariables(vcconfig.Evaluate(includePaths[i])); 802 | } 803 | sourceForAnalysis.addIncludePaths(includePaths); 804 | 805 | recursiveAddToolDetails(sourceForAnalysis, vcconfig, nmakeTool, vcconfig.PropertySheets, ref bInheritDefs, ref bInheritUndefs); 806 | } 807 | } 808 | 809 | return sourceForAnalysis; 810 | } 811 | catch (Exception ex) 812 | { 813 | DebugTracer.Trace(ex); 814 | return null; 815 | } 816 | } 817 | 818 | private static bool isVisualCppProjectKind(string kind) 819 | { 820 | return kind.Equals(VSConstants.UICONTEXT.VCProject_string); 821 | } 822 | 823 | private static bool implementsInterface(object objectToCheck, string interfaceName) 824 | { 825 | Type objectType = objectToCheck.GetType(); 826 | var requestedInterface = objectType.GetInterface(interfaceName); 827 | return requestedInterface != null; 828 | } 829 | 830 | private async void scanProgressUpdated(int filesScanned) 831 | { 832 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 833 | 834 | EnvDTE.StatusBar statusBar = _dte.StatusBar; 835 | if (statusBar == null) 836 | return; 837 | 838 | try 839 | { 840 | if (filesScanned >= 0) 841 | { 842 | string label = "cppcheck scanning for files (" + filesScanned + ")"; 843 | statusBar.Text = label; 844 | } 845 | else 846 | { 847 | statusBar.Clear(); 848 | } 849 | } 850 | catch (Exception) {} 851 | } 852 | 853 | private async void updateStatusBarProgress(bool inProgress, string label, int currentPercentage) 854 | { 855 | try 856 | { 857 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 858 | _dte.StatusBar.Progress(inProgress, label, currentPercentage, 100); 859 | } 860 | catch (Exception) {} 861 | } 862 | 863 | private async void checkProgressUpdated(object sender, ICodeAnalyzer.ProgressEvenArgs e) 864 | { 865 | int progress = e.Progress; 866 | if (progress == 0) 867 | progress = 1; // statusBar.Progress won't display a progress bar with 0% 868 | 869 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 870 | 871 | string label = ""; 872 | if (progress < 100) 873 | { 874 | if (e.FilesChecked == 0 || e.TotalFilesNumber == 0) 875 | label = "cppcheck analysis in progress..."; 876 | else 877 | label = "cppcheck analysis in progress (" + (completedFileCount + e.FilesChecked) + " out of " + (completedFileCount + e.TotalFilesNumber) + " files checked)"; 878 | 879 | lastAnalyzerTotalFiles = e.TotalFilesNumber; 880 | 881 | updateStatusBarProgress(true, label, progress); 882 | } 883 | else 884 | { 885 | label = "cppcheck analysis completed"; 886 | completedFileCount += lastAnalyzerTotalFiles; 887 | lastAnalyzerTotalFiles = 0; 888 | 889 | updateStatusBarProgress(true, label, Math.Max(progress, 100)); 890 | 891 | _ = System.Threading.Tasks.Task.Run(async delegate 892 | { 893 | await System.Threading.Tasks.Task.Delay(5000); 894 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 895 | updateStatusBarProgress(false, label, 100); 896 | }); 897 | 898 | setMenuState(false); 899 | } 900 | } 901 | 902 | private static void CreateDefaultGlobalSuppressions() 903 | { 904 | string globalsuppressionsFilePath = ICodeAnalyzer.suppressionsFilePathByStorage(ICodeAnalyzer.SuppressionStorage.Global); 905 | if (!File.Exists(globalsuppressionsFilePath)) 906 | { 907 | SuppressionsInfo suppressionsInfo = new SuppressionsInfo(); 908 | suppressionsInfo.SkippedIncludesMask.Add(".*Microsoft Visual Studio.*"); 909 | suppressionsInfo.SkippedIncludesMask.Add(".*Microsoft SDKs.*"); 910 | suppressionsInfo.SkippedIncludesMask.Add(".*Windows Kits.*"); 911 | suppressionsInfo.SkippedIncludesMask.Add(".*boost.*"); 912 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\ActiveQt.*"); 913 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\Qt$"); 914 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtCore.*"); 915 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtDeclarative.*"); 916 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtGui.*"); 917 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtMultimedia.*"); 918 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtNetwork.*"); 919 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtOpenGL.*"); 920 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtOpenVG.*"); 921 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtScript.*"); 922 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtScriptTools.*"); 923 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtSql.*"); 924 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtSvg.*"); 925 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtTest.*"); 926 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtWebKit.*"); 927 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtXml.*"); 928 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtXmlPatterns.*"); 929 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtConcurrent.*"); 930 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtMultimediaWidgets.*"); 931 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtOpenGLExtensions.*"); 932 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtQml.*"); 933 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtQuick.*"); 934 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtSensors.*"); 935 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtWebKitWidgets.*"); 936 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtWidgets.*"); 937 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtZlib.*"); 938 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\include\\QtV8.*"); 939 | suppressionsInfo.SkippedIncludesMask.Add(@".*\\mkspecs\\win32-.*"); 940 | 941 | suppressionsInfo.SkippedFilesMask.Add("^moc_.*\\.cpp$"); 942 | suppressionsInfo.SkippedFilesMask.Add("^qrc_.*\\.cpp$"); 943 | suppressionsInfo.SkippedFilesMask.Add("^ui_.*\\.h$"); 944 | 945 | suppressionsInfo.SaveToFile(globalsuppressionsFilePath); 946 | } 947 | } 948 | 949 | enum ProjectItemType { cppFile, cFile, headerFile, folder, other }; 950 | 951 | private static DTE _dte = null; 952 | private DocumentEvents _eventsHandlers = null; 953 | private CommandEvents _commandEventsHandlers = null; 954 | private List _analyzers = new List(); 955 | 956 | private OutputWindowPane _outputPane = null; 957 | 958 | private const int commandEventIdSave = 331; 959 | private const int commandEventIdSaveAll = 224; 960 | 961 | private static CPPCheckPluginPackage _instance = null; 962 | } 963 | } 964 | -------------------------------------------------------------------------------- /CPPCheckPlugin/ChecksPanel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Windows.Controls; 5 | using System.Xml; 6 | 7 | namespace VSPackage.CPPCheckPlugin 8 | { 9 | class ChecksPanel 10 | { 11 | private StackPanel mPanel; 12 | 13 | // copypasted from cppcheck documentation 14 | private Dictionary SeverityToolTips = new Dictionary() 15 | { 16 | {"error", "Programming error.\nThis indicates severe error like memory leak etc.\nThe error is certain."}, 17 | {"warning", "Used for dangerous coding style that can cause severe runtime errors.\nFor example: forgetting to initialize a member variable in a constructor."}, 18 | {"style", "Style warning.\nUsed for general code cleanup recommendations. Fixing these will not fix any bugs but will make the code easier to maintain.\nFor example: redundant code, unreachable code, etc."}, 19 | {"performance", "Performance warning.\nNot an error as is but suboptimal code and fixing it probably leads to faster performance of the compiled code."}, 20 | {"portability", "Portability warning.\nThis warning indicates the code is not properly portable for different platforms and bitnesses (32/64 bit). If the code is meant to compile in different platforms and bitnesses these warnings should be fixed."}, 21 | {"information", "Checking information.\nInformation message about the checking (process) itself. These messages inform about header files not found etc issues that are not errors in the code but something user needs to know."}, 22 | {"debug", "Debug message.\nDebug-mode message useful for the developers."} 23 | }; 24 | 25 | class CheckInfo 26 | { 27 | public string id; 28 | public string label; 29 | public string toolTip; 30 | public CheckBox box; 31 | }; 32 | class SeverityInfo 33 | { 34 | public string id; 35 | public string toolTip; 36 | public List checks = new List(); 37 | 38 | public CheckBox box; 39 | public ScrollViewer scrollView; 40 | }; 41 | Dictionary mChecks = new Dictionary(); 42 | 43 | public ChecksPanel(StackPanel panel) 44 | { 45 | mPanel = panel; 46 | 47 | BuildChecksList(); 48 | GenerateControls(); 49 | LoadSettings(); 50 | } 51 | 52 | public void LoadSettings() 53 | { 54 | var enabledSeverities = Properties.Settings.Default.SeveritiesString.Split(','); 55 | HashSet suppressions = new HashSet(Properties.Settings.Default.SuppressionsString.Split(',')); 56 | 57 | foreach (var severity in mChecks) 58 | { 59 | severity.Value.scrollView.IsEnabled = false; 60 | foreach (CheckInfo check in severity.Value.checks) 61 | { 62 | check.box.IsChecked = suppressions.Contains(check.id) == false; 63 | } 64 | } 65 | 66 | mChecks["error"].box.IsChecked = true; 67 | mChecks["error"].box.IsEnabled = false; 68 | mChecks["error"].box.Content = "error (can't be disabled)"; 69 | mChecks["error"].scrollView.IsEnabled = true; 70 | foreach (var severity in enabledSeverities) 71 | { 72 | if (mChecks.ContainsKey(severity)) 73 | { 74 | mChecks[severity].box.IsChecked = true; 75 | mChecks[severity].scrollView.IsEnabled = true; 76 | } 77 | } 78 | } 79 | 80 | private string GetSeveritiesString() 81 | { 82 | string result = ""; 83 | foreach (var severity in mChecks) 84 | { 85 | if (severity.Key != "error" && severity.Value.box.IsChecked == true) 86 | { 87 | if (result.Length != 0) 88 | result += ","; 89 | result += severity.Value.id; 90 | } 91 | } 92 | return result; 93 | } 94 | 95 | private string GetSuppressionsString() 96 | { 97 | string result = ""; 98 | foreach (var severity in mChecks) 99 | { 100 | foreach (CheckInfo check in severity.Value.checks) 101 | { 102 | if (check.box.IsChecked == false) 103 | { 104 | if (result.Length != 0) 105 | result += ","; 106 | result += check.id; 107 | } 108 | } 109 | } 110 | return result; 111 | } 112 | 113 | private void BuildChecksList() 114 | { 115 | var checksList = LoadChecksList(); 116 | 117 | foreach (XmlNode node in checksList.SelectNodes("//errors/error")) 118 | { 119 | string id = node.Attributes["id"].Value; 120 | string severity = node.Attributes["severity"].Value; 121 | string message = node.Attributes["msg"].Value; 122 | string verboseMessage = node.Attributes["verbose"].Value; 123 | 124 | if (!mChecks.ContainsKey(severity)) 125 | mChecks.Add(severity, new SeverityInfo { id = severity, toolTip = SeverityToolTips[severity] }); 126 | 127 | string checkToolTip = FormatTooltip(id, severity, message, verboseMessage); 128 | 129 | mChecks[severity].checks.Add(new CheckInfo { id = id, toolTip = checkToolTip, label = message }); 130 | } 131 | } 132 | 133 | private static string FormatTooltip(string id, string severity, string message, string verboseMessage) 134 | { 135 | string multilineToolTip = ""; 136 | string remainingToolTip = "id : " + id + "\n" + verboseMessage; 137 | while (remainingToolTip.Length > 100) 138 | { 139 | int spaceIdx = remainingToolTip.IndexOf(' ', 100); 140 | if (spaceIdx == -1) 141 | break; 142 | multilineToolTip += remainingToolTip.Substring(0, spaceIdx) + Environment.NewLine; 143 | remainingToolTip = remainingToolTip.Substring(spaceIdx + 1); 144 | } 145 | multilineToolTip += remainingToolTip; 146 | return multilineToolTip; 147 | } 148 | 149 | private XmlDocument LoadChecksList() 150 | { 151 | using (var process = new System.Diagnostics.Process()) 152 | { 153 | var startInfo = process.StartInfo; 154 | startInfo.UseShellExecute = false; 155 | startInfo.CreateNoWindow = true; 156 | startInfo.RedirectStandardOutput = true; 157 | startInfo.FileName = AnalyzerCppcheck.cppcheckExePath(); 158 | startInfo.WorkingDirectory = Path.GetDirectoryName(startInfo.FileName); 159 | startInfo.Arguments = "--errorlist --xml-version=2"; 160 | process.Start(); 161 | String output; 162 | using (var outputStream = process.StandardOutput) 163 | { 164 | output = outputStream.ReadToEnd(); 165 | } 166 | process.WaitForExit(); 167 | 168 | var checksList = new XmlDocument(); 169 | checksList.LoadXml(output); 170 | return checksList; 171 | } 172 | } 173 | 174 | private void GenerateControls() 175 | { 176 | foreach (var severity in mChecks) 177 | { 178 | var severityCheckBox = new CheckBox(); 179 | severity.Value.box = severityCheckBox; 180 | severityCheckBox.Name = severity.Value.id; 181 | severityCheckBox.Content = severity.Value.id; 182 | severityCheckBox.ToolTip = severity.Value.toolTip; 183 | 184 | severityCheckBox.Checked += Severity_Changed; 185 | severityCheckBox.Unchecked += Severity_Changed; 186 | 187 | mPanel.Children.Add(severityCheckBox); 188 | 189 | var scrollView = new ScrollViewer(); 190 | scrollView.Margin = new System.Windows.Thickness(20, 0, 0, 0); 191 | scrollView.MaxHeight = 100; 192 | mPanel.Children.Add(scrollView); 193 | 194 | var subPanel = new StackPanel(); 195 | severity.Value.scrollView = scrollView; 196 | 197 | scrollView.Content = subPanel; 198 | 199 | severity.Value.checks.Sort((check1, check2) => check1.label.CompareTo(check2.label)); 200 | foreach (CheckInfo check in severity.Value.checks) 201 | { 202 | var box = new CheckBox(); 203 | check.box = box; 204 | box.Name = check.id; 205 | box.Content = /*check.id + ":\t" +*/ check.label; 206 | box.ToolTip = check.toolTip; 207 | 208 | box.Checked += Check_Changed; 209 | box.Unchecked += Check_Changed; 210 | 211 | subPanel.Children.Add(box); 212 | } 213 | } 214 | } 215 | 216 | private void Severity_Changed(object sender, System.Windows.RoutedEventArgs e) 217 | { 218 | var box = (CheckBox)sender; 219 | if (mChecks.ContainsKey(box.Name)) 220 | { 221 | mChecks[box.Name].scrollView.IsEnabled = box.IsChecked == true; 222 | } 223 | Properties.Settings.Default.SeveritiesString = GetSeveritiesString(); 224 | Properties.Settings.Default.Save(); 225 | } 226 | 227 | private void Check_Changed(object sender, System.Windows.RoutedEventArgs e) 228 | { 229 | Properties.Settings.Default.SuppressionsString = GetSuppressionsString(); 230 | Properties.Settings.Default.Save(); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /CPPCheckPlugin/CppcheckMessagesList.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | -------------------------------------------------------------------------------- /CPPCheckPlugin/CppcheckMessagesList.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace VSPackage.CPPCheckPlugin 4 | { 5 | /// 6 | /// Interaction logic for CppcheckMessagesList.xaml 7 | /// 8 | public partial class CppcheckMessagesList : Window 9 | { 10 | public CppcheckMessagesList() 11 | { 12 | InitializeComponent(); 13 | 14 | var panel = new ChecksPanel(Checks_Panel); 15 | panel.LoadSettings(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CPPCheckPlugin/CppcheckSettings.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |