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
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog
TBA
===

* Allow source files to include headers from the same directory without a directory prefix. (#375)
* Fixed a whitespace/newline false positive for control conditions containing lambdas. (#410)
* We now error on relative include paths (``./``, ``../``). (#432)
* This makes ``#include "./foo.h"`` produce two separate errors: that foo.cpp should include foo.h and that relative paths are not allowed.
Expand Down
32 changes: 25 additions & 7 deletions cpplint.py
Original file line number Diff line number Diff line change
Expand Up @@ -5856,13 +5856,31 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
and IsHeaderExtension(match.group(2))
and not _third_party_headers_pattern.match(match.group(1))
):
error(
filename,
linenum,
"build/include_subdir",
4,
"Include the directory when naming header files",
)
source_path = os.path.realpath(filename)
same_dir_header = os.path.join(os.path.dirname(source_path), match.group(1))
if not os.path.isfile(same_dir_header):
error(
filename,
linenum,
"build/include_subdir",
4,
"Include the directory when naming header files",
)
else:
try:
is_same_file = os.path.samefile(same_dir_header, filename)
except (OSError, ValueError):
is_same_file = os.path.normcase(
os.path.normpath(same_dir_header)
) == os.path.normcase(os.path.normpath(source_path))
if is_same_file:
error(
filename,
linenum,
"build/include_subdir",
4,
"Include the directory when naming header files",
)

# we shouldn't include a file more than once. actually, there are a
# handful of instances where doing so is okay, but in general it's
Expand Down
101 changes: 98 additions & 3 deletions cpplint_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5824,13 +5824,39 @@ def testBuildHeaderGuardWithRepository(self):
cpplint._repository = None
cpplint._root = None

def testBuildInclude(self):
def testBuildInclude(self, tmp_path):
# Test that include statements have slashes in them.
self.TestLint(
repo = tmp_path / "repo"
repo.mkdir()
(repo / ".git").mkdir()
root_source = repo / "foo.cc"
root_source.write_text("")
root_header = repo / "foo.h"
root_header.write_text("")
source_dir = repo / "src"
source_dir.mkdir()
nested_source = source_dir / "foo.cc"
nested_source.write_text("")
same_dir_header = source_dir / "utils.hpp"
same_dir_header.write_text("")

self.TestLanguageRulesCheck(
str(root_source),
'#include "foo.h"',
"",
)
self.TestLanguageRulesCheck(
str(nested_source),
'#include "utils.hpp"',
"",
)
self.TestLanguageRulesCheck(
str(nested_source),
'#include "foo.h"',
"Include the directory when naming header files [build/include_subdir] [4]",
)
self.TestLint(
self.TestLanguageRulesCheck(
str(nested_source),
'#include "bar.hh"',
"Include the directory when naming header files [build/include_subdir] [4]",
)
Expand All @@ -5852,6 +5878,75 @@ def testBuildInclude(self):
self.TestLint('#include "Python.h"', "")
self.TestLint('#include "lua.h"', "")

def testBuildIncludeSymlinkPath(self, tmp_path):
physical_dir = tmp_path / "physical"
(physical_dir / "child").mkdir(parents=True)
lexical_dir = tmp_path / "lexical"
lexical_dir.mkdir()
symlink = lexical_dir / "link"
try:
symlink.symlink_to(physical_dir / "child", target_is_directory=True)
except OSError as error:
pytest.skip(f"directory symlinks are unavailable: {error}")

source = physical_dir / "foo.cc"
source.write_text("")
source_path = symlink / ".." / source.name
try:
if not source_path.exists() or not os.path.samefile(source_path, source):
pytest.skip("directory symlink paths do not resolve '..' through the target")
except OSError as error:
pytest.skip(f"directory symlink parent traversal is unavailable: {error}")

same_dir_header = physical_dir / "utils.hpp"
same_dir_header.write_text("")
self.TestLanguageRulesCheck(str(source_path), '#include "utils.hpp"', "")

same_dir_header.unlink()
(lexical_dir / "utils.hpp").write_text("")
self.TestLanguageRulesCheck(
str(source_path),
'#include "utils.hpp"',
"Include the directory when naming header files [build/include_subdir] [4]",
)

def testBuildIncludeSymlinkAlias(self, tmp_path):
source = tmp_path / "self.h"
source.write_text("")
expected = "Include the directory when naming header files [build/include_subdir] [4]"

symlink = tmp_path / "symlink.h"
try:
symlink.symlink_to(source)
except OSError as error:
pytest.skip(f"file symlinks are unavailable: {error}")
self.TestLanguageRulesCheck(str(source), '#include "symlink.h"', expected)

def testBuildIncludeHardlinkAlias(self, tmp_path):
source = tmp_path / "self.h"
source.write_text("")
expected = "Include the directory when naming header files [build/include_subdir] [4]"
hardlink = tmp_path / "hardlink.h"
try:
os.link(source, hardlink)
except OSError as error:
pytest.skip(f"hard links are unavailable: {error}")
self.TestLanguageRulesCheck(str(source), '#include "hardlink.h"', expected)

def testBuildIncludeInvalidHeaderPath(self, tmp_path):
source = tmp_path / "source.cc"
source.write_text("")
self.TestLanguageRulesCheck(
str(source),
'#include "bad\x00.h"',
"Include the directory when naming header files [build/include_subdir] [4]",
)

def testBuildIncludeInvalidNonHeaderPath(self, tmp_path):
source = tmp_path / "source.cc"
source.write_text("")
self.TestLanguageRulesCheck(str(source), '#include "bad\x00.txt"', "")

def testHppInclude(self):
code = "\n".join(["#include <vector>", "#include <boost/any.hpp>"])
self.TestLanguageRulesCheck("foo.h", code, "")
Expand Down
3 changes: 1 addition & 2 deletions samples/codelite-sample/simple.def
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ src/*
4
Done processing src/pptable.cpp
Done processing src/pptable.h
Total errors found: 675
Total errors found: 674

src/pptable.cpp:0: No copyright message found. You should have a line: "Copyright [year] <Copyright Owner>" [legal/copyright] [5]
src/pptable.cpp:1: Include the directory when naming header files [build/include_subdir] [4]
src/pptable.cpp:6: { should almost always be at the end of the previous line [whitespace/braces] [4]
src/pptable.cpp:7: Tab found; better to use spaces [whitespace/tab] [1]
src/pptable.cpp:8: Tab found; better to use spaces [whitespace/tab] [1]
Expand Down
3 changes: 1 addition & 2 deletions samples/vlc-sample/simple.def
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ src/*
Done processing src/libvlc.c
Done processing src/libvlc.h
Done processing src/missing.c
Total errors found: 600
Total errors found: 599

src/libvlc.c:40: Relative paths like . and .. are not allowed. [build/include] [4]
src/libvlc.c:41: Found C system header after other header. Should be: libvlc.h, c system, c++ system, other. [build/include_order] [4]
src/libvlc.c:47: Found C system header after other header. Should be: libvlc.h, c system, c++ system, other. [build/include_order] [4]
src/libvlc.c:48: Found C system header after other header. Should be: libvlc.h, c system, c++ system, other. [build/include_order] [4]
src/libvlc.c:49: Found C system header after other header. Should be: libvlc.h, c system, c++ system, other. [build/include_order] [4]
src/libvlc.c:50: Found C system header after other header. Should be: libvlc.h, c system, c++ system, other. [build/include_order] [4]
src/libvlc.c:71: Include the directory when naming header files [build/include_subdir] [4]
src/libvlc.c:75: Found C system header after other header. Should be: libvlc.h, c system, c++ system, other. [build/include_order] [4]
src/libvlc.c:86: Extra space before [ [whitespace/braces] [5]
src/libvlc.c:86: Extra space after ( in function call [whitespace/parens] [4]
Expand Down
Loading