1- using System ;
1+ using System ;
22using System . Diagnostics ;
33using System . Globalization ;
44using System . Runtime . InteropServices ;
@@ -34,6 +34,8 @@ namespace VSPackage.CPPCheckPlugin
3434 [ Guid ( GuidList . guidCPPCheckPluginPkgString ) ]
3535 public sealed class CPPCheckPluginPackage : AsyncPackage
3636 {
37+ private int completedFileCount = 0 , lastAnalyzerTotalFiles = 0 ;
38+
3739 public CPPCheckPluginPackage ( )
3840 {
3941 _instance = this ;
@@ -328,7 +330,7 @@ private void documentSavedSync(Document document)
328330
329331 //dynamic project = document.ProjectItem.ContainingProject.Object;
330332 Project project = document . ProjectItem . ContainingProject ;
331- SourceFile sourceForAnalysis = await createSourceFileAsync ( document . FullName , currentConfig , project ) ;
333+ SourceFile sourceForAnalysis = await createSourceFileAsync ( document . ProjectItem ) ;
332334 if ( sourceForAnalysis == null )
333335 return ;
334336
@@ -397,12 +399,10 @@ private async Task scanProjectItemForSourceFilesAsync(ProjectItem item, SourceFi
397399 {
398400 await JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
399401
400- Debug . WriteLine ( "Scanning project item \" " + item . Name + "\" ..." ) ;
401402 var itemType = await getTypeOfProjectItemAsync ( item ) ;
402403
403404 if ( itemType == ProjectItemType . folder )
404405 {
405- Debug . WriteLine ( "It's a folder, enumerating it." ) ;
406406 foreach ( ProjectItem subItem in item . ProjectItems )
407407 {
408408 await scanProjectItemForSourceFilesAsync ( subItem , configuredFiles , configuration , project ) ;
@@ -422,13 +422,14 @@ private async Task scanProjectItemForSourceFilesAsync(ProjectItem item, SourceFi
422422 if ( ! configuredFiles . Exists ( itemFileName ) )
423423 {
424424 // Don't bother rebuilding the entire definition if it already exists.
425- SourceFile sourceFile = await createSourceFileAsync ( itemFileName , configuration , project ) ;
426- configuredFiles . addOrUpdateFile ( sourceFile ) ;
427- scanProgressUpdated ( configuredFiles . Count ( ) ) ;
425+ SourceFile sourceFile = await createSourceFileAsync ( item ) ;
426+ if ( sourceFile != null )
427+ {
428+ configuredFiles . addOrUpdateFile ( sourceFile ) ;
429+ scanProgressUpdated ( configuredFiles . Count ( ) ) ;
430+ }
428431 }
429432 }
430- else
431- Debug . WriteLine ( "It's something else, skipping it." ) ;
432433 }
433434
434435 private async Task < SourceFilesWithConfiguration > getAllSupportedFilesFromProjectAsync ( Project project )
@@ -500,14 +501,46 @@ private async Task checkProjectsAsync(List<Project> projects)
500501 SourceFilesWithConfiguration sourceFiles = new SourceFilesWithConfiguration ( ) ;
501502 sourceFiles . Configuration = config ;
502503
503- await AddTextToOutputWindowAsync ( "Looking for C++ source files in the project \" " + project . Name + "\" \n " ) ;
504-
505504 foreach ( ProjectItem projectItem in project . ProjectItems )
506505 {
507506 await scanProjectItemForSourceFilesAsync ( projectItem , sourceFiles , config , project ) ;
508507 }
509508
510- allConfiguredFiles . Add ( sourceFiles ) ;
509+ // Although we're using the same base configuration, it's possible for each file to override that.
510+ // Group files into separate configs based on actual parameters. We'll be iterating in reverse order so
511+ // reverse the list first to keep the final order the same.
512+ List < SourceFile > allSourceFiles = sourceFiles . Files . ToList ( ) ;
513+ allSourceFiles . Reverse ( ) ;
514+
515+ while ( allSourceFiles . Any ( ) )
516+ {
517+ SourceFilesWithConfiguration newConfig = new SourceFilesWithConfiguration ( ) ;
518+ newConfig . Configuration = config ;
519+
520+ SourceFile templateFile = allSourceFiles . Last ( ) ;
521+ newConfig . addOrUpdateFile ( templateFile ) ;
522+ allSourceFiles . RemoveAt ( allSourceFiles . Count - 1 ) ;
523+
524+ for ( int i = allSourceFiles . Count - 1 ; i >= 0 ; i -- )
525+ {
526+ SourceFile otherFile = allSourceFiles [ i ] ;
527+
528+ if ( otherFile . Macros . All ( templateFile . Macros . Contains ) && templateFile . Macros . All ( otherFile . Macros . Contains ) &&
529+ otherFile . MacrosToUndefine . All ( templateFile . MacrosToUndefine . Contains ) && templateFile . MacrosToUndefine . All ( otherFile . MacrosToUndefine . Contains ) &&
530+ otherFile . IncludePaths . All ( templateFile . IncludePaths . Contains ) && templateFile . IncludePaths . All ( otherFile . IncludePaths . Contains ) &&
531+ otherFile . ProjectName == templateFile . ProjectName
532+ )
533+ {
534+ newConfig . addOrUpdateFile ( otherFile ) ;
535+ allSourceFiles . RemoveAt ( i ) ;
536+ }
537+ }
538+
539+ if ( newConfig . Any ( ) )
540+ {
541+ allConfiguredFiles . Add ( newConfig ) ;
542+ }
543+ }
511544 }
512545
513546 _ = JoinableTaskFactory . RunAsync ( async ( ) => {
@@ -535,14 +568,32 @@ private async Task<ProjectItemType> getTypeOfProjectItemAsync(ProjectItem item)
535568
536569 case "{8E7B96A8-E33D-11D0-A6D5-00C04FB67F6A}" :
537570 case VSConstants . ItemTypeGuid . PhysicalFile_string :
538- var codeModel = item . FileCodeModel ;
539- if ( codeModel != null )
571+ if ( item . ConfigurationManager != null )
540572 {
541- switch ( codeModel . Language )
542- {
543- case CodeModelLanguageConstants . vsCMLanguageVC :
544- return ProjectItemType . cppFile ;
573+ try
574+ {
575+ VCFile vcFile = item . Object as VCFile ;
576+ VCProject vcProject = item . ContainingProject . Object as VCProject ;
577+ VCFileConfiguration fileConfig = vcFile . FileConfigurations . Item ( vcProject . ActiveConfiguration . Name ) ;
578+
579+ if ( ! fileConfig . ExcludedFromBuild )
580+ {
581+ if ( item . FileCodeModel != null && item . FileCodeModel . Language == CodeModelLanguageConstants . vsCMLanguageVC )
582+ {
583+ switch ( vcFile . ItemType )
584+ {
585+ case "ClInclude" :
586+ return ProjectItemType . headerFile ;
587+
588+ case "ClCompile" :
589+ return ProjectItemType . cppFile ;
590+ }
591+ }
592+ }
545593 }
594+ catch ( Exception )
595+ {
596+ }
546597 }
547598
548599 return ProjectItemType . other ;
@@ -578,83 +629,110 @@ public void stopAnalysis()
578629
579630 private void runAnalysis ( List < SourceFilesWithConfiguration > configuredFiles , bool analysisOnSavedFile )
580631 {
632+ completedFileCount = 0 ;
633+
581634 foreach ( var analyzer in _analyzers )
582635 {
636+ lastAnalyzerTotalFiles = 0 ;
583637 analyzer . analyze ( configuredFiles , analysisOnSavedFile ) ;
584638 }
585639 }
586640
587- private static void recursiveAddToolDetails ( SourceFile sourceForAnalysis , VCConfiguration vcconfig , dynamic subPropertySheets , VCCLCompilerTool tool )
641+ private static void recursiveAddToolDetails ( SourceFile sourceForAnalysis , VCConfiguration projectConfig , VCCLCompilerTool tool , dynamic propertySheets , ref bool bInheritDefs , ref bool bInheritUndefs )
588642 {
589- string macrosToUndefine = tool . UndefinePreprocessorDefinitions ;
590-
591- string [ ] definitions = tool . PreprocessorDefinitions . Split ( ';' ) ;
592- bool bInheritDefs = definitions . Contains ( "\\ \" $(INHERIT)\\ \" " ) ;
593- for ( int i = 0 ; i < definitions . Length ; ++ i )
643+ // TODO: If the special keyword "\\\"$(INHERIT)\\\"" appears, we should inherit at that specific point.
644+ if ( bInheritDefs )
594645 {
595- definitions [ i ] = Environment . ExpandEnvironmentVariables ( vcconfig . Evaluate ( definitions [ i ] ) ) ;
596- }
646+ string [ ] macrosToDefine = tool . PreprocessorDefinitions . Split ( ';' ) ;
647+ bInheritDefs = ! macrosToDefine . Contains ( "\\ \" $(NOINHERIT)\\ \" " ) ;
648+ for ( int i = 0 ; i < macrosToDefine . Length ; ++ i )
649+ {
650+ macrosToDefine [ i ] = Environment . ExpandEnvironmentVariables ( projectConfig . Evaluate ( macrosToDefine [ i ] ) ) ;
651+ }
597652
598- sourceForAnalysis . addMacros ( definitions ) ;
599- sourceForAnalysis . addMacrosToUndefine ( macrosToUndefine . Split ( ';' ) ) ;
653+ sourceForAnalysis . addMacros ( macrosToDefine ) ;
654+ }
600655
601- if ( bInheritDefs )
656+ if ( bInheritUndefs )
602657 {
603- foreach ( var propSheet in subPropertySheets )
658+ string [ ] macrosToUndefine = tool . UndefinePreprocessorDefinitions . Split ( ';' ) ;
659+ bInheritUndefs = ! macrosToUndefine . Contains ( "\\ \" $(NOINHERIT)\\ \" " ) ;
660+ for ( int i = 0 ; i < macrosToUndefine . Length ; ++ i )
604661 {
605- var test = propSheet . Name ;
662+ macrosToUndefine [ i ] = Environment . ExpandEnvironmentVariables ( projectConfig . Evaluate ( macrosToUndefine [ i ] ) ) ;
663+ }
664+
665+ sourceForAnalysis . addMacrosToUndefine ( macrosToUndefine ) ;
666+ }
606667
607- dynamic toolsCollection = propSheet . Tools ;
608- foreach ( var subTool in toolsCollection )
668+ if ( propertySheets != null && ( bInheritDefs || bInheritUndefs ) )
669+ {
670+ // Scan any inherited property sheets.
671+ foreach ( var propSheet in propertySheets )
672+ {
673+ VCCLCompilerTool propSheetTool = ( VCCLCompilerTool ) propSheet . Tools . Item ( "VCCLCompilerTool" ) ;
674+ if ( propSheetTool != null )
609675 {
610- // Project-specific includes
611- if ( implementsInterface ( subTool , "Microsoft.VisualStudio.VCProjectEngine.VCCLCompilerTool" ) )
612- {
613- recursiveAddToolDetails ( sourceForAnalysis , vcconfig , propSheet . PropertySheets , subTool ) ;
614- }
676+ // When looping over the inherited property sheets, don't allow rules to filter back up the way.
677+ bool bInheritDefs1 = bInheritDefs , bInheritUndefs1 = bInheritUndefs ;
678+ recursiveAddToolDetails ( sourceForAnalysis , projectConfig , propSheetTool , propSheet . PropertySheets , ref bInheritDefs1 , ref bInheritUndefs1 ) ;
615679 }
616680 }
617681 }
618682 }
619683
620- private static async Task < SourceFile > createSourceFileAsync ( string filePath , Configuration configuration , Project project )
684+ private static async Task < SourceFile > createSourceFileAsync ( ProjectItem item )
621685 {
622686 try
623687 {
624688 await Instance . JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
625689
626- Debug . Assert ( isVisualCppProjectKind ( project . Kind ) ) ;
627-
628- VCProject vcProject = project . Object as VCProject ;
629- VCConfiguration vcconfig = vcProject . ActiveConfiguration ;
690+ Debug . Assert ( isVisualCppProjectKind ( item . ContainingProject . Kind ) ) ;
630691
631- String configurationName = configuration . ConfigurationName ;
632- dynamic config = ( ( dynamic ) project . Object ) . Configurations . Item ( configurationName ) ;
692+ VCFile vcFile = item . Object as VCFile ;
693+ VCProject vcProject = item . ContainingProject . Object as VCProject ;
694+ VCConfiguration vcconfig = vcProject . ActiveConfiguration ;
695+ VCFileConfiguration fileConfig = vcFile . FileConfigurations . Item ( vcconfig . Name ) ;
633696
634- // TODO: This no longer works, why?
635- // string toolSetName = ((dynamic)vcconfig).PlatformToolsetFriendlyName;
636- string toolSetName = null ;
697+ string toolSetName = ( ( dynamic ) vcconfig ) . PlatformToolsetFriendlyName ;
637698
638699 string projectDirectory = vcProject . ProjectDirectory ;
639- string projectName = project . Name ;
700+ string projectName = vcProject . Name ;
640701 SourceFile sourceForAnalysis = null ;
641- dynamic toolsCollection = vcconfig . Tools ;
642- foreach ( var tool in toolsCollection )
702+
703+ if ( item . FileCount > 0 )
643704 {
644- // Project-specific includes
645- if ( implementsInterface ( tool , "Microsoft.VisualStudio.VCProjectEngine.VCCLCompilerTool" ) )
705+ bool bInheritDefs = true , bInheritUndefs = true ;
706+ string [ ] includePaths = { } ;
707+
708+ // Do the file-level first in case it disables inheritance. Include files don't have file-level config.
709+ if ( implementsInterface ( fileConfig . Tool , "Microsoft.VisualStudio.VCProjectEngine.VCCLCompilerTool" ) )
710+ {
711+ sourceForAnalysis = new SourceFile ( item . FileNames [ 1 ] , projectDirectory , projectName , toolSetName ) ;
712+ includePaths = fileConfig . Tool . FullIncludePath . Split ( ';' ) ;
713+
714+ // Other details may be gathered from the file, project or any inherited property sheets.
715+ recursiveAddToolDetails ( sourceForAnalysis , vcconfig , fileConfig . Tool , null , ref bInheritDefs , ref bInheritUndefs ) ;
716+ }
717+
718+ // Now get the full include path
719+ VCCLCompilerTool projectTool = ( VCCLCompilerTool ) vcconfig . Tools . Item ( "VCCLCompilerTool" ) ;
720+ if ( projectTool != null && implementsInterface ( projectTool , "Microsoft.VisualStudio.VCProjectEngine.VCCLCompilerTool" ) )
646721 {
647- sourceForAnalysis = new SourceFile ( filePath , projectDirectory , projectName , toolSetName ) ;
722+ if ( sourceForAnalysis == null )
723+ {
724+ sourceForAnalysis = new SourceFile ( item . FileNames [ 1 ] , projectDirectory , projectName , toolSetName ) ;
725+ includePaths = projectTool . FullIncludePath . Split ( ';' ) ;
726+ }
648727
649- string includes = tool . FullIncludePath ;
650- string [ ] includePaths = includes . Split ( ';' ) ;
728+ // Take the full include path from file level, which is already fully resolved.
651729 for ( int i = 0 ; i < includePaths . Length ; ++ i )
652730 {
653731 includePaths [ i ] = Environment . ExpandEnvironmentVariables ( vcconfig . Evaluate ( includePaths [ i ] ) ) ;
654732 }
655733 sourceForAnalysis . addIncludePaths ( includePaths ) ;
656734
657- recursiveAddToolDetails ( sourceForAnalysis , vcconfig , vcconfig . PropertySheets , tool ) ;
735+ recursiveAddToolDetails ( sourceForAnalysis , vcconfig , projectTool , vcconfig . PropertySheets , ref bInheritDefs , ref bInheritUndefs ) ;
658736 }
659737 }
660738
@@ -720,15 +798,24 @@ private async void checkProgressUpdated(object sender, ICodeAnalyzer.ProgressEve
720798 if ( e . FilesChecked == 0 || e . TotalFilesNumber == 0 )
721799 label = "cppcheck analysis in progress..." ;
722800 else
723- label = "cppcheck analysis in progress (" + e . FilesChecked + " out of " + e . TotalFilesNumber + " files checked)" ;
801+ label = "cppcheck analysis in progress (" + ( completedFileCount + e . FilesChecked ) + " out of " + ( completedFileCount + e . TotalFilesNumber ) + " files checked)" ;
802+
803+ lastAnalyzerTotalFiles = e . TotalFilesNumber ;
724804
725805 statusBar . Progress ( true , label , progress , 100 ) ;
726806 }
727807 else
728808 {
729809 label = "cppcheck analysis completed" ;
810+ completedFileCount += lastAnalyzerTotalFiles ;
811+ lastAnalyzerTotalFiles = 0 ;
730812
731- statusBar . Progress ( true , label , progress , 100 ) ;
813+ try
814+ {
815+ // This raises an exception during shutdown.
816+ statusBar . Progress ( true , label , progress , 100 ) ;
817+ }
818+ catch ( Exception ) { }
732819
733820 _ = System . Threading . Tasks . Task . Run ( async delegate
734821 {
0 commit comments