Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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