Prototype fix

This commit is contained in:
Ng Zhi An 2018-10-13 22:42:04 -07:00 committed by Vidar Holen
parent cb57b4a74f
commit 41613babd9
5 changed files with 101 additions and 12 deletions

View File

@ -1336,7 +1336,10 @@ prop_checkBackticks1 = verify checkBackticks "echo `foo`"
prop_checkBackticks2 = verifyNot checkBackticks "echo $(foo)" prop_checkBackticks2 = verifyNot checkBackticks "echo $(foo)"
prop_checkBackticks3 = verifyNot checkBackticks "echo `#inlined comment` foo" prop_checkBackticks3 = verifyNot checkBackticks "echo `#inlined comment` foo"
checkBackticks _ (T_Backticked id list) | not (null list) = checkBackticks _ (T_Backticked id list) | not (null list) =
style id 2006 "Use $(...) notation instead of legacy backticked `...`." addComment $
makeCommentWithFix StyleC id 2006 "Use $(...) notation instead of legacy backticked `...`."
((replaceStart 1 "$(") ++ (replaceEnd 1 ")"))
-- style id 2006 "Use $(...) notation instead of legacy backticked `...`."
checkBackticks _ _ = return () checkBackticks _ _ = return ()
prop_checkIndirectExpansion1 = verify checkIndirectExpansion "${foo$n}" prop_checkIndirectExpansion1 = verify checkIndirectExpansion "${foo$n}"
@ -1640,8 +1643,10 @@ checkSpacefulness params t =
makeComment InfoC (getId token) 2223 makeComment InfoC (getId token) 2223
"This default assignment may cause DoS due to globbing. Quote it." "This default assignment may cause DoS due to globbing. Quote it."
else else
makeComment InfoC (getId token) 2086 makeCommentWithFix InfoC (getId token) 2086
"Double quote to prevent globbing and word splitting." "Double quote to prevent globbing and word splitting." (surroundWith "\"")
-- makeComment InfoC (getId token) 2086
-- "Double quote to prevent globbing and word splitting."
writeF _ _ name (DataString SourceExternal) = setSpaces name True >> return [] writeF _ _ name (DataString SourceExternal) = setSpaces name True >> return []
writeF _ _ name (DataString SourceInteger) = setSpaces name False >> return [] writeF _ _ name (DataString SourceInteger) = setSpaces name False >> return []
@ -2538,7 +2543,9 @@ checkUncheckedCdPushdPopd params root =
&& not (isSafeDir t) && not (isSafeDir t)
&& not (name t `elem` ["pushd", "popd"] && ("n" `elem` map snd (getAllFlags t))) && not (name t `elem` ["pushd", "popd"] && ("n" `elem` map snd (getAllFlags t)))
&& not (isCondition $ getPath (parentMap params) t)) $ && not (isCondition $ getPath (parentMap params) t)) $
warn (getId t) 2164 "Use 'cd ... || exit' or 'cd ... || return' in case cd fails." -- warn (getId t) 2164 "Use 'cd ... || exit' or 'cd ... || return' in case cd fails."
warnWithFix (getId t) 2164 "Use 'cd ... || exit' or 'cd ... || return' in case cd fails."
(replaceEnd 0 " || exit")
checkElement _ = return () checkElement _ = return ()
name t = fromMaybe "" $ getCommandName t name t = fromMaybe "" $ getCommandName t
isSafeDir t = case oversimplify t of isSafeDir t = case oversimplify t of
@ -2695,7 +2702,7 @@ checkArrayAssignmentIndices params root =
T_Literal id str -> [(id,str)] T_Literal id str -> [(id,str)]
_ -> [] _ -> []
guard $ '=' `elem` str guard $ '=' `elem` str
return $ warn id 2191 "The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it." return $ warnWithFix id 2191 "The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it." (surroundWith "\"")
in in
if null literalEquals && isAssociative if null literalEquals && isAssociative
then warn (getId t) 2190 "Elements in associative arrays need index, e.g. array=( [index]=value ) ." then warn (getId t) 2190 "Elements in associative arrays need index, e.g. array=( [index]=value ) ."

View File

@ -150,6 +150,19 @@ err id code str = addComment $ makeComment ErrorC id code str
info id code str = addComment $ makeComment InfoC id code str info id code str = addComment $ makeComment InfoC id code str
style id code str = addComment $ makeComment StyleC id code str style id code str = addComment $ makeComment StyleC id code str
warnWithFix id code str fix = addComment $
let comment = makeComment WarningC id code str in
comment {
tcFix = Just fix
}
makeCommentWithFix :: Severity -> Id -> Code -> String -> Fix -> TokenComment
makeCommentWithFix severity id code str fix =
let comment = makeComment severity id code str in
comment {
tcFix = Just fix
}
makeParameters spec = makeParameters spec =
let params = Parameters { let params = Parameters {
rootNode = root, rootNode = root,

View File

@ -42,7 +42,8 @@ tokenToPosition startMap t = fromMaybe fail $ do
return $ newPositionedComment { return $ newPositionedComment {
pcStartPos = fst span, pcStartPos = fst span,
pcEndPos = snd span, pcEndPos = snd span,
pcComment = tcComment t pcComment = tcComment t,
pcFix = tcFix t
} }
where where
fail = error "Internal shellcheck error: id doesn't exist. Please report!" fail = error "Internal shellcheck error: id doesn't exist. Please report!"

View File

@ -129,8 +129,47 @@ outputForFile color sys comments = do
putStrLn (color "source" line) putStrLn (color "source" line)
mapM_ (\c -> putStrLn (color (severityText c) $ cuteIndent c)) x mapM_ (\c -> putStrLn (color (severityText c) $ cuteIndent c)) x
putStrLn "" putStrLn ""
mapM_ (\c -> putStrLn "Did you mean:" >> putStrLn (fixedString c line)) x
) groups ) groups
-- need to do something smart about sorting by end index
fixedString :: PositionedComment -> String -> String
fixedString comment line =
case (pcFix comment) of
Nothing -> ""
Just rs ->
apply_replacement rs line 0
where
apply_replacement [] s _ = s
apply_replacement ((Start n r):xs) s offset =
let start = (posColumn . pcStartPos) comment
end = start + n
z = do_replace start end s r
len_r = (fromIntegral . length) r in
apply_replacement xs z (offset + (end - start) + len_r)
apply_replacement ((End n r):xs) s offset =
-- tricky math because column is 1 based
let end = (posColumn . pcEndPos) comment + 1
start = end - n
z = do_replace start end s r
len_r = (fromIntegral . length) r in
apply_replacement xs z (offset + (end - start) + len_r)
-- start and end comes from pos, which is 1 based
-- do_replace 0 0 "1234" "A" -> "A1234" -- technically not valid
-- do_replace 1 1 "1234" "A" -> "A1234"
-- do_replace 1 2 "1234" "A" -> "A234"
-- do_replace 3 3 "1234" "A" -> "12A34"
-- do_replace 4 4 "1234" "A" -> "123A4"
-- do_replace 5 5 "1234" "A" -> "1234A"
do_replace start end o r =
let si = fromIntegral (start-1)
ei = fromIntegral (end-1)
(x, xs) = splitAt si o
(y, z) = splitAt (ei - si) xs
in
x ++ r ++ z
cuteIndent :: PositionedComment -> String cuteIndent :: PositionedComment -> String
cuteIndent comment = cuteIndent comment =
replicate (fromIntegral $ colNo comment - 1) ' ' ++ replicate (fromIntegral $ colNo comment - 1) ' ' ++

View File

@ -34,9 +34,9 @@ module ShellCheck.Interface
, Severity(ErrorC, WarningC, InfoC, StyleC) , Severity(ErrorC, WarningC, InfoC, StyleC)
, Position(posFile, posLine, posColumn) , Position(posFile, posLine, posColumn)
, Comment(cSeverity, cCode, cMessage) , Comment(cSeverity, cCode, cMessage)
, PositionedComment(pcStartPos , pcEndPos , pcComment) , PositionedComment(pcStartPos , pcEndPos , pcComment, pcFix)
, ColorOption(ColorAuto, ColorAlways, ColorNever) , ColorOption(ColorAuto, ColorAlways, ColorNever)
, TokenComment(tcId, tcComment) , TokenComment(tcId, tcComment, tcFix)
, emptyCheckResult , emptyCheckResult
, newParseResult , newParseResult
, newAnalysisSpec , newAnalysisSpec
@ -49,10 +49,16 @@ module ShellCheck.Interface
, emptyCheckSpec , emptyCheckSpec
, newPositionedComment , newPositionedComment
, newComment , newComment
, Fix
, Replacement(Start, End)
, surroundWith
, replaceStart
, replaceEnd
) where ) where
import ShellCheck.AST import ShellCheck.AST
import Control.Monad.Identity import Control.Monad.Identity
import Data.Monoid
import qualified Data.Map as Map import qualified Data.Map as Map
@ -190,27 +196,50 @@ newComment = Comment {
cMessage = "" cMessage = ""
} }
-- only support single line for now
data Replacement =
Start Integer String
| End Integer String
deriving (Show, Eq)
type Fix = [Replacement]
surroundWith s =
(replaceStart 0 s) ++ (replaceEnd 0 s)
-- replace first n chars
replaceStart n r =
[ Start n r ]
-- replace last n chars
replaceEnd n r =
[ End n r ]
data PositionedComment = PositionedComment { data PositionedComment = PositionedComment {
pcStartPos :: Position, pcStartPos :: Position,
pcEndPos :: Position, pcEndPos :: Position,
pcComment :: Comment pcComment :: Comment,
pcFix :: Maybe Fix
} deriving (Show, Eq) } deriving (Show, Eq)
newPositionedComment :: PositionedComment newPositionedComment :: PositionedComment
newPositionedComment = PositionedComment { newPositionedComment = PositionedComment {
pcStartPos = newPosition, pcStartPos = newPosition,
pcEndPos = newPosition, pcEndPos = newPosition,
pcComment = newComment pcComment = newComment,
pcFix = Nothing
} }
data TokenComment = TokenComment { data TokenComment = TokenComment {
tcId :: Id, tcId :: Id,
tcComment :: Comment tcComment :: Comment,
tcFix :: Maybe Fix
} deriving (Show, Eq) } deriving (Show, Eq)
newTokenComment = TokenComment { newTokenComment = TokenComment {
tcId = Id 0, tcId = Id 0,
tcComment = newComment tcComment = newComment,
tcFix = Nothing
} }
data ColorOption = data ColorOption =