diff --git a/cpplint.py b/cpplint.py index e400b05..24897da 100755 --- a/cpplint.py +++ b/cpplint.py @@ -308,8 +308,14 @@ "build/include_order", "build/include_what_you_use", "build/namespaces_headers", - "build/namespaces_literals", - "build/namespaces", + "build/namespaces/header/block/literals", + "build/namespaces/header/block/nonliterals", + "build/namespaces/header/namespace/literals", + "build/namespaces/header/namespace/nonliterals", + "build/namespaces/source/block/literals", + "build/namespaces/source/block/nonliterals", + "build/namespaces/source/namespace/literals", + "build/namespaces/source/namespace/nonliterals", "build/printf_format", "build/storage_class", "legal/copyright", @@ -935,6 +941,16 @@ "Missing space after ,": r"s/,\([^ ]\)/, \1/g", } +# Used for backwards compatibility and ease of use +_FILTER_SHORTCUTS = { + "build/namespaces_literals": [ + "build/namespaces/header/block/literals", + "build/namespaces/header/namespace/literals", + "build/namespaces/source/block/literals", + "build/namespaces/source/namespace/literals", + ] +} + # The root directory used for deriving header guard CPP variable. # This is set by --root flag. _root = None @@ -1457,7 +1473,12 @@ def AddFilters(self, filters): for filt in filters.split(","): clean_filt = filt.strip() if clean_filt: - self.filters.append(clean_filt) + if len(clean_filt) > 1 and clean_filt[1:] in _FILTER_SHORTCUTS: + starting_char = clean_filt[0] + new_filters = [starting_char + x for x in _FILTER_SHORTCUTS[clean_filt[1:]]] + self.filters.extend(new_filters) + else: + self.filters.append(clean_filt) for filt in self.filters: if not filt.startswith(("+", "-")): msg = f"Every filter in --filters must start with + or - ({filt} does not)" @@ -3287,6 +3308,14 @@ def InAsmBlock(self): """ return self.stack and self.stack[-1].inline_asm != _NO_ASM + def InBlockScope(self): + """Check if we are currently one level inside a block scope. + + Returns: + True if top of the stack is a block scope, False otherwise. + """ + return len(self.stack) > 0 and not isinstance(self.stack[-1], _NamespaceInfo) + def InTemplateArgumentList(self, clean_lines, linenum, pos): """Check if current position is inside template argument list. @@ -6021,22 +6050,26 @@ def CheckLanguage( ) if re.search(r"\busing namespace\b", line): - if re.search(r"\bliterals\b", line): - error( - filename, - linenum, - "build/namespaces_literals", - 5, - "Do not use namespace using-directives. Use using-declarations instead.", - ) - else: - error( - filename, - linenum, - "build/namespaces", - 5, - "Do not use namespace using-directives. Use using-declarations instead.", - ) + is_literals = re.search(r"\bliterals\b", line) is not None + is_header = not _IsSourceExtension(file_extension) + file_type = "header" if is_header else "source" + + # Check for the block scope for multiline blocks. + # Check if the line starts with the using directive as a heuristic in case it's all one line + is_block_scope = nesting_state.InBlockScope() or not line.startswith("using namespace") + + scope_type = "block" if is_block_scope else "namespace" + literal_type = "literals" if is_literals else "nonliterals" + + specific_category = f"build/namespaces/{file_type}/{scope_type}/{literal_type}" + + error( + filename, + linenum, + specific_category, + 5, + "Do not use namespace using-directives. Use using-declarations instead.", + ) # Detect variable-length arrays. match = re.match(r"\s*(.+::)?(\w+) [a-z]\w*\[(.+)];", line) diff --git a/cpplint_unittest.py b/cpplint_unittest.py index e36093f..574e5fd 100755 --- a/cpplint_unittest.py +++ b/cpplint_unittest.py @@ -3640,7 +3640,7 @@ def DoTest(self, lines): assert ( error_collector.Results().count( "Do not use namespace using-directives. Use using-declarations instead. " - "[build/namespaces] [5]" + "[build/namespaces/source/namespace/nonliterals] [5]" ) == 1 ) @@ -3649,20 +3649,6 @@ def DoTest(self, lines): DoTest(self, ["", "", "", "using namespace foo;"]) DoTest(self, ["// hello", "using namespace foo;"]) - def testUsingLiteralsNamespaces(self): - self.TestLint( - "using namespace std::literals;", - "Do not use namespace" - " using-directives. Use using-declarations instead." - " [build/namespaces_literals] [5]", - ) - self.TestLint( - "using namespace std::literals::chrono_literals;", - "Do" - " not use namespace using-directives. Use using-declarations instead." - " [build/namespaces_literals] [5]", - ) - def testNewlineAtEOF(self): def DoTest(self, data, is_missing_eof): error_collector = ErrorCollector(self.assertTrue) @@ -4203,6 +4189,73 @@ def testEndOfNamespaceComments(self): == 0 ) + def testUsingNamespacesGranular(self): + """Test granular using namespace checks for different contexts.""" + + self.TestLanguageRulesCheck( + "foo.h", + "using namespace std;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/namespace/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.h", + "using namespace std::chrono::literals;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/namespace/literals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "using namespace std;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/namespace/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "using namespace std::chrono::literals;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/namespace/literals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.h", + "{ using namespace std; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/block/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.h", + "{ using namespace std::chrono::literals; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/block/literals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "{ using namespace std; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/block/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "{ using namespace std::chrono::literals; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/block/literals] [5]", + ) + def testComma(self): self.TestLint("a = f(1,2);", "Missing space after , [whitespace/comma] [3]") self.TestLint( diff --git a/samples/silly-sample/filters.def b/samples/silly-sample/filters.def index e0fac32..5780b3e 100644 --- a/samples/silly-sample/filters.def +++ b/samples/silly-sample/filters.def @@ -13,7 +13,7 @@ src/sillycode.cpp:1: Include the directory when naming header files [build/inc src/sillycode.cpp:2: is an unapproved C++11 header. [build/c++11] [5] src/sillycode.cpp:3: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] src/sillycode.cpp:4: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] -src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5] +src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces/source/namespace/nonliterals] [5] src/sillycode.cpp:40: If/else bodies with multiple statements require braces [readability/braces] [4] src/sillycode.cpp:66: Single-parameter constructors should be marked explicit. [runtime/explicit] [4] src/sillycode.cpp:76: Single-parameter constructors should be marked explicit. [runtime/explicit] [4] diff --git a/samples/silly-sample/includeorder_cfirst.def b/samples/silly-sample/includeorder_cfirst.def index a3b30a4..9be5e44 100644 --- a/samples/silly-sample/includeorder_cfirst.def +++ b/samples/silly-sample/includeorder_cfirst.def @@ -11,7 +11,7 @@ src/sillycode.cpp:2: Should have a space between // and comment [whitespace/co src/sillycode.cpp:2: is an unapproved C++11 header. [build/c++11] [5] src/sillycode.cpp:3: Found other system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] src/sillycode.cpp:4: Found other system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] -src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5] +src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces/source/namespace/nonliterals] [5] src/sillycode.cpp:8: public: should be indented +1 space inside class Date [whitespace/indent] [3] src/sillycode.cpp:15: { should almost always be at the end of the previous line [whitespace/braces] [4] src/sillycode.cpp:39: { should almost always be at the end of the previous line [whitespace/braces] [4] diff --git a/samples/silly-sample/sed.def b/samples/silly-sample/sed.def index b3778ee..850a16d 100644 --- a/samples/silly-sample/sed.def +++ b/samples/silly-sample/sed.def @@ -15,7 +15,7 @@ sed -i '249s/\([^ ]\){/\1 {/' src/sillycode.cpp # Missing space before { [white # src/sillycode.cpp:2: " is an unapproved C++11 header." [build/c++11] [5] # src/sillycode.cpp:3: "Found C system header after other header. Should be: sillycode.h, c system, c++ system, other." [build/include_order] [4] # src/sillycode.cpp:4: "Found C system header after other header. Should be: sillycode.h, c system, c++ system, other." [build/include_order] [4] -# src/sillycode.cpp:5: "Do not use namespace using-directives. Use using-declarations instead." [build/namespaces] [5] +# src/sillycode.cpp:5: "Do not use namespace using-directives. Use using-declarations instead." [build/namespaces/source/namespace/nonliterals] [5] # src/sillycode.cpp:8: "public: should be indented +1 space inside class Date" [whitespace/indent] [3] # src/sillycode.cpp:15: "{ should almost always be at the end of the previous line" [whitespace/braces] [4] # src/sillycode.cpp:39: "{ should almost always be at the end of the previous line" [whitespace/braces] [4] diff --git a/samples/silly-sample/simple.def b/samples/silly-sample/simple.def index 7bc1dc6..b7ce111 100644 --- a/samples/silly-sample/simple.def +++ b/samples/silly-sample/simple.def @@ -12,7 +12,7 @@ src/sillycode.cpp:2: Should have a space between // and comment [whitespace/co src/sillycode.cpp:2: is an unapproved C++11 header. [build/c++11] [5] src/sillycode.cpp:3: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] src/sillycode.cpp:4: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] -src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5] +src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces/source/namespace/nonliterals] [5] src/sillycode.cpp:8: public: should be indented +1 space inside class Date [whitespace/indent] [3] src/sillycode.cpp:15: { should almost always be at the end of the previous line [whitespace/braces] [4] src/sillycode.cpp:39: { should almost always be at the end of the previous line [whitespace/braces] [4]