Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ test/testfunctions.o: test/testfunctions.cpp lib/check.h lib/checkers.h lib/chec
test/testgarbage.o: test/testgarbage.cpp lib/check.h lib/checkers.h lib/checks.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/smallvector.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testgarbage.cpp

test/testimportproject.o: test/testimportproject.cpp externals/tinyxml2/tinyxml2.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h test/fixture.h test/redirect.h
test/testimportproject.o: test/testimportproject.cpp lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/redirect.h
$(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testimportproject.cpp

test/testincompletestatement.o: test/testincompletestatement.cpp lib/check.h lib/checkers.h lib/checkimpl.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
Expand Down
1,054 changes: 566 additions & 488 deletions lib/importproject.cpp

Large diffs are not rendered by default.

20 changes: 7 additions & 13 deletions lib/importproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@

class Settings;
struct Suppressions;
namespace tinyxml2 {
class XMLDocument;
}

/// @addtogroup Core
/// @{
Expand All @@ -56,11 +53,14 @@ namespace cppcheck {
}
}

using VariablesMap = std::map<std::string, std::string, cppcheck::stricmp>;

/**
* @brief Importing project settings.
*/
class CPPCHECKLIB WARN_UNUSED ImportProject {
public:

enum class Type : std::uint8_t {
NONE,
UNKNOWN,
Expand All @@ -76,7 +76,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {

protected:
static void fsSetDefines(FileSettings& fs, std::string defs);
static void fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list<std::string> &in, std::map<std::string, std::string, cppcheck::stricmp> &variables);
static void fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list<std::string> &in, VariablesMap &variables);

public:
std::list<FileSettings> fileSettings;
Expand Down Expand Up @@ -106,28 +106,22 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
void ignoreOtherConfigs(const std::string &cfg);

Type import(const std::string &filename, Settings *settings=nullptr, Suppressions *supprs=nullptr);

protected:
bool importCompileCommands(std::istream &istr);
bool importCppcheckGuiProject(std::istream &istr, Settings &settings, Suppressions &supprs);
static std::string collectArgs(const std::string &cmd, std::vector<std::string> &args);
void setRelativePaths(const std::string &filename);

struct SharedItemsProject {
bool successful = false;
std::string pathToProjectFile;
std::vector<std::string> includePaths;
std::vector<std::string> sourceFiles;
};
VariablesMap mVariables;

bool importVcxproj(const std::string &filename, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
bool importVcxproj(const std::string &filename, const tinyxml2::XMLDocument &doc, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
bool importVcxproj(const std::string &filename, VariablesMap &variables, const std::vector<std::string> &fileFilters);

private:
static void parseArgs(FileSettings &fs, const std::vector<std::string> &args);

bool importSln(std::istream &istr, const std::string &path, const std::vector<std::string> &fileFilters);
bool importSlnx(const std::string& filename, const std::vector<std::string>& fileFilters);
SharedItemsProject importVcxitems(const std::string &filename, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
bool importBcb6Prj(const std::string &projectFilename);

std::string mPath;
Expand Down
9 changes: 7 additions & 2 deletions test/cli/proj2_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
'x = 3 / 0;\n' +
' ^\n') % os.path.join('b', 'b.c')

def __get_lines(s):
# file order is not guaranteed when multiple jobs are used (TEST_CPPCHECK_INJECT_J) so
# compare output order-independently
return sorted(s.split('\n'))

def __create_compile_commands(proj_dir):
proj_dir = str(proj_dir)
j = [{'directory': os.path.join(proj_dir, 'a'), 'command': 'gcc -c a.c', 'file': 'a.c'},
Expand Down Expand Up @@ -152,7 +157,7 @@ def test_gui_project_loads_relative_vs_solution_2(tmp_path):
create_gui_project_file(os.path.join(tmp_path, 'test.cppcheck'), root_path='proj2', import_project='proj2/proj2.sln')
ret, stdout, stderr = cppcheck(['--project=test.cppcheck'], cwd=tmp_path)
assert ret == 0, stdout
assert stderr == __ERR_A + __ERR_B
assert __get_lines(stderr) == __get_lines(__ERR_A + __ERR_B)

def test_gui_project_loads_relative_vs_solution_with_exclude(tmp_path):
proj_dir = tmp_path / 'proj2'
Expand All @@ -170,4 +175,4 @@ def test_gui_project_loads_absolute_vs_solution_2(tmp_path):
import_project=os.path.join(proj_dir, 'proj2.sln'))
ret, stdout, stderr = cppcheck(['--project=test.cppcheck'], cwd=tmp_path)
assert ret == 0, stdout
assert stderr == __ERR_A + __ERR_B
assert __get_lines(stderr) == __get_lines(__ERR_A + __ERR_B)
32 changes: 32 additions & 0 deletions test/cli/props-dirs/ProjA/ProjA.vcxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1}</ProjectGuid>
<RootNamespace>ProjA</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<!-- Pulls in shared/shared.props, which itself pulls in common/common.props -->
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(MSBuildThisFileDirectory)..\shared\shared.props" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>PROJA_DEFINE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="a.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
11 changes: 11 additions & 0 deletions test/cli/props-dirs/ProjA/a.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "common.h"

#ifndef COMMON_H_INCLUDED_MARKER
#error "common.h was not found - AdditionalIncludeDirectories from common.props did not resolve"
#endif

int main()
{
int x = 1;
return x / 0;
}
29 changes: 29 additions & 0 deletions test/cli/props-dirs/ProjB/ProjB.vcxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{b2b2b2b2-b2b2-b2b2-b2b2-b2b2b2b2b2b2}</ProjectGuid>
<RootNamespace>ProjB</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<!-- Imports common/common.props DIRECTLY - does NOT go through shared/shared.props.
Must NOT see PROJA_DEFINE or SHARED_DEFINE from ProjA, which is processed first
in the same solution (regression coverage for the mVariables cross-project leak). -->
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(MSBuildThisFileDirectory)..\common\common.props" />
</ImportGroup>
<ItemGroup>
<ClCompile Include="b.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
11 changes: 11 additions & 0 deletions test/cli/props-dirs/ProjB/b.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "common.h"

#ifndef COMMON_H_INCLUDED_MARKER
#error "common.h was not found - AdditionalIncludeDirectories from common.props did not resolve"
#endif

int main()
{
int y = 2;
return y / 0;
}
3 changes: 3 additions & 0 deletions test/cli/props-dirs/common/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#ifndef COMMON_H_INCLUDED_MARKER
#define COMMON_H_INCLUDED_MARKER
#endif
13 changes: 13 additions & 0 deletions test/cli/props-dirs/common/common.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Imported from both ProjA (indirectly, via shared/shared.props) and ProjB (directly).
AdditionalIncludeDirectories uses $(MSBuildThisFileDirectory) to prove it resolves to
this file's own directory (common/) rather than to whichever project imported it. -->
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>COMMON_DEFINE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
</Project>
7 changes: 7 additions & 0 deletions test/cli/props-dirs/props-dirs.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Solution>
<Configurations>
<Platform Name="x64" />
</Configurations>
<Project Path="ProjA/ProjA.vcxproj" Id="a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1" />
<Project Path="ProjB/ProjB.vcxproj" Id="b2b2b2b2-b2b2-b2b2-b2b2-b2b2b2b2b2b2" />
</Solution>
14 changes: 14 additions & 0 deletions test/cli/props-dirs/shared/shared.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Only imported by ProjA. Chains into common/common.props via a relative,
$(MSBuildThisFileDirectory)-based path - this is the cross-directory nested
props-importing-props case (ProjA/ -> shared/ -> common/). -->
<ImportGroup Label="PropertySheets">
<Import Project="$(MSBuildThisFileDirectory)..\common\common.props" />
</ImportGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>SHARED_DEFINE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
</Project>
78 changes: 78 additions & 0 deletions test/cli/props_dirs_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

# python -m pytest props_dirs_test.py
#
# Regression coverage for MSBuild property-sheet (.props) loading across multiple
# directories:
# - $(MSBuildThisFileDirectory) must resolve to each .props file's own directory,
# not the importing project's directory, even through a chain of nested imports
# (ProjA/ -> shared/shared.props -> common/common.props).
# - AdditionalIncludeDirectories set via that chain must actually make a header in a
# different directory (common/common.h) resolvable from the project's source file.
# - A project that imports common/common.props directly (ProjB) must pick up exactly
# what that file sets and nothing that a *different* project in the same solution
# (ProjA) added on top - no cross-project variable leakage.

import os

from testutils import cppcheck

__script_dir = os.path.dirname(os.path.abspath(__file__))

__ERR_A = ('%s:10:14: error: Division by zero. [zerodiv]\n'
' return x / 0;\n'
' ^\n') % os.path.join('props-dirs', 'ProjA', 'a.cpp')
__ERR_B = ('%s:10:14: error: Division by zero. [zerodiv]\n'
' return y / 0;\n'
' ^\n') % os.path.join('props-dirs', 'ProjB', 'b.cpp')


def __get_lines(s):
# file order is not guaranteed when multiple jobs are used (TEST_CPPCHECK_INJECT_J) so
# compare output order-independently
return sorted(s.split('\n'))


def test_props_dirs_solution():
args = [
'--project=props-dirs/props-dirs.slnx',
'--no-cppcheck-build-dir'
]
ret, stdout, stderr = cppcheck(args, cwd=__script_dir)
assert ret == 0, stdout

# both files were actually analyzed (division by zero fires) which also proves
# "common.h" was found via AdditionalIncludeDirectories - if it hadn't resolved, the
# #error guard in each .cpp would have fired instead and there would be no zerodiv
assert __get_lines(stderr) == __get_lines(__ERR_A + __ERR_B)


def test_props_dirs_defines_and_standard():
args = [
'--project=props-dirs/props-dirs.slnx',
'--no-cppcheck-build-dir',
'--dump'
]
ret, stdout, _ = cppcheck(args, cwd=__script_dir)
assert ret == 0, stdout

dump_a = os.path.join(__script_dir, 'props-dirs', 'ProjA', 'a.cpp.dump')
dump_b = os.path.join(__script_dir, 'props-dirs', 'ProjB', 'b.cpp.dump')
assert os.path.exists(dump_a), f"Dump file not found at {dump_a}"
assert os.path.exists(dump_b), f"Dump file not found at {dump_b}"

with open(dump_a, 'rt') as f:
dump_a_content = f.read()
with open(dump_b, 'rt') as f:
dump_b_content = f.read()

# ProjA imports shared/shared.props (which itself imports common/common.props), and
# also sets its own PROJA_DEFINE - all three must be present, most specific first
assert 'cfg="_WIN32=1;_WIN64=1;PROJA_DEFINE=1;SHARED_DEFINE=1;COMMON_DEFINE=1;_MSC_VER=1900"' in dump_a_content
assert '<cpp version="c++17"/>' in dump_a_content

# ProjB imports common/common.props directly - it must see COMMON_DEFINE, but neither
# PROJA_DEFINE nor SHARED_DEFINE, which only ever applied to ProjA
assert 'cfg="_WIN32=1;_WIN64=1;COMMON_DEFINE=1;_MSC_VER=1900"' in dump_b_content
assert '<cpp version="c++17"/>' in dump_b_content
assert 'PROJA_DEFINE' not in dump_b_content
assert 'SHARED_DEFINE' not in dump_b_content
7 changes: 7 additions & 0 deletions test/cli/vcxproj-unicode/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <iostream>

int main() {
std::cout << "Hello world!" << std::endl;
return 0;
}

33 changes: 33 additions & 0 deletions test/cli/vcxproj-unicode/vcxproj_unicode.vcxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Configuration">
<!-- Only to test that the last configuration entry overwrites this -->
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
<UseOfMfc>Static</UseOfMfc>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions test/cli/vcxproj_forced_includes/AllX64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class all
{
all() {
int x = 3 / 0; (void)x; // ERROR
}
};
8 changes: 8 additions & 0 deletions test/cli/vcxproj_forced_includes/DebugX64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <iostream>

int foo()
{
std::cout << "DebugX64\n";
int x = 3 / 0; (void)x; // ERROR
return 0;
}
6 changes: 6 additions & 0 deletions test/cli/vcxproj_forced_includes/DebugX64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class debug
{
debug() {
int x = 3 / 0; (void)x; // ERROR
}
};
6 changes: 6 additions & 0 deletions test/cli/vcxproj_forced_includes/GlobalDebugX64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class global
{
global() {
int x = 3 / 0; (void)x; // ERROR
}
};
6 changes: 6 additions & 0 deletions test/cli/vcxproj_forced_includes/GlobalReleaseX64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class global
{
global() {
int x = 3 / 0; (void)x; // ERROR
}
};
6 changes: 6 additions & 0 deletions test/cli/vcxproj_forced_includes/PropsDebugX64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class props
{
props() {
int x = 3 / 0; (void)x; // ERROR
}
};
Loading
Loading